【Fifty Project - D33】
连带着端午一起给自己放了一个小长假,返校的第一天开始了无尽的挨骂QAQ,一个小论文从上午讨论到下午,是真的一坨,也是真的不想写哇
今日完成记录
Time | Plan | 完成情况 |
---|---|---|
8:30 - 9:30 | RabbitMQ学习 | √ |
9:30 - 11:00 | 挨骂 | √ |
13:30 - 14:30 | 练胸 | √ |
14:30 - 15:30 | 继续挨骂 | √ |
15:30 - 17:30 | RabbitMQ学习 | √ |
19:00 - 20:30 | 篮球 | √ |
20:30 - 22:00 | 羽毛球 | √ |
RabbitMQ
粗略完成了初级篇,今天开始高级篇,主要是关于消息队列的可靠性部分
主要学习问题有两个:
- 如何保证MQ消息的可靠性
- 如果真的出现了消息发送失败,有没有其他的兜底方案
在讨论这两个问题之前,先分析一下在MQ发送消息到消费消息成功这个过程中,哪些环节可能出现丢失消息?
如上图,可能发生消息丢失的情况有:
- 生产者发送消息给交换机:
- 连接MQ失败
- 发送消息找不到交换机
- 交换机接收但是找不到匹配的队列【路由失败】
- 消息到达MQ后处理消息的进程发生异常
- MQ导致消息丢失:
- 消息发送到队列后未被消费就宕机了
- 消费者处理消息时:
- 消息接收后未处理就宕机
- 消息接收后处理过程抛出异常
因此,我们应该在这三个方面保证MQ的可靠性:
- 确保生产者一定会将消息发送到MQ
- 确保MQ不会弄丢消息
- 确保消费者成功消费消息
生产者的可靠性
生产者重试机制
首先针对第一种场景:生产者发送消息时,由于网络导致的MQ连接中断
为了解决这个问题,SpringAMQP提供了消息发送的重试机制:当RabbitTemplate与MQ连接超时后,进行多次重试
修改application.yaml增加相关设置
spring:rabbitmq:connection-timeout: 1stemplate:retry:enabled: trueinitial-interval: 1000ms # 失败后的初始等待时间multiplier: 1 # 失败后下次的等待时长倍数 下次等待时间 = 上一次等待时间/initial-interval * multipliermax-attempts: 3 # 最大尝试重连次数
简单测试一下,首先停掉rabbitmq服务,模拟网络连接失败,然后启动测试脚本尝试发送消息,观察日志情况。
06-05 19:24:41:919 INFO 33216 --- [ main] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [192.168.4.41:5672]
06-05 19:24:43:948 INFO 33216 --- [ main] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [192.168.4.41:5672]
06-05 19:24:45:970 INFO 33216 --- [ main] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [192.168.4.41:5672]
观察发现等待时间间隔是2s,并不是1s,实际上这是因为其他操作的一些耗时和计算开销。当设置初始间隔为2s,退避指数为2,最大等待时间为20s,发现等待时间变化是:3,5,9,11,21,21,21。每一项减去1刚好符合计算公式,所以这个1s应该就是一些其余的计算开销。
生产者确认机制
一般来说,生产者和MQ之间的网络通畅,基本不会出现消息丢失的情况,因此大多数情况不需要考虑这个问题,但是在少数情况下,也会出现消息发送到MQ后丢失的现象:
- MQ内部处理消息的进程异常
- 生产者发送消息到MQ后没找到exchange
- 发送到exchange后没找到queue,无法路由
因此设计了生产者确认机制,publisher confirm和publish return。