redis系列整体栏目
内容 | 链接地址 |
---|---|
【一】分布式事务之2pc两阶段提交 | https://zhenghuisheng.blog.csdn.net/article/details/142406325 |
【二】分布式事务seata的安装下载与环境搭建 | https://zhenghuisheng.blog.csdn.net/article/details/142893117 |
【三】分布式事务seata的AT模式 | https://zhenghuisheng.blog.csdn.net/article/details/142977918 |
【四】分布式事务seata的XA模式 | https://zhenghuisheng.blog.csdn.net/article/details/143059012 |
分布式事务seata的XA模式
- 一,分布式事务seata的XA模式
- 1,XA模式的基本使用
- 2,XA模式的底层实现
- 3,XA模式源码实现
- 4,XA总结
一,分布式事务seata的XA模式
在前面讲解了seata的安装以及核心的AT模式,AT模式相对来说比较主流,是阿里的首推模式,与之比较相像的模式就是本文的核心内容XA模式,本文主要是作为了解,因为XA模式能实现的,AT模式都能实现
XA模式的底层原理也比较简单,就是利用原生的spring事务做底层支持,通过对spring的事务封装来完成XA事务的支持,因此对于XA模式,不仅仅是支持关系型数据库,还得支持spring原生事务的支持
在了解XA模式的事务之前,可以直接查看官网:https://seata.apache.org/zh-cn/docs/dev/mode/xa-mode
1,XA模式的基本使用
在使用XA模式之前,需要启动nacos服务、TC服务,将这些服务成功启动
依旧是选用之前的三个服务,分别是订单服务,库存服务和账户服务,订单事务作为全局事务的发起者
在order订单服务配置seata的注册中心和配置中心配置如下,由于seata的默认mode模式为AT模式,因此如果想要使用XA模式的话,则需要手动的设置数据源的代理模式为XA模式
seata:
#设置数据源的代理模式为XA,默认是AT
data-source-proxy-mode: XA
tx-service-group: default_tx_group
registry:
# 指定nacos作为注册中心
type: nacos
nacos:
application: seata-server
server-addr: localhost:8848
group: SEATA_GROUP
config:
# 指定nacos作为配置中心
type: nacos
nacos:
server-addr: localhost:8848
namespace: 7e838c12-8554-4231-82d5-6d93573ddf32
group: SEATA_GROUP
data-id: seataServer.properties
订单服务的代码如下,其内部实现和AT模式的代码一样,但是需要多加一个 Transactional 开启本地事务的注解
@Override
@Transactional
@GlobalTransactional(name="createOrder",rollbackFor=Exception.class)
public Order saveOrder(OrderVo orderVo) {
//下单
Order order = new Order();
//创建订单 xxx
Integer saveOrderRecord = orderMapper.insert(order);
//扣减库存
storageFeignService.deduct(orderVo.getCommodityCode(), orderVo.getCount());
//扣减余额
Boolean debit= accountFeignService.debit(orderVo.getUserId(), orderVo.getMoney());
//更新订单
Integer updateOrderRecord = orderMapper.updateOrderStatus(order.getId(),OrderStatus.SUCCESS.getValue());
return order;
}
与AT模式不一致的是,XA模式不需要像AT模式一样需要通过前后置镜像结合undolog使用,因此在建表也不需要手动的建undolog日志表
2,XA模式的底层实现
xa模式的底层设计思想就是利用了2pc两阶段提交,先实现与提交操作,然后收集预提交的结果,根据全部的结果进行事务的提交或者回滚
//订单创建和更新库存预提交
int orderStatus = order.prepare();
int stockStatus = stock.prepare();
//判断二者之间的状态是否都为1
if(orderStatus && stockStatus){
//同时提交
order.commit();
stock.commit();
}else{
//同时回滚
order.rollback();
stock.rollback();
}
可以直接看官网给的架构流程图,在TM和RM之间定义了XA规范,通过RPC通信实现TM和RM之间的通信。在每一个RM中,先时开始XA和结束XA,然后执行XA的预提交操作,最后通过TC的反馈进行最终的提交或者回滚。在开启全局事务事务之后,也会注册一个全局的XID来保证事务的唯一性
通过下图的流程图可以知道,其具体的实现流程如下:
- 首先是TM开启一个全局事务,然后与TC建立连接,TC返回一个xid给TM
- 随后分支事务执行XA协议及预编译操作,分支事务直接使用spring的原生事务作为支持
- 随后RM的本地事务会向TC注册一个本地分支,并携带TC的xid保证事务的一致性
- 随后分支事务向TC上报预编译的状态,TC收集到全部状态之后再决定全局事务提交或者回滚
- TC通过netty的方式向各个RM实现通信,只要有一个本地事务提交失败则全部失败回滚,只有全部成功才能通知全部的本地事务提交
在XA内部中,得直接使用spring原生的事务,内部可以直接使用 XADataSource 数据源,而不像AT模式一样,可以通过代理方式自定义数据源,但是AT模式需要手动的记录前置镜像和后置镜像以及undolog的日志,但是在XA模式中不需要,因此XA模式不仅仅要是关系型数据库,还得需要满足使用与原生的XA事务,即原生的spring事务
3,XA模式源码实现
首先可以直接查看XA的代理模式 PreparedStatementProxyXA 类,然后直接查看里面的执行更新的方法
public int executeUpdate() throws SQLException {
return (Integer)ExecuteTemplateXA.execute(this.connectionProxyXA, (statement, args) -> {
return statement.executeUpdate();
}, this.getTargetStatement(), new Object[0]);
}
在这个execute执行方法时,方法内部主要有两个核心代码,一个是执行回滚的代码,另一个是执行提交的代码
res = statementCallback.execute(targetStatement, args);
connectionProxyXA.rollback();
connectionProxyXA.commit();
在提交事务中,用一个一把 synchronized 锁,也就是说RM的本地事务中,如果上一个事务没有提交或者回滚,那么下一个线程则会一直处于阻塞等待状态,在该方法内部中,当超时了则会直接抛出异常
public synchronized void commit() throws SQLException {
if (!this.currentAutoCommitStatus) {
if (this.xaActive && this.xaBranchXid != null) {
try {
this.end(67108864);
long now = System.currentTimeMillis();
this.checkTimeout(now);
this.setPrepareTime(now);
this.xaResource.prepare(this.xaBranchXid);
} catch (XAException var8) {
} finally {
this.cleanXABranchContext();
}
}
}
}
在提交事务中,主要是通过最下这段代码实现提交,需要携带本地事务的分支id
this.xaResource.prepare(this.xaBranchXid);
在回滚事务中的详细代码如下,需要通过携带全局事务XID和对应的本地事务的分支id
public void rollback() throws SQLException {
if (!this.currentAutoCommitStatus) {
if (this.xaActive && this.xaBranchXid != null) {
try {
if (!this.rollBacked) {
this.xaResource.end(this.xaBranchXid, 536870912);
this.xaRollback(this.xaBranchXid);
}
DefaultResourceManager.get().branchReport(BranchType.XA, this.xid, this.xaBranchXid.getBranchId(), BranchStatus.PhaseOne_Failed, (String)null);
} finally {
this.cleanXABranchContext();
}
}
}
}
在ResourceManagerXA 类中,内部定义了分支事务的提交回滚的详细细节
4,XA总结
xa模式的变成模型和At的模式编程模型一直,at模式通过快照结合undolog来实现一致性,xa模式通过定义xa规范来实现一致性,但是xa在提交时使用了jvm进程锁,效率不如at模式,因此在技术选型上,除非只能是用原生的xa规范,否则可以优先使用at模式实现分布式事务,因为xa模式能实现的,at模式都能实现,并且在效率方面,at模式的效率也是高于xa模式