消息如何保障100%的投递成功?
BAT/TMD 互联网大厂的解决方案:
-
1.消息落库,对消息状态进行打标
-
在发送消息的时候,需要将消息持久化到数据库中,并给这个消息设置一个状态(未发送、发送中、到达)。当消息状态发生了变化,需要对消息做一个变更。针对没有到达的消息做一个轮训操作,重新发送。对轮训次数也需要做一个限制3-5次。确保消息能够成功的发送.
-
消息的延迟投递,做二次确认,回调检查
具体采用哪种方案,还需要根据业务与消息的并发量而定。 -
保障MQ我们思考如果第一种可靠性投递,在高并发的场景下是否合适?
第一种方案对数据有两次入库,一次业务数据入库,一次消息入库。这样对数据的入库是一个瓶颈。
-
-
其实我们只需要对业务进行入库。
消息的延迟投递,做二次确认,回调检查,这种方式并不一定能保证100%成功,但是也能保证99.99%的消息成功。如果遇到特别极端的情况,那么就只能需要人工去补偿,或者定时任务去做。
-
2.消息落库,对消息状态进行打标
step1:业务消息入库成功后,第一次消息发送。
step2:同样在消息入库成功后,发送第二次消息,这两条消息是同时发送的。第二条消息是延迟检查,可以设置2min、5min 延迟发送。
step3:消费端监听指定队列。
step4:消费端处理完消息后,内部生成新的消息send confirm。投递到MQ Broker。
step5: Callback Service 回调服务监听MQ Broker,如果收到Downstream service发送的消息,则可以确定消息发送成功,执行消息存储到MSG DB。
step6:Check Detail检查监听step2延迟投递的消息。此时两个监听的队列不是同一个,5分钟后,Callback service收到消息,检查MSG DB。如果发现之前的消息已经投递成功,则不需要做其他事情。如果检查发现失败,则Callback 进行补偿,主动发送RPC 通信。通知上游生产端重新发送消息。-这样做的目的:少做了一次DB存储。关注点并不是百分百的投递成功,而是性能。
幂等性是什么?
幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中,即f(f(x)) = f(x)。简单的来说就是一个操作多次执行产生的结果与一次执行产生的结果一致。
- 我们可以借鉴数据库的乐观锁机制:
- 比如我们执行一条更新库存的SQL语句:
- UPDATE T_REPS SET COUNT = COUNT - 1,VERSION = VERSION + 1 WHERE VERSION = 1
- 利用加版本号Version的方式来保证幂等性。
唯一ID+指纹码机制
-
唯一ID + 指纹吗机制,利用数据库主键去重。
保证唯一性 -
SELECT COUNT(1) FROM T_ORDER WHERE ID = 唯一ID + 指纹码
-
如果查询没有,则添加。有则不需要做任何操作,消费端不需要消费消息。
-
好处:实现简单
-
坏处:高并发下有数据库写入的性能瓶颈
-
解决方案:跟进ID进行分库分表进行算法路由
-
分摊流量压力。
-
Redis 原子特性实现
-
最简单使用Redis的自增。
-
使用Redis进行幂等,需要考虑的问题。
-
第一:我们是否需要数据落库,如果落库的话,关键解决的问题是数据库和缓存如何做到原子性?加事务不行,Redis和数据库的事务不是同一个,无法保证同时成功同时失败。大家有什么更好的方案呢?
-
第二:如果不进行落库,那么都存储到缓存中,如何设置定时同步的策略?
-
怎么做到缓存数据的稳定性?
-