简介: 从应用场景出发,给出解决方案与实现原理,并提供整套工业级实现源码。
作者:丁威
在电商系统上线初期,往往会进行一些“拉新”活动,例如活动部门提出新用户注册送积分、送优惠券活动。
基于分布式、微服务的设计理念,通常的架构设计(子系统交互)如下图所示:
其核心系统介绍如下:
上面的架构设计非常优雅,但并不是无懈可击,如果新用户注册成功,但消息发送到 MQ 失败,或者消息成功发送到 MQ,但发送完 MQ 后系统出现异常导致用户注册失败又该如何呢?
上面的问题其实就是典型的分布式事务问题:即如何保证用户注册(数据库操作)与 MQ 消息发送这两个分布式操作的一致性。
RocketMQ 事务消息闪亮登场。
一言以蔽之:RocketMQ 事务消息要解决的问题是消息发送与业务的一致性,其解决思路:二阶段提交与事务状态回查,其具体实现流程如下图所示:
其核心设计理念:
在具体实践中,消息发送者在无法获取事务状态时不要武断的返回 ROLLBACK,而是要返回 UNOWN,让服务端定时重试回查,说明如下:
在将 PREPARE 消息发送到 Broker 后,服务端发起事务查询时本地事务可能还未提交,为了避免无效的事务回查机制,RocketMQ 通常至少在收到 PREPARE 消息 6s 后才会发起第一次事务回查,可通过 transactionTimeOut 配置。故客户端在实现事务回查时无法证明事务状态时不应该返回 ROLLBACK,而是返回 UNOWN。
光说不练假把式,接下来以一个新用户注册送优惠券的场景来详细介绍如何使用事务消息。
项目模块职责说明如下:
事务消息的核心代码组装在 transaction-service,其核心类图如下:
其中核心要点如下:
温馨提示:之所以不在 UserServicveImpl 中执行本地事务,是因为 executeLocalTransaction 中抛出的异常会被 RocketMQ 框架捕捉,及异常无法被 UserServiceImpl 感知,即无法实现其事务的一致性。
接下来展示其核心代码,全部源码已上传到 github 仓库。
仓库地址:https://github.com/dingwpmz/rocketmq-learning。
UserServiceImpl 核心实现
UserServiceImpl 的核心要点如下:
UserRegTransactionListener 核心实现
事务监听器需要实现执行本地事务与事务回查两个接口。
1、实现 executeLocalTransaction
首先需要实现 executeLocalTransaction 方法,执行本地事务,其代码如下图所示:
其中几个关键点说明如下:
2、实现 checkLocalTransaction
其次需要实现事务状态回查,用来 RocketMQ 服务端感知事务是否成功,其实现原理如下图所示:
其实现关键点如下:
代码获取
上文只是将事务消息的核心代码加以解读,并重点阐述每个步骤的实现关键点,笔者基于 SpringBoot,尝试结合场景学习 RocketMQ 的使用技巧,其代码上传到了 github 仓库:https://github.com/dingwpmz/rocketmq-learning。
原文链接
本文为阿里云原创内容,未经允许不得转载。