在微服务架构中,完成某一个业务功能可能需要横跨多个服务,操作多个数据库。这就涉及到到了分布式事务,需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性。
在 Seata 的架构中,一共有三个角色:
其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。
ShardingSphere整合Seata
引入依赖
<!‐‐shardingsphere整合seata依赖‐‐>
<dependency>
<groupId>org.apache.shardingspheregroupId>
<artifactId>sharding‐transaction‐base‐seata‐atartifactId>
<version>4.1.1version>
dependency>
配置seata.conf
包含 Seata 柔性事务的应用启动时,用户配置的数据源会根据 seata.conf 的配置,适配为
Seata 事务所需的 DataSourceProxy,并且注册至 RM 中。
client {
application.id = tulingmall‐order‐curr
transaction.service.group = tuling‐order‐group
}
开启全局事务配置
核心类SeataATShardingTransactionManager
//全局事务交给SeataATShardingTransactionManager管理
@ShardingTransactionType(TransactionType.BASE)
@Transactional
public CommonResult generateOrder(OrderParam orderParam, Long memberId) {
...
}
注意:GlobalTransactional和ShardingTransactionType不能同时出现,此处不能使用@GlobalTransactional。同时需要关闭数据源自动代理
seata:
enable‐auto‐data‐source‐proxy: false #关闭数据源自动代理,交给sharding‐jdbc那边
可靠消息最终一致性方案是指当事务发起执行完成本地事务后并发出一条消息,事务参与方(消息消费者)一定能够接收消息并处理事务成功,此方案强调的是只要消息发给事务参与方最终事务要达到一致。
RocketMQ保证消息不丢
生产端:同步消息(ACK机制)
存储端:同步刷盘
消费端:手动offset
RocketMQ事务消息设计则主要是为了解决Producer端的消息发送与本地事务执行的原子性问题,RocketMQ的设计中broker与producer端的双向通信能力,使得broker天生可以作为一个事务协调者存在;而RocketMQ本身提供的存储机制为事务消息提供了持久化能力;RocketMQ的高可用机制以及可靠消息设计则为事务消息在系统发生异常时依然能够保证达成事务的最终一致性。
在RocketMQ 4.3后实现了完整的事务消息,实际上其实是对本地消息表的一个封装,将本地消息表移动到了MQ内部,解决Producer端的消息发送与本地事务执行的原子性问题。
1)Producer发送事务消息
Producer(MQ发送方)发送事务消息至MQ Server,MQ Server将消息状态标记为Prepared(预览状态),注意此时这条消息消费者(MQ订阅方)是无法消费到的。
2)MQ Server回应消息发送成功
MQ Server接收到Producer发送给的消息则回应发送成功表示MQ已接收到消息。
3)Producer执行本地事务
Producer端执行业务代码逻辑,通过本地数据库事务控制。
4)消息投递
若Producer本地事务执行成功则自动向MQ Server发送commit消息,MQ Server接收到commit消息后将消费消息,消费成功则向MQ回应ack,否则将重复接收消息。这里ack默认自动回应,即程序执行正常则自动回应ack
以上主干流程已由RocketMQ实现,只需要分别实现本地事务执行以及本地事务回查方法,因此只需关注本地事务的执行状态即可。
RocketMQ提供RocketMQLocalTransactionListener接口 :
public interface RocketMQLocalTransactionListener {
/**
* 发送prepare消息成功此方法被回调,该方法用于执行本地事务
* @param msg 回传的消息,利用transactionId即可获取到该消息的唯一Id
* @param arg 调用send方法时传递的参数,当send时候若有额外的参数可以传递到send方法中,这里能获取到
*@return 返回事务状态,COMMIT :提交 ROLLBACK :回滚 UNKNOW :回调
*/
RocketMQLocalTransactionState executeLocalTransaction(Message msg,Object arg);
/**
* @param msg 通过获取transactionId来判断这条消息的本地事务执行状态
* @return 返回事务状态,COMMIT :提交 ROLLBACK :回滚 UNKNOW :回调
*/
RocketMQLocalTransactionState checkLocalTransaction(Message msg);
}
案例
/**
* 使用事务消息机制发送订单
* @return
*/
public boolean sendCreateOrderMsg(Long orderId, Long memberId){
// SendResult result = rocketMQTemplate.syncSend(asyncOrderTopic+":"+ORDERTAG,message);
String destination = asyncOrderTopic+":"+ORDERTAG;
Message<String> message = MessageBuilder.withPayload(orderId+":"+memberId)
.build();
TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(destination,message,orderId);
return SendStatus.SEND_OK == sendResult.getSendStatus();
}
/**
* 发送延时同步库存消息,60s后同步库存
* @param productId
* @param promotionId
* @return
*/
public boolean sendStockSyncMessage(Long productId,Long promotionId){
Message message = MessageBuilder.withPayload(productId+":"+promotionId).build();
SendResult result = rocketMQTemplate.syncSend("stock-sync",message,5000,5);
return SendStatus.SEND_OK == result.getSendStatus();
}