RabbitMQ高级特性--消息100%投递成功方案

 保障消息100%投递成功设计方案


 什么是生产端的可靠性投递?

  1. 保障消息的成功发出
  2. 保障MQ节点的成功接收
  3. 发送端收到MQ节点(Broker) 确认应答
  4. 完善的消息补偿机制

如果想保障消息100%投递成功,只做到前三步不一定能够保障。有些极端情况,比如生产端在投递消息时可能失败了,或者说生产端投递了消息,MQ Broker也收到了,MQ Broker在返回确认应答时,由于网络闪断导致生产端没有收到应答,此时这条消息就不知道投递成功了还是失败了,所以针对这些情况需要做一些补偿机制。

互联网大厂的解决方案:

  1. 消息落库,对消息状态进行打标
  2. 消息的延迟投递,做二次确认,回调检查

具体使用哪种要根据业务场景和并发量、数据量大小来决定

方案一:消息信息落库,对消息状态进行打标的方案如下图:

step 1:进行业务数据入库:比如发送一条订单消息,首先把业务数据也就是订单信息进行入库,然后生成一条消息,把消息也进行入库,这条消息应该包含消息状态属性,并设置初始值比如为0,表示消息创建成功正在发送中,这种方式缺陷在于我们要对数据库进行持久化两次。

step 2:首先要保证第一步消息都存储成功了,没有出现任何异常情况,然后生产端再进行消息发送。如果失败了就进行快速失败机制。

step 3:MQ把消息收到的结果应答(confirm)给生产端。

step 4:生产端有一个Confirm Listener,去异步的监听Broker回送的响应,从而判断消息是否投递成功,如果成功,去数据库查询该消息,并将消息状态更新为1,表示消息投递成功。

假设step 2 已经OK了,在第三步回送响应时,网络突然出现了闪断,导致生产端的Listener收不到这条消息的confirm应答,也就是说这条消息的状态一直为0了。

step 5:此时我们需要设置一个规则,比如说消息在入库时候设置一个临界值timeout,5分钟之后如果状态还是0,那就需要把消息抽取出来。这里,使用分布式定时任务,去定时抓取DB中距离消息创建时间超过5分钟的且状态为0的消息。

step 6:把抓取出来的消息进行重新投递(Retry Send),也就是从第二步开始继续往下走。

step 7:当然有些消息可能由于一些实际的问题无法路由到Broker,比如routingKey设置不对,对应的队列被误删除了,这种消息即使重试多次也仍然无法投递成功,所以需要对重试次数做限制,比如限制3次,如果投递次数大于3次,那么就将消息状态更新为2,表示这个消息最终投递失败。

对于方案一可靠性投递,在高并发的场景下是否适合?

对于方案一,需要做两次数据库的持久化操作,在高并发场景下数据库将存在性能瓶颈。其实在核心链路中只需要对业务数据进行入库,消息没必要先入库,可以做一个消息的延迟投递,做二次确认,回调检查。

方案二:消息的延迟投递,做二次确认,回调检查,如下图:

Upstream Service上游服务也就是生产端,Downstream service下游服务也就是消费端,Callback service是回调服务。

step1:先将业务消息进行入库,然后生产端将消息发送出去,注意一定是等数据库操作完成:之后再去发送消息。

step 2:在发送消息之后,紧接着生产端再次发送一条消息(Second Send Delay Check),即延迟消息投递检查,这里需要设置一个延迟时间,比如5分钟之后进行投递。

step 3:消费端去监听指定队列,将收到的消息进行处理。

step 4:处理完成之后,发送一个confirm消息,也就是回送响应,但是这里响应不是正常的ACK,而是重新生成一条消息,投递到MQ中。

step 5:上面的Callback service是一个单独的服务,其实它扮演了方案一的存储消息的DB角色,它通过MQ去监听下游服务发送的confirm消息,如果Callback service收到confirm消息,那么就对消息做持久化存储,即将消息持久化到DB中。

step6:5分钟之后延迟消息发送到MQ了,然后Callback service还是去监听延迟消息所对应的队列,收到Check消息后去检查DB中是否存在消息,如果存在,则不需要做任何处理,如果不存在或者消费失败了,那么Callback service就需要主动发起RPC通信给上游服务,告诉它延迟投递的这条消息没有找到,需要重新发送,生产端收到信息后就会重新查询业务消息然后将消息发送出去。

方案二也是互联网大厂更为经典和主流的解决方案:

方案二不一定能保障百分百投递成功,但是基本上可以保障大概99.9%的消息是OK的,有些特别极端的情况只能是人工去做补偿了,或者使用定时任务去做。

方案二主要目的是为了减少数据库操作,提高并发量。 在高并发场景下,最关心的不是消息100%投递成功,而是一定要保证性能,保证能抗得住这么大的并发量。所以能减少数据库的操作就尽量减少,可以异步的进行补偿。

其实在主流程里面是没有这个Callback service的,它属于一个补偿的服务,整个核心链路就是生产端入库业务消息,发送消息到MQ,消费端监听队列,消费消息。其他的步骤都是一个补偿机制。

 

猜你喜欢

转载自blog.csdn.net/LuuvyJune/article/details/92772358
今日推荐