Rabbitmq消息补偿机制

消息补偿机制

1、SpringBoot 封装的补偿机制

在 SpringBoot 为 RabbitMQ 封装的依赖中,提供一种补偿机制,如果发出的消息在一段时间内没有响应(签收或者拒绝),那么该消息就会进行重发。默认情况下会隔5秒一直进行重发,直到消费者响应。

我们可以通过自定义配置参数来修改默认的补偿机制

spring: rabbitmq: listener: simple: retry: enabled: true # 自动触发补偿机制 max-attempts: 5 # 补偿机制尝试次数 max-interval: 10000 # 重试最大间隔时间 initial-interval: 2000 # 重试初始间隔时间 multiplier: 2 # 间隔时间乘子,间隔时间*乘子=下一次的间隔时间,最大不能超过设置的最大间隔时间

2、自定义补偿机制。对于封装的补偿机制存在一些不足,因为其是无差别补偿,也就是只要消费者没有响应就会重发,但是对于一些异常导致没有响应即使发几次都会导致没有响应(如数据计算异常,数据类型转换异常),这样的补偿机制就会消耗 CPU 资源。所以对于这些异常可以捕获然后直接处理。对于其他异常(如调用第三方接口失败)则可以进行补偿重试。

对于MQ 整个模块的补偿机制,可以参考下面的架构图

各步骤:

1、发生业务操作,业务数据写入数据库

2、生产者将消息发送给MQ的队列Q1

3、发送了一条与step2中一摸一样的延迟消息到对了Q3

4、消费者监听Q1,获取到了step2中发送的业务消息

5、消费者在收到生产者的业务消息后,发送了一条确认消息(记录收到的消息信息)到Q2

6、回调检查服务监听了Q2,获取到了消费者发送的确认消息

7、回调检查服务将这条确认消息写入数据库等待之后的比对

8、Q3中的延迟消息延迟时间已到,被回调检查服务接收到,之后就拿着这条延迟消息在数据库中比对,如果比对成功,证明消费者接收到了生产者的业务消息并处理成功(如果不处理成功谁会傻了吧唧发送确认消息呢);如果比对失败,证明消费者没有接收到生产者的业务消息,或者说消费者接收到了业务消息之后始终没有处理成功相关的业务并发送确认消息。这时回调检查服务就会调用生产者的相关业务接口,让生产者再次发送这条失败的消息

9、有一种最极端的情况,step2和step3的消息都发送失败了或者说在消息传递过程中发生意外丢失了!定时检查服务会一直轮询保存确认消息的数据库中的消息数据,并于生产者的业务数据库中的业务数据进行比对,如果两者比对数量一致,则代表业务执行没有问题;如果比对不一致,确认消息数据库的数据量小于生产者业务数据量的话,就证明消费者没有接收到生产者发送的消息。这时定时检查服务会通知生产者再次发送消息到MQ的队列Q1

消息幂等性

由于消息补偿机制的存在,可以更加有效保证消息可以被消费,但是带来的问题是可能某个消息执行的比较久,导致同一条消息再次被发送给了消费者,而前一条消息顺利执行完,这样一条消息就会被多次执行,所以消费者端的方法需要涉及成幂等性,也就是对于一条消息,无论被消费者消费几次,效果都是一样的。实现方案主要有两种。

1、唯一ID+指纹码。

唯一ID指的是使用 UUID、或者操作数据的主键,而指纹码是与业务相关的ID,比如雪花算法就是根据当前时间戳生成的,生成的ID就属于指纹码。

在生产者发送时创建 Messgae 对象,将业务数据以及唯一ID+指纹码保存到Meaasge对象中进行发送

Message message = MessageBuilder.withBody(msg.getBytes()).setContentType(MessageProperties.CONTENT_TYPE_JSON) .setContentEncoding("utf-8").setMessageId(UUID.randomUUID() + "").build(); amqpTemplate.convertAndSend(queueName, message);

然后在消费者端接收,获取ID,在消费者消费最后以其为主键添加到mysql中,在业务开始时检查是否存在,不存在继续执行。

缺点是高并发场景下会受到性能瓶颈限制。可以通过分库分表解决。

2、redis 操作。

在消费者方法开始使用 redis 的 setnx 方法来处理判断数据可以一步到位,是实现幂等性的最佳方案。

消息顺序执行

如果多个消费者监听同一个队列,那么默认下消息会依次顺序分配给消费者。

上面提到预取值概念,通过配置消费者端的 channel 的 basicQos 参数来修改,但是这会收到消费者执行快慢、生产者发送消息到队列的顺序等因素影响,所以并不可靠。

所以实现消息顺序执行的方式就是增加队列,拆分消费者,使每个消费者只监听一个队列。

相关推荐
键盘上的蚂蚁-31 分钟前
Python 语言结合 Flask 框架来实现一个基础的代购商品管理
jvm·数据库·oracle
代码欢乐豆1 小时前
MongoDB的部署和操作
数据库·mongodb
<e^πi+1=0>1 小时前
使用Locust对MongoDB进行负载测试
数据库·mongodb
圆蛤镇程序猿1 小时前
【什么是MVCC?】
java·数据库·oracle
开心邮递员1 小时前
sql server: split 函数;cross apply操作符
数据库·sql
老大白菜1 小时前
PostgreSQL 内置函数
数据库·postgresql
Damon撇嘴笑1 小时前
Cause: java.sql.SQLException: sql injection violation, comment not allow异常问题处理
java·数据库·sql
山林竹笋2 小时前
Java解析PDF数据库设计文档
数据库·pdf
Aimin20222 小时前
Kali系统(Debian 10.3) 遇到的问题
数据库·mysql·debian
Run Out Of Brain2 小时前
使用systemd管理MySQL服务器
服务器·数据库·mysql