这是一个基于消息的分布式事务的一部分,主要通过消息来实现,生产者把消息发到队列后,由消费方去执行剩下的逻辑,而当消费方处理失败后,我们需要进行重试,即为了最现数据的最终一致性,在rabbitmq里,它有消息重试和重试次数的配置,但当你配置之后,你的TTL达到 后,消息不能自动放入死信队列,所以这块需要手工处理一下.

rabbitmq关于消息重试的配置

  rabbitmq:
host: xxx
port: xxx
username: xxx
password: xxx
virtual-host: xxx
###开启消息确认机制 confirms
publisher-confirms: true
publisher-returns: true
listener:
simple:
acknowledge-mode: manual #设置确认方式
prefetch: 1 #每次处理1条消息
retry.max-attempts: 3 # 最大重试次数
retry.enabled: true #是否开启消费者重试(为false时关闭消费者重试,这时消费端代码异常会一直重复收到消息)
retry.initial-interval: 2000 #重试间隔时间(单位毫秒)
default-requeue-rejected: true #该配置项是决定由于监听器抛出异常而拒绝的消息是否被重新放回队列。默认值为true,需要手动basicNack时这些参数谅失效了

手工实现消息重试并放入死信的方式

定义队列的相关配置

/**
* 创建普通交换机.
*/
@Bean
public TopicExchange lindExchange() {
//消息持久化
return (TopicExchange) ExchangeBuilder.topicExchange(EXCHANGE).durable(true).build();
} @Bean
public TopicExchange deadExchange() {
return (TopicExchange) ExchangeBuilder.topicExchange(LIND_DL_EXCHANGE).durable(true).build();
} /**
* 基于消息事务的处理方式,当消费失败进行重试,有时间间隔,当达到超时时间,就发到死信队列,等待人工处理.
* @return
*/
@Bean
public Queue testQueue() {
//设置死信交换机
return QueueBuilder.durable(QUEUE).withArgument("x-dead-letter-exchange", LIND_DL_EXCHANGE)
//毫秒
.withArgument("x-message-ttl", CONSUMER_EXPIRE)
//设置死信routingKey
.withArgument("x-dead-letter-routing-key", LIND_DEAD_QUEUE).build();
}
@Bean
public Queue deadQueue() {
return new Queue(LIND_DEAD_QUEUE);
}
@Bean
public Binding bindBuildersRouteKey() {
return BindingBuilder.bind(testQueue()).to(lindExchange()).with(ROUTER);
} @Bean
public Binding bindDeadBuildersRouteKey() {
return BindingBuilder.bind(deadQueue()).to(deadExchange()).with(LIND_DEAD_QUEUE);
}

消费者实现的代码

/**
* 延时队列:不应该有RabbitListener订阅者,应该让它自己达到超时时间后自动转到死信里去消费
* 消息异常处理:消费出现异常后,延时几秒,然后从新入队列消费,直到达到TTL超时时间,再转到死信,证明这个信息有问题需要人工干预
*
* @param message
*/
@RabbitListener(queues = MqConfig.QUEUE)
public void testSubscribe(Message message, Channel channel) throws IOException, InterruptedException {
try {
System.out.println(LocalDateTime.now() + ":Subscriber:" + new String(message.getBody(), "UTF-8"));
//当程序处理出现问题时,消息使用basicReject上报
int a = 0;
int b = 1 / a;
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
} catch (Exception ex) {
//出现异常手动放回队列
Thread.sleep(2000);
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
} }
/**
* 死信队列.
*
* @param message
*/
@RabbitListener(queues = MqConfig.LIND_DEAD_QUEUE)
public void dealSubscribe(Message message, Channel channel) throws IOException {
System.out.println("Dead Subscriber:" + new String(message.getBody(), "UTF-8"));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
}

消费者这块,也可以直接声明队列和绑定交换机,直接在注解上添加 QueueBinding即可.

  @RabbitListener(bindings = {@QueueBinding(value = @Queue(
name = MqConfig.QUEUE,
durable = "true",arguments = {@Argument(name = "x-dead-letter-exchange", value = MqConfig.LIND_DL_EXCHANGE),
@Argument(name = "x-message-ttl", value = MqConfig.CONSUMER_EXPIRE,type="java.lang.Long"),
@Argument(name = "x-dead-letter-routing-key", value = MqConfig.LIND_DEAD_QUEUE)}),
exchange = @Exchange(value = MqConfig.EXCHANGE, durable = "true",type="topic") )})
public void testSubscribe(Message message, Channel channel) throws IOException, InterruptedException { }

这边尝试让消费者执行出错,然后走到catch里使用basicNack方法把消息从新放里队列里,并让线程让休息2秒,以避免频繁操作,之后就是我们希望看到的代码

2019-12-20T17:21:31.190:Subscriber:send a message to mq
2019-12-20T17:21:33.200:Subscriber:send a message to mq
2019-12-20T17:21:35.206:Subscriber:send a message to mq
2019-12-20T17:21:37.213:Subscriber:send a message to mq
2019-12-20T17:21:39.221:Subscriber:send a message to mq
Dead Subscriber:send a message to mq

这就是一个消息队列的补偿机制,使用死信队列也可以实现延时消息的机制,有时间再给大家分享!

rabbitmq~消息失败后重试达到 TTL放到死信队列(事务型消息补偿机制)的更多相关文章

  1. RabbitMQ 消费端限流、TTL、死信队列

    目录 消费端限流 1. 为什么要对消费端限流 2.限流的 api 讲解 3.如何对消费端进行限流 TTL 1.消息的 TTL 2.队列的 TTL 死信队列 实现死信队列步骤 总结 消费端限流 1. 为 ...

  2. RabbitMQ TTL、死信队列

    TTL概念 TTL是Time To Live的缩写,也就是生存时间. RabbitMQ支持消息的过期时间,在消息发送时可以进行指定. RabbitMQ支持队列的过期时间,从消息入队列开始计算,只要超过 ...

  3. RocketMQ之八:重试队列,死信队列,消息轨迹

    问题思考 死信队列的应用场景? 死信队列中的数据是如何产生的? 如何查看死信队列中的数据? 死信队列的读写权限? 死信队列如何消费? 重试队列和死信队列的配置 消息轨迹 1.应用场景 一般应用在当正常 ...

  4. RabbitMQ消费端ACK与重回队列机制,TTL,死信队列详解(十一)

    消费端的手工ACK和NACK 消费端进行消费的时候,如果由于业务异常我们可以进行日志的记录,然后进行补偿. 如果由于服务器宕机等严重问题,那么我们就需要手工进行ACK保障消费端成功. 消费端重回队列 ...

  5. RabbitMQ使用 prefetch_count优化队列的消费,使用死信队列和延迟队列实现消息的定时重试,golang版本

    RabbitMQ 的优化 channel prefetch Count 死信队列 什么是死信队列 使用场景 代码实现 延迟队列 什么是延迟队列 使用场景 实现延迟队列的方式 Queue TTL Mes ...

  6. Oracle 数据库 JOB 失败后解密法重试

    因为官方文档上没有找到相关的说明,所以这里进行了例如以下測试,为了找到oracle数据库中 job 失败后重试时间的规律. 数据库版本号:11.2.0.3 測试说明:这里创建了一个日志表以及一个执行时 ...

  7. RockerMQ消息消费、重试

    消息中间件—RocketMQ消息消费(一) 消息中间件—RocketMQ消息消费(二)(push模式实现) 消息中间件—RocketMQ消息消费(三)(消息消费重试) MQ中Pull和Push的两种消 ...

  8. Rabbitmq消费失败死信队列

    Rabbitmq 重消费处理 一 处理流程图: 业务交换机:正常接收发送者,发送过来的消息,交换机类型topic AE交换机: 当业务交换机无法根据指定的routingkey去路由到队列的时候,会全部 ...

  9. RabbitMQ与.net core(四) 消息的优先级 与 死信队列

    1.消息的优先级 假如现在有个需求,我们需要让一些优先级最高的通知推送到客户端,我们可以使用redis的sortedset,也可以使用我们今天要说的rabbit的消息优先级属性 Producer代码 ...

随机推荐

  1. C博客作业05--2019-指针

    0.展示PTA总分 1.本章学习总结 1.1 学习内容总结 1.2 本章学习体会 2.PTA实验作业 2.16 -7 输出月份英文名 2.1.1 伪代码 char* getmonth(int n) { ...

  2. sql server建库建表(数据库和数据表的常用操作)

    数据库和数据表 (开发常用 操作) 一,数据库的创建 一个SQLServer 是由两个文件组成的:数据文件(mdf) 和日志文件(ldf),所以我们创建数据库就是要为其指定数据库名.数据文件和日志文件 ...

  3. CentOS6下安装zabbix3.4

    安装环境 系统 Centos6.7,最小化安装 CPU 2核 内存 2G 硬盘 200G IP 192.168.100.168/24 主机名 zabbix Zabbix版本 3.4 数据库 mysql ...

  4. 简单地认识一下 HTML

    简单复盘一下 HTML. 1.HTML 什么是 HTML?HTML 是 Hyper Text Markup Language 的简写,译成中文是「超文本标记语言」. 顾名思义,超文本,就是不止于文本, ...

  5. 后端传给前端Long类型数据,导致精度丢失

    1.前几天遇到了一个问题,后端向前端传递一个Long类型的数据,前端拿到的值不对. 2.当Long类型的数据大于17位时候,就会出现精度丢失的情况. 3.解决办法 我们采用的解决方案是将后端的Long ...

  6. 【Android - 自定义View】之自定义View实现“刮刮卡”效果

    首先来介绍一下这个自定义View: (1)这个自定义View的名字叫做 GuaguakaView ,继承自View类: (2)这个View实现了很多电商项目中的“刮刮卡”的效果,即用户可以刮开覆盖层, ...

  7. 【搞定面试官】try中有return,finally还会执行吗?

    本篇文章我们主要探讨 一下如果try {}语句中有return,这种情况下finally语句还会执行吗?其实JVM规范是对这种情况有特殊规定的,那我就先上代码吧! public class Final ...

  8. 上手spring boot项目(一)之如何在controller类中返回到页面

    题记:在学习了springboot和thymeleaf之后,想完成一个项目练练手,于是使用springboot+mybatis和thymeleaf完成一个博客系统,在完成的过程中出现的一些问题,将这些 ...

  9. shell 解析 json

    如果 想利用 shell 从 json 数据 中 解析出某个字段, 可以 利用 正则匹配, 比如 想 解析出 "spanId":"8461203268866670975& ...

  10. MySQL的存储(二、创建表并插入)

    创建表 首先创建一个 spiders的数据库 cursor.execute("create database spiders default character set utf8" ...