6 张图带你彻底搞懂分布式事务 XA 模式,华为2021深圳春招面试

  • 第二阶段执行 XA commit/rollback。

mysql 目前是支持 seata xa 模式的两阶段优化的。

「但是这个优化对 oracle 不支持,因为 oracle 实现的是标准的 xa 协议,即 xa end 后,协调节点向事务参与者统一发送 prepare,最后再发送 commit/rollback。这也导致了 seata 的 xa 模式对 oracle 支持不太好。」

[](

)seata XA 源码

================================================================================

seata 中的 XA 模式是使用数据源代理来实现的,需要手动配置数据源代理,代码如下:


@Bean

@ConfigurationProperties(prefix = "spring.datasource")

public DruidDataSource druidDataSource() {

    return new DruidDataSource();

}



@Bean("dataSourceProxy")

public DataSource dataSource(DruidDataSource druidDataSource) {

    return new DataSourceProxyXA(druidDataSource);

}



  • 也可以根据普通 DataSource 来创建 XAConnection,但是这种方式有兼容性问题(比如 oracle),所以 seata 使用了开发者自己配置 XADataSource。

  • seata 提供的 XA 数据源代理,要求代码框架中必须使用 druid 连接池。

[](

)1. XA 第一阶段


当 RM 收到 DML 请求后,seata 会使用 ExecuteTemplateXA来执行,执行方法 execute 中有一个地方很关键,就是把 autocommit 属性改为了 false,而 mysql 默认 autocommit 是 true。事务提交之后,还要把 autocommit 改回默认。

下面我们看一下 XA 第一阶段提交的主要代码。

[](

)1)开启 XA

上面代码标注[1]处,调用了 ConnectionProxyXA 类的 setAutoCommit 方法,这个方法的源代码中,XA start 主要做了三件事:

  • 向 TC 注册分支事务

  • 调用数据源的 XA Start


xaResource.start(this.xaBranchXid, XAResource.TMNOFLAGS);



  • 把 xaActive 设置为 true

RM 并没有直接使用 TC 返回的 branchId 作为 xa 数据源的 branchId,而是使用全局事务 id(xid) 和 branchId 重新构建了一个。

[](

)2)执行 sql

调用 PreparedStatementProxyXA 的 execute 执行 sql。

[](

)3)XA end/prepare


public void commit() throws SQLException {

    //省略部分源代码

    try {

        // XA End: Success

        xaResource.end(xaBranchXid, XAResource.TMSUCCESS);

        // XA Prepare

        xaResource.prepare(xaBranchXid);

        // Keep the Connection if necessary

        keepIfNecessary();

    } catch (XAException xe) {

        try {

            // Branch Report to TC: Failed

            DefaultResourceManager.get().branchReport(BranchType.XA, xid, xaBranchXid.getBranchId(),

                BranchStatus.PhaseOne_Failed, null);

        } catch (TransactionException te) {

            //这儿只打印了一个warn级别的日志

        }

        throw new SQLException(

            "Failed to end(TMSUCCESS)/prepare xa branch on " + xid + "-" + xaBranchXid.getBranchId() + " since " + xe

                .getMessage(), xe);

    } finally {

        cleanXABranchContext();

    }

}



从这个源码我们看到,commit 主要做了三件事:

  • 调用数据源的 XA end

  • 调用数据源的 XA prepare

  • 向 TC 报告分支事务状态

到这里我们就可以看到,seata 把 xa 协议的前两个阶段合成了一个阶段。

[](

)2. XA commit


这里的调用关系用一个时序图来表示:

5.png

看一下 RmBranchCommitProcessor 类的 process 方法,代码如下:


@Override

public void process(ChannelHandlerContext ctx, RpcMessage rpcMessage) throws Exception {

    String remoteAddress = NetUtil.toStringAddress(ctx.channel().remoteAddress());

    Object msg = rpcMessage.getBody();

    if (LOGGER.isInfoEnabled()) {

        LOGGER.info("rm client handle branch commit process:" + msg);

    }

    handleBranchCommit(rpcMessage, remoteAddress, (BranchCommitRequest) msg);

}



从调用关系时序图可以看出,上面的 handleBranchCommit 方法最终调用了 AbstractRMHandler 的 handle 方法,最后通过 branchCommit 方法调用了 ResourceManagerXA 类的 finishBranch 方法。

ResourceManagerXA 类是 XA 模式的资源管理器,看下面这个类图,也就是 seata 中资源管理器(RM)的 UML 类图:

6.png

上面的 finishBranch 方法调用了 connectionProxyXA.xaCommit 方法,我们最后看一下 xaCommit 方法:


public void xaCommit(String xid, long branchId, String applicationData) throws XAException {

    XAXid xaXid = XAXidBuilder.build(xid, branchId);

 //因为使用mysql,这里xaResource是MysqlXAConnection

    xaResource.commit(xaXid, false);

    releaseIfNecessary();

}



最后

针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。

image

上述的面试题答案都整理成文档笔记。 也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)

**[CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】](

)**

image

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。

[外链图片转存中…(img-Mt1zIIFA-1631154743346)]

上述的面试题答案都整理成文档笔记。 也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)

**[CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】](

)**

[外链图片转存中…(img-Ig8zYm6f-1631154743348)]

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值