4

MQ系列11:如何保证消息可靠性传输(除夕奉上) - Hello-Brand

 1 year ago
source link: https://www.cnblogs.com/wzh2010/p/15888525.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

MQ系列1:消息中间件执行原理
MQ系列2:消息中间件的技术选型
MQ系列3:RocketMQ 架构分析
MQ系列4:NameServer 原理解析
MQ系列5:RocketMQ消息的发送模式
MQ系列6:消息的消费
MQ系列7:消息通信,追求极致性能
MQ系列8:数据存储,消息队列的高可用保障
MQ系列9:高可用架构分析
MQ系列10:如何保证消息幂等性消费

这篇我们来说说 MQ 消息的可靠性传输。可靠性传输其实包含两种情况:一种是重复消费的情况,我们上一篇的幂等性消费解决的就是这个问题;另外一种是消息丢失的情况的,要确保我们生产的消息一定最终会得到消费。这时候就要从消息执行的几个阶段去保证,每一个阶段都不能出现问题。

image

2 消息生产阶段

消息生产阶段指的是消息从生产到消息发送出去,经过网络传输,再到达Broker服务器并被接收的这整个阶段,我们需要一个健壮的确认机制(ACK)来保证消息传递的可靠性。如果说消息被接收到之后可以反馈给消息生产方去确认,那这个过程就比较完美了。

  • 消息创建和发送事务性原则保证,要么成功,要么不成功
  • 同步发送时,处理好返回值,如果发生异常,则进行异常捕捉并处理。
  • 异步发送时,处理好回调的工作,如果发生异常,则进行异常捕捉并处理。
  • 异常/超时重试机制:如果长时间收不到确认返回结果,则需要进行重试;如果返回的结果是异常的,也可以有限的进行重试。
    超时重试和异常重试需要谨慎使用,重试次数也要谨慎斟酌。建议只对消息丢失、错误、丢失特别敏感的时候使用,如果过度使用,反而可能造成请求堆积,队列阻塞。
    image

3 消息服务器处理阶段

Broker作为消息服务器,主要用于消息收发的操作。一般情况下只要消息服务正常运行,并依赖数据持久化能力,丢消息的可能行就比较小。
但是在很多场景下,为了提升消息队列的效率,为了提升吞吐能力,在没有确定完成持久化动作(刷盘)之前,就会把确认消息返回。即只要消息进行
Commit了,那就是成功的。但是如果还没持久化成功便发生了宕机,那就有存在消息丢失的风险。可以参照如下优化:

  • 单节点模式下的Broker,优化Broker参数,在收到消息并持久化到磁盘之后才把确认消息返回给生产者 Producer。下面以RocketMQ为例子介绍配置优化手段:
    • 如果是RabbitMQ,则将Message的delivermode设置为2,exchange持久化动作操作完成之后才返回确认消息,确保消息不丢失;
    • 将 flushDiskType 设置为 SYNC_FLUSH,这是同步刷盘的意思,那就要求把这个动作同步完成之后才算消息发送成功。
  • 上面说的是单节点模式,如果配置了集群模式,一般是多副本,则要求确认消息要发到 一半以上(N/2 + 1)的节点并得到响应。这样Producer才算真正发送成功。
    image

4 消息消费阶段

消息存储到了Broker之后,剩下的就是消息消费了。消息消费阶段跟生产阶段大概一致,都是使用确认机制来保证消息的可靠性和传输的。
当Consumer从Broker拉取到消息之后,开始消费消息,执行业务的的逻辑程序,业务程序执行成功后,才给Broker发送消费确认响应。
如果没成功或者消息在发送中途丢失,就没有确认响应,这样的话,在下一轮消息拉取的时候,Broker依旧会返回这一条消费数据给你,避免网络抖动原因或者Consumer在执行消费出错导致丢失。

4.1 消费分区的策略模式

多个消费者消费用一个分区,我们经常会出现这种情况:同一个Consumer Group 里面有多个Consumer,比如Comsumer A 拉走了某一批数据,但是还没返回确认消息,Consumer B 又过来要 拉数据了,Broker要怎么判定呢?
这边举个例子:Consumer A 拉取 index = 106 位置的数据,但是还没返回消费完成的确认信息,这时候消费位置依然是 index = 10086,如果 Consumer B 也过拉取数据,则

  • Broker接收确认信息的时间未超时(比如配置为5s),则说明Consumer A还在消费中,回绝了Consumer B的请求。
  • Broker接收确认信息的时间已超时(比如配置为5s),则说明Consumer A消费失败了,返回 index = 106 位置的消息数据给 Consumer B。
    所以,多个消费者消费同一个分区,要严格按照顺序消费,具体可以参考官网的介绍,很详细。

4.2 消费重试和死信队列

在RocketMQ中,当消息第一次消费失败时,消息队列会自动进行消息重试,达到最大重试次数(可配置阈值,比如5)后,若消费依然失败,则表明消费者在正常情况下无法正确地消费该消息。此时,消息队列RocketMQ版不会立刻将消息丢弃,而是将其发送到该消费者对应的特殊队列中。这种无法被消费的消息称为死信消息(Dead-Letter Message),存储死信消息的特殊队列称为死信队列(Dead-Letter Queue)。
可以使用单独的作业服务进行独立处理,比如重新发送死信消息进行消费,避免消息漏处理导致业务服务可用性问题。

image

总得来说:MQ可以从三个角度来分析:生产者丢数据、消息队列服务器(Broker)丢数据、消费者丢数据
生产者丢数据:RabbitMQ提供transaction和confirm模式来确保生产者不丢消息。
消息队列服务丢数据:开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。
消费者丢数据:与生产者基本一直,等消费完成并接收到confirm才能确认是消费成功。超时或者失败则重试,重试超过指定阈值的时候,计入死信队列并独立处理。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK