RabbitMQ 消息的可靠投递


 

概述

可靠性投递

  • 保障消息能够成功发出
  • 保 障rabbitmq(broker)能够成功接收。接收指的是:broker接收到生产者发送的消息,放到exchange中,分发给对应的queue,交付给对应的消费者。
  • 发送端要收到broker的确认应答,确认broker已收到|处理消息
  • 完善的消息补偿机制。发送端没收到broker的确认应答,不知道消息是否成功投递成功,这时候就需要做一些补偿处理,比如重新投递。

rabbitmq的server又叫做broker,接收客户端的连接,实现AMQP实体服务,包含exchange、queue等多种组件。

说白了,broker就是rabbitmq服务器。
 

实现消息可靠性投递的、常见的2种解决方案

  • 消息状态入库,对消息状态进行打标
  • 延时再次投递消息,回调确认

 

方案一 消息状态打标

1、生产者将消息入库、消息状态入库

比如用户下单,产生一个Order对象,把这个Order对象数据持久化到数据库中。

单独用一张表来存储消息状态:
用外键关联消息(比如关联订单表的id),
设置一个status列记录消息投递状态,默认值为0,表示消息未投递到broker;
设置一个时间列,记录消息投递时间;
设置一个重试次数列,记录重新投递的次数,默认值为0。
 

2、生产者发送消息到broker

把Order对象发送到broker,因为消息都要转换为byte[ ]发送,什么类型都可以。
 

3、生产者接收到broker的确认应答,将数据库中该条消息的状态修改为1,表示成功投递
 

4、分布式系统的定时任务

如果消息投递一段时间后,未收到broker的确认应答,怎么补偿处理?

使用定时任务来做:

生产者每隔一段时间,比如5min,启动一条线程来查询数据库中重试次数达到指定值(比如3)、且投递时间已超过指定值(比如5min)的消息,将其状态修改为2,表示重试指定次数后仍未能成功投递;

再查询状态为0、且发送时间已超过指定时间的消息,重新投递,并更新投递时间为当前时间、重试次数+1;
 

方案一存在的问题

生产者执行定时任务也有额外的开销,生产者要进行2方面的数据库IO操作(消息本身+消息状态),IO是很花时间的,在高并发的情况下,数据库性能很容易成为系统性能的瓶颈。

并发量大的情况下,第一种方案严重拉低生产者的性能。

相比之下,第二种方案用得更多,但稍微复杂一点。

 

方案二 延时再次投递消息

1、生产者将消息入库,并将消息发送给broker,broker将消息放到对应的queue1中

2、消费者监听queue1,处理消息,处理一条消息后产生一条新消息作为确认(绑定queue2),比如以Order的id作为新消息,总之要能唯一标识处理的消息。

3、消费者将产生的消息发送给broker,broker将消息放到queue2中(注意不是消费者监听的queue1)

4、单独写一个callback service(回调服务),来监听queue2,把queue2中的消息入库,比如放在tb_msg_processed表中,一条记录代表一条已被消费者处理的消息

5、生产者发送消息后,延时再次发送这条消息(绑定queue3),比如3min|5min后再次发送这条消息。

6、回调服务监听queue3,把queue中消息与数据库中的记录对比,比如把queue3获取到的Order的id取出来,查询tb_msg_processed中有没有这个Order id,有就说明投递成功;没有就说说明未投妥,回调服务rpc通知生产者(传递order id),生产者从数据库查询该条消息的数据(order对象),重新投递(queue1)——重新走一遍流程。
 

第一种方案消费者使用数据包来确认应答(ack),第二种由消费者自己产生一条消息来确认应答。
在这里插入图片描述
整个流程中,生产者又叫做upstream service(上游服务),消费者又叫做downstream service(下游服务)。
 

方案二的优缺点

相较于第一种方案,第二种方案多写一个服务,每对生产者——消费者都使用一个额外的queue来确认,回调服务开发成本高些、略微复杂些;

部署回调服务又要使用、维护额外的机器,成本变高了。

但生产者的数据库IO操作减少了,提升了性能。只要性能上去,稍微增加点成本完全可以接受。

 

说明

(1)消息入库完成,然后发送消息(Order对象)到broker,注意顺序

(2)分布式事务对性能的影响很大,并发量中小的可以加事务,如果并发量很大,事务会严重拉低性能,不建议加事务(能不加就不加)

(3)不管是第一种、还是第二种,都很难做到100%的投递成功。优先考虑能够扛得住高并发(性能),在保证性能的前提下尽可能提高消息投递的可靠性

©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页