可靠消息最终一致性方案是指当事务发起方执行完成本地事务后并发出一条消息,事务参与方(消息消费者)一定能够接收消息并处理事务成功,此方案强调的是只要消息发给事务参与方最终事务要达到一致。
本地消息表
本地消息表这个方案最初是eBay提出的,此方案的核心是通过本地事务保证数据业务操作和消息(在本地建一张消息日志表)的一致性,然后通过定时任务将消息发送至消息中间件,待确认消息发送给消费方成功再将消息删除。
1、用户注册
用户服务在本地事务新增用户和增加 ”积分消息日志“。(用户表和消息表通过本地事务保证一致)
下边是伪代码
begin transaction;
//1.新增用户
//2.存储积分消息日志
commit transation;
这种情况下,本地数据库操作与存储积分消息日志处于同一个事务中,本地数据库操作与记录消息日志操作具备原子性。
2、定时任务扫描日志
如何保证将消息发送给消息队列呢?
经过第一步消息已经写到消息日志表中,可以启动独立的线程,定时对消息日志表中的消息进行扫描并发送至消息中间件,在消息中间件反馈发送成功后删除该消息日志,否则等待定时任务下一周期重试。
3、消费消息
如何保证消费者一定能消费到消息呢?
这里可以使用MQ的ack(即消息确认)机制,消费者监听MQ,如果消费者接收到消息并且业务处理完成后向MQ发送ack(即消息确认),此时说明消费者正常消费消息完成,MQ将不再向消费者推送消息,否则消费者会不断重试向消费者来发送消息。
积分服务接收到”增加积分“消息,开始增加积分,积分增加成功后向消息中间件回应ack,否则消息中间件将重复投递此消息。由于消息会重复投递,积分服务的”增加积分“功能需要实现幂等性。
总结:用户服务在本地事务中记录消息日志,启动一个独立的线程,用定时任务来扫描表中的数据,将消息发送给MQ。若MQ反馈成功则将这一行数据删除,以此来保证消息一定能到达MQ。消费者在接收到消息并处理完成后给MQ发一个ACK确认,如果MQ没有收到这个ACK确认,就一直重发消息(注意考虑幂等性)。