可靠消息的分布式解决方案
约 2705 字大约 9 分钟
2025-09-10
可靠消息的基本概述
在分布式事务解决方案中,“可靠消息”指的是一种通过消息队列来保证分布式系统中多个服务间数据一致性的事务处理截止。它的核心思想是:将一个分布式事务拆分成多个本地事务,通过可靠消息传递来协调这些本地事务的执行,最终达到数据最终一致性的目的。
在微服务架构或分布式系统中,一个业务操作往往需要由多个服务共同完成,每个服务都有自己的数据库,彼此之间是独立的。如果直接使用传统的ACID分布式事务(如2PC),会存在性能差、耦合度高、扩展性不好等问题。基于此类原因提出了最终一致性方案,其中可靠消息就是实现最终一致性的常用手段。
可靠消息的核心思想
可靠消息的核心在于:
- 将事务操作与消费发送绑定在一起,确保消息的可靠性;
- 通过消息队列异步通知其它服务执行后续操作;
- 保证消息即不会丢失,也不会重复消费,最终让各个服务达到一致;
简单来说就是:一个服务在完成自己的本地事务之后,通过可靠的方法发送一条消息给消息队列,其他服务监听到这个消息并执行相应的操作,从而完成整个业务逻辑。
可靠消息典型的实现方式
本地消息表
业务服务在执行本地事务的时,同时往本地数据库的一个消息表中插入一条消息记录(该消息记录和业务数据在同一个本地事务中提交)。
有一个后台任务在不断的轮询这个消息表,将已提交但未发送的消息发送到消息队列(如RabbitMQ、Kafka等)。
消息发送成功后,标记该消息未“已发送”,避免重复发送;
其他服务监听消息队列,接受到消息后执行相应的业务逻辑,并可发送确认回执;
如果消息消费失败,则进行重试,直到成功,以此保证最终一致性;
本地消息表的优点是实现相对简单,依赖少,缺点是业务是需要维护一张消息表,有一定的侵入性。如下所示,演示了用户注册的基础流程:
- 用户注册时:用户服务添加用户的基础信息;
- 用户注册时:积分服务初始化用户的积分信息;
事务消息
某些消息队列(如阿里巴巴开源的RocketMQ)提供了事务消息的能力,流程大致如下:
- 生产者发送一条“半消息(Half Message)”到MQ,此时消息对消费者不可见;
- 然后执行本地事务;
- 根据本地事务执行结果,向MQ提交二次确认(Commit或Rollback),决定这条消息是否真正投递给消费者;
- 如果本地事务执行成功:提交消息,然后消费者消费消息;
- 如果本地事务执行失败:回滚消息,消费者收不到消息;
- MQ提供事务状态回查机制,防止生产者宕机等原因未反馈,确保消息最终被正确处理;
事务消息的优点在于对业务代码的侵入小,由MQ保证消息的可靠性,缺点是依赖于特定的MQ,不是所有的MQ都支持事务消息。如下所示,仍然为要用户注册时的具体流程;
可靠消费要解决的关键问题
为了实现真正的可靠消息,必须解决以下几个核心问题:
- 消息的可靠性发送:确保消息本地事务成功后一定能发出去,不会因为服务崩溃等导致消息丢失;
- 消息的幂等性消费:由于网络抖动等原因,消费者可能会重复收到同一条消息,因此消费者必须能识别并幂等处理;
- 消息的顺序性和事务状态的追踪:在复杂场景下,还需保证消息的顺序,以及能够追踪事务状态,进行补偿或回查;
- 避免消息重复消息/丢失消费:通过唯一消息ID、去重表、状态记录等方式解决;
可靠消息的适用场景
- 对实时性要求不高,但要求最终一致性的场景(如订单创建后发送通知、积分发送、日志同步等)
- 跨服务、跨数据库的业务操作;
- 高并发、高性能要求的系统,不想使用2PC等强一致性方案;
- 异步解耦,提升系统扩展性和可用性;
最大努力通知
最大努力通知是分布式事务解决方案中实现最终一致性的一种策略,它强调的是:系统会尽最大努力通知其它参与方执行相应的操作,但不做严格一致性保证,通常通过异步、重试等方式尽量确保成功,适用于对一致性要求较低但更注重系统可用性和性能的场景。
最大努力通知与可靠消息的区别
解决方案思想不同
可靠消息一致性,发起通知方需要保证将消息发出去,并且将消息发送到接受通知方,消息可靠性关键由发起通知方来保证。
最大努力通知,发起通知方尽最大努力将业务处理结果通知给消息接受方,但是可能消息接受不到,此时需要接受通知方主动调用发起通知方的接口查询业务处理结果,通知的可靠性关键在接受通知方;
两者的业务场景不同
可靠消息 一致性关注的是交易过程的事务一致,以异步的方式完成交易。
最大努力通知关注的是业务完成后的通知,即将事务执行结果可靠的通知出去。
技术解决方向不同
可靠消息一致性要解决消息从发出到接受的一致性,即消息发出并且被接受到;
最大努力通知无法保证消息从发出到接受的一致性,只提供消息接受的可靠性机制。可靠机制是,最大努力将消息通知给接受方,当消息无法被接受方接受到时,由接受方主动查询消息(业务处理结果)。
最大努力通知的工作流程
以最典型的业务场景举例子:用户支付成功后,支付系统需要通知订单系统更新订单状态为“已支付”。
- 用户支付成功,支持系统完成本地事务(如更新支付记录状态为“支付成功”);
- 支付系统尝试通过HTTP接口、RPC或消息队列等方式通知订单系统:“这笔订单已支付,请更新状态”。
- 如果通知成功(订单系统返回成功响应),流程结束;
- 如果通知失败(比如网络延迟超时,订单系统宕机),支付系统会按照一定的策略进行有限次数的重试;
- 如果重试多次仍然失败,支付系统就不再继续通知,但会记录该通知失败的事件(比如数据库或日志系统)。
- 订单系统可能通过以下的方式来保证最终一致性:
- 主动调用支付系统的接口查询支付记录的状态;
- 人工干预或预警:对于重要业务,可以设置告警,由人工介入处理;
- 订单系统也可以提供一个回调接口,供支付系统补单时调用;
如下图所谓为最大努力通知的流程: