简介:
谈到消息的可靠性投递,无法避免的,在实际的工作中会经常碰到,比如一些核心业务需要保障消息不丢失
-
第一种方案(数据库记录状态变更方式):
- Step 1: 首先把消息信息(业务数据)存储到数据库中,紧接着,我们再把这个消息记录也存储到一张消息记录表里(或者另外一个同源数据库的消息记录表)
- Step 2:发送消息到MQ Broker节点(采用confirm方式发送,会有异步的返回结果)
- Step 3、4:生产者端接受MQ Broker节点返回的Confirm确认消息结果,然后进行更新消息记录表里的消息状态。比如默认Status = 0 当收到消息确认成功后,更新为1即可!
- Step 5:但是在消息确认这个过程中可能由于网络闪断、MQ Broker端异常等原因导致 回送消息失败或者异常。这个时候就需要发送方(生产者)对消息进行可靠性投递了,保障消息不丢失,100%的投递成功!(有一种极限情况是闪断,Broker返回的成功确认消息,但是生产端由于网络闪断没收到,这个时候重新投递可能会造成消息重复,需要消费端去做幂等处理)所以我们需要有一个定时任务,(比如每5分钟拉取一下处于中间状态的消息,当然这个消息可以设置一个超时时间,比如超过1分钟 Status = 0 ,也就说明了1分钟这个时间窗口内,我们的消息没有被确认,那么会被定时任务拉取出来)
- Step 6:接下来我们把中间状态的消息进行重新投递 retry send,继续发送消息到MQ ,当然也可能有多种原因导致发送失败
- Step 7:我们可以采用设置最大努力尝试次数,比如投递了3次,还是失败,那么我们可以将最终状态设置为Status = 2 ,最后 交由人工解决处理此类问题(或者把消息转储到失败表中)。
-
-
第二种方案(延迟查询+补偿机制):
首先,介绍下,图中模块表示含义:
Upstream Service表示生产端;
Downstream Service表示消费端;
MQ Broker可能是一个MQ集群;
Callback Service表示回调服务;
还是以订单为例。
- 第一步:先将订单业务入库,然后把消息发送到broker端的一个队列1。注意这次,我并不是再把我订单消息又存储到另外一个数据库中,这里只进行一次入库操作。
- 第二步:第一步发送消息后,设置一个延迟时间,比如五分钟再次发送该消息,这条消息会发送到broker端的队列2;
- 第三步:消费端去监听指定的队列1,对消息进行消费处理;
- 第四步:消费端把消息真正处理完之后,还要自己内部再生成一条新的消息叫做send confirm 确认。这条confirm的确认消息也会发送到broker端的一个队列3。
- 第五步:回调服务会有一个Confirm Listener去监听这个队列3,如果回调服务收到了来自消费端回送的这条confirm消息,那么,回调服务则认为消费端对数据消费成功,对这条消息做一个持久化的存储,即将消息存入MSG DB。
- 第六步:五分钟之后,延迟投递的消息到达broker端指定队列2。回调服务会去监听队列2,如果延迟消息到达队列2,那么回调服务就会去检查MSG DB数据库,查看这条消息是否已经被消费端消费。
若MSG DB中存在记录,则回调服务什么都不做。
若MSG DB中不存在记录,说明消费者一直没有返回响应数据或者在返回过程中由于网络原因导致返回失败,这时。回调服务会主动发起RPC通信,发送一套resend的命令(带上消息的id),告知生产者刚才发的这条消息未找到,重新重新发送。然后,生产端再重复执行第一步。
消息延迟投递要点
延迟投递:相比第一种消息落库方案,该方案生产端在发送消息到broker端队列1时,还额外延迟发送同一条消息到broker端另一个队列2。
回调检查:若消费端对队列1中数据消费成功,则回调服务监听到消费端的确认消费消息,会将这条消息持久化到MSG DB中。当回调服务监听到队列2的延迟消息时,检查MSG DB是否存在记录。有记录则不处理,无记录则回调服务需发送一条resend命令给生产端,执行第一步重复操作。
极端情况:如果消费端一直没有返回confirm确认消息,或者回调服务在更新MSG DB时出现异常,怎么办?延迟投递能提供补偿机制。因为五分钟之后,回调服务一定能监听到这条消息,然后去MSG DB查找该消息记录是否存在,从而决定生产端是否需要重新发送消息。
消息延迟投递优点
少做一次DB的存储,提高性能:
可能最开始,若执行两次insert持久化操作,可能最多每秒1000单,但如果只持久化一次,可能就是每秒2000单,这样就相当于节省一台服务器了,而且减少了这种两次持久化数据库操作可能出现的问题。
异步补偿机制,提供可靠性:两个DB实现解耦。主流核心链路:订单消息入库,生产端发送订单消息到broker端,消费者负责监听队列进行消费。Callback提供的是一个补偿服务,它不是业务高峰期的核心链路,而是将它拆出来作为一个单独的服务进行消息的异步补偿。
总结:消息延迟投递方案最大限度的节省了一次数据信息落库的操作,提高整个高并发性能。同时,回调服务提供的异步补偿机制,让生产端可靠性投递有了进一步保障。