合作机构:阿里云 / 腾讯云 / 亚马逊云 / DreamHost / NameSilo / INWX / GODADDY / 百度统计
数据一致性是确保业务操作正确执行的基础,本文将以电商系统为例,详细分析其分布式系统中的一致性问题。订单核心流程:
订单服务 -> 创建订单 -> 库存服务 -> 扣减库存 -> 积分服务 -> 增加积分 -> 仓储服务 -> 通知发货
生产中存在两种常用的解决方案:TCC和可靠消息最终一致性。前者要求强一致,后者要求最终一致。
强一致主要用于核心模块,例如交易/订单等。最终一致一般用于边缘模块例如库存,通过mq去通知,保证最终一致性,也可以业务解耦。
TCC:
订单服务、库存服务、积分服务 -> 绑定为一个TCC事务;
撤销订单时,回滚扣减库存和增加积分。
可靠消息最终一致性:
可以去发送一个请求给消息中间件,由中间件保证一定会把消息交给下游的库存服务去扣减库存,仓储服务去通知发货等;
如果这个过程中有消息发送失败,则可靠消息中间件应该保证不停的重试投递消息。
本文重点分析如何利用RocketMQ的事务消息实现最终一致性,TCC事务将在另外一篇文章分享。
RocketMQ的事务消息有两个核心概念(流程):
暂时不能被 Consumer消费的消息。Producer已经把消息发送到 Broker端,但是此消息的状态被标记为不能投递,处于这种状态下的消息称为半消息。事实上,该状态下的消息会被放在一个叫做 RMQ_SYS_TRANS_HALF_TOPIC的主题下。
当 Producer端对它二次确认后,也就是 Commit之后,Consumer端才可以消费到;那么如果是Rollback,该消息则会被删除,永远不会被消费到。
可能会因为网络原因、应用问题等,导致Producer端一直没有对这个半消息进行确认,那么这时候 Broker服务器会定时扫描这些半消息,主动找Producer端查询该消息的状态。
简而言之,RocketMQ事务消息的实现原理就是基于两阶段提交和事务状态回查,来决定消息最终是提交还是回滚的。
结合整个订单接口服务,分为两个支付链路,一个是核心链路(订单业务),一个是非核心链路(wms) 整个流程。
先向RocketMQ发送half msg,然后调用核心链路。核心链路要是返回失败,就会走失败的逻辑:退款,更改订单状态为取消,再给rocketmq发送callback废弃掉刚才的消息。
如果成功,就commit msg让消费者可以消费。如果在等待期间,一直没有callback/commit那么mq就会走回调查询具体的状态。
消费者接收到消息后,消费完成就回复mq一个ack, 如果消费失败了,mq就会重新投递或者换一个服务投递。使用rocketmq的half msg机制,可以实现这一套固定模式的最终一致性。
【核心链路-订单、库存、积分】
springboot下,RocketMQ的集成还是很简单的,引入
rocketmq-spring-boot-starter依赖、添加相关配置后,即可利用RocketMQTemplate的sendMessageInTransaction方法发送消息:
/**
* 发送事务消息
*
* @param topic topic
* @param message 消息对象
*/
public void sendMessageInTransaction(String topic, Object message) {
String transactionId = UUID.randomUUID().toString();
TransactionSendResult result = this.rocketMQTemplate.sendMessageInTransaction(topic, MessageBuilder.withPayload(message)
.setHeader(RocketMQHeaders.TRANSACTION_ID, transactionId)
.build(), message);
}
TOP