丢失消息则丢了数据,这是我们不能接受的,否则MQ意义何在?
因此主流MQ其实都提供了可靠性投递机制,确保即使网络异常,消息也能可靠传递,而不会丢失。
如果发现还是丢失消息了,多半是开发者问题,很可能没有正确配置MQ。不同MQ在保证消息可靠传递方面的实现原理其实也是一样的。
1 验证丢失的消息
大公司一般都通过分布式链路追踪系统,很方便追踪每条消息。
如果是中小公司,也有个简单方案验证。即利用MQ的有序性:
- 在Producer端,给每个发出的消息附加一个连续递增的序号
- 然后在Consumer端检查这序号的连续性
- Consumer收到消息序号严格递增,则无消息丢失
- 若存在序号不连续,则丢了消息
通过缺失的序号还能确定到底丢失的哪条消息
大多MQ客户端支持拦截器,可在Pro发消息前的拦截器中注入序号到消息中,在Con收消息的拦截器中检测序号连续性。
- 好处
消息验证代码不会侵入业务代码。系统稳定后也方便将验证逻辑关闭/删除。
分布式系统下实现验证方法,须注意:
- Kafka、RocketMQ不保证在Topic上的严格顺序,只保证分区上的消息有序,所以在发消息时须指定分区。且在每个分区单独验证消息序号连续性。
如果系统的Producer多实例,由于并不好协调多Producer之间的发送顺序,所以也需要每个Producer分别生成消息序号,且需要附加Producer标识,在Con端按每个Pro分别验证序号连续性。
Consumer实例数量最好和分区数量一一对应,如此便可方便在Con内验证消息序号连续性。
2 确保消息可靠传递
有小伙伴要问了,到底哪些地方会导致丢消息,又该如何避免呢?
- 消息从生产到消费完成的阶段
2.1 生产阶段
在Producer创建消息出来,通过网络传输发送到Broker。
MQ通过最常用的请求确认机制保证消息可靠传递:
调用发消息方法时,MQ客户端把消息发至于Broker,Broker收到后,给客户端返回确认响应,表明已收。客户端收到响应后,完成一次正常消息的发送。
只要Pro收到Broker的确认,即可保证消息在生产阶段不会丢失。
- 有些MQ长时间未收到发送确认响应后,会自动重试
- 若重试失败,以返回值或者异常方式通知用户
写发消息代码时,注意正确处理返回值或捕获异常,即可保证该阶段消息不会丢失。
示例
以Kafka为例看可靠发送消息:
同步发送时,只要注意捕获异常即可。
try {
RecordMetadata metadata = producer.send(record).get();
System.out