这是一个基于消息的分布式事务的一部分,主要通过消息来实现,生产者把消息发到队列后,由消费方去执行剩下的逻辑,而当消费方处理失败后,我们需要进行重试,即为了最现数据的最终一致性,在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. kali下安装phpstudy

    只需要用到三条命令就可完成,具体如下: wget -c http://lamp.phpstudy.net/phpstudy.bin chmod +x phpstudy.bin #权限设置 ./phps ...

  2. RocketMQ一个新的消费组初次启动时从何处开始消费呢?

    目录 1.抛出问题 1.1 环境准备 1.2 消息发送者代码 1.3 消费端验证代码 2.探究CONSUME_FROM_MAX_OFFSET实现原理 2.1 CONSUME_FROM_LAST_OFF ...

  3. 2019-10-9:渗透测试,基础学习,php文件上传,mysql基础

    header("Content-Type:text/html;charst="utf-8")设置头部信息,解决编码问题setcookie("loginStrin ...

  4. Spring与Shiro整合 登陆操作

    Spring与Shiro整合 登陆操作 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 编写登陆Controller方法  讲解: 首先,如果你登陆失败的时候,它会把你的异常信息丢到 ...

  5. 【NHOI2018】扑克游戏

    [问题描述] 有一种别样“小猫钓鱼”扑克游戏.有 N 张牌,每张牌都有一个花色和点数.游戏的规则:扑克接龙时,若前面有同样花色的牌,你可以将这两张牌连同之间的牌都取走,得到的分值为取走牌点数之和.这里 ...

  6. JavaScript笔记九

    1.数组方法 reverse() - 可以用来反转一个数组,它会对原数组产生影响 concat() - 可以连接两个或多个数组,它不会影响原数组,而是新数组作为返回值返回 join() - 可以将一个 ...

  7. 迁移桌面程序到MS Store(13)——动态检查Win10 API是否可用

    假设我们现有一个WPF程序,需要支持1903以前的Windows 10版本.同时在1903以后的版本上,额外多出一个Ink的功能.那么我们就可以通过ApiInformation.IsApiContra ...

  8. Spring Cloud - Zuul添加过滤器

    Zuul作为网关的其中一个重要功能,就是实现请求的鉴权.而这个动作我们往往是通过Zuul提供的过滤器来实现的. 一.过滤器方法的作用 想要使用Zuul实现过滤功能,我们需要自定义一个类继承ZuulFi ...

  9. Swift - 给UICollectionview设置组背景和圆角

    钟情圆角怎么办 最近由于我们的UI钟情于圆角搞得我很方,各种圆角渐变,于是就有了下面这篇给UICollection组设置圆角和背景色的诞生,不知道在我们平时有没有遇到这样子的一些需求,就是按照每一组给 ...

  10. .net压缩文件

    首先nuget安装DotNetZip 代码很少