Day06 Druid 的事务实现

Day06 Druid 的事务实现

今天分析下 pool 下的 xa 包:package com.alibaba.druid.pool.xa;

一、XA 协议

XA 协议是由 X/Open 组织提出的分布式事务处理规范,主要定义了事务管理器 TM 和局部资源管理器 RM 之间的接口。Mysql 从 5.0 版本开始,InnoDB 存储引擎已经支持 XA 协议。

1. 两阶段提交

分布式事务的两阶段提交是把整个事务提交分为 prepare 和 commit 两个阶段:

​ 第一阶段,事务协调者向事务参与者发送 prepare 请求,事务参与者收到请求后,如果可以提交事务,回复 yes,否则回复 no。

​ 第二阶段,如果所有事务参与者都回复了 yes,事务协调者向所有事务参与者发送 commit 请求,否则发送 rollback 请求。

两阶段提交存在三个问题:

  • 同步阻塞,本地事务在 prepare 阶段锁定资源,如果有其他事务需要访问锁定的资源,就必须等待前面的事务完成。这样就造成了系统性能下降。
  • 协调节点单点故障,如果第一个阶段 prepare 成功了,但是第二个阶段协调节点发出 commit 指令之前宕机了,所有服务的数据资源处于锁定状态,事务将无限期地等待。
  • 数据不一致,如果第一阶段 prepare 成功了,但是第二阶段协调节点向某个节点发送 commit 命令时失败,就会导致数据不一致。
2. 三阶段提交

为了解决两阶段提交的问题,三阶段提交做了改进:

  • 协调节点、事务参与者都引入了超时机制
  • 第一阶段的 prepare 阶段分成了两步,canCommi 和 preCommit。

引入 preCommit 阶段后,协调节点会在 commit 之前再次检查各个事务参与者的状态,保证它们的状态是一致的。但是也存在问题,那就是如果第三阶段发出 rollback 请求,有的节点没有收到,那没有收到的节点会在超时之后进行提交,造成数据不一致。

接下来看看 Druid 中 分布式事务的实现。

二、JtdsXAResource

JtdsXAResource 类实现了 XAResource 接口, XAResource 接口位于 javax.transaction.xa 包下,提供了事务相关的功能封装:

package javax.transaction.xa;

public interface XAResource {
    int TMENDRSCAN = 8388608;
    int TMFAIL = 536870912;
    int TMJOIN = 2097152;
    int TMNOFLAGS = 0;
    int TMONEPHASE = 1073741824;
    int TMRESUME = 134217728;
    int TMSTARTRSCAN = 16777216;
    int TMSUCCESS = 67108864;
    int TMSUSPEND = 33554432;
    int XA_RDONLY = 3;
    int XA_OK = 0;

    // 提交事务
    void commit(Xid var1, boolean var2) throws XAException;
    // 结束事务
    void end(Xid var1, int var2) throws XAException;
	// 告诉资源管理器忽略一个 prepared 状态的事务分支
    void forget(Xid var1) throws XAException;
    // 获取事务超时时间
    int getTransactionTimeout() throws XAException;
	// 判断目标对象所代表的资源管理器实例是否与参数var1所代表的资源管理器实例相同
    boolean isSameRM(XAResource var1) throws XAException;
    // 设置事务超时时间
    int prepare(Xid var1) throws XAException;
	// 查看存在的 prepared 状态的xa事务
    Xid[] recover(int var1) throws XAException;
    // 事务回滚
    void rollback(Xid var1) throws XAException;
    // 设置事务超时时间
    boolean setTransactionTimeout(int var1) throws XAException;
    // 开始事务
    void start(Xid var1, int var2) throws XAException;
}

JtdsXAResource 类 使用 jTDS JDBC Drive 来实现了这些事务相关的功能。

jTDS 是一个开源的、纯 Java 的JDBC 3.0 驱动程序。

三、DruidXADataSource 类:

DruidXADataSource 类继承自 DruidDataSource,实现了 XADataSource 接口。XADataSource 类位于 javax.sql 包下,是内部使用的 XAConnection 对象的工厂。

public class DruidXADataSource extends DruidDataSource implements XADataSource {

    private final static Log  LOG = LogFactory.getLog(DruidXADataSource.class);
    private static final long serialVersionUID = 1L;
    private Object            h2Factory        = null;

    /**
     * 尝试建立可用于分布式事务的物理数据库连接
     * @return 一个 XAConnection 对象,表示与数据源的物理连接,可用于分布式事务
     */
    @Override
    public XAConnection getXAConnection() throws SQLException {
        DruidPooledConnection conn = this.getConnection();

        Connection physicalConn = conn.unwrap(Connection.class);

        // 创建用于分布式事务的物理数据库连接
        XAConnection rawXAConnection = createPhysicalXAConnection(physicalConn);

        return new DruidPooledXAConnection(conn, rawXAConnection);
    }

    protected void initCheck() throws SQLException {
        super.initCheck();

        DbType dbType = DbType.of(this.dbTypeName);
        if (JdbcUtils.H2.equals(dbType)) {
            h2Factory = H2Utils.createJdbcDataSourceFactory();
        }
    }

    private XAConnection createPhysicalXAConnection(Connection physicalConn) throws SQLException {
        DbType dbType = DbType.of(this.dbTypeName);

        if (dbType == null) {
            throw new SQLException("xa not support dbType : " + this.dbTypeName);
        }

        switch (dbType) {
            case oracle:
                try {
                    return OracleUtils.OracleXAConnection(physicalConn);
                } catch (XAException xae) {
                    LOG.error("create xaConnection error", xae);
                    return null;
                }
            case mysql:
            case mariadb:
                // 通过反射创建 MySQL XA 连接
                return MySqlUtils.createXAConnection(driver, physicalConn);
            case postgresql:
                return PGUtils.createXAConnection(physicalConn);
            case h2:
                return H2Utils.createXAConnection(h2Factory, physicalConn);
            case jtds:
                return new JtdsXAConnection(physicalConn);
            default:
                throw new SQLException("xa not support dbType : " + this.dbTypeName);

        }
    }

    /**
     * 尝试使用给定的用户名和密码建立物理数据库连接。 返回的连接是可用于分布式事务的连接
     * @param user 数据库用户名
     * @param password 数据库密码
     * @return 可用于分布式事务的连接
     */
    @Override
    public XAConnection getXAConnection(String user, String password) throws SQLException {
        throw new UnsupportedOperationException("Not supported by DruidDataSource");
    }

}

四、JtdsXAConnection

JtdsXAConnection 类实现了 XAConnection 接口来进行连接池管理。

通过 jTDS 的 XASupport 类来进行连接池管理。

五、DruidPooledXAConnection

DruidPooledXAConnection 实现了 XAConnection 接口来进行连接池管理,XAConnection 接口位于 javax.sql包下并继承自 PooledConnection,为分布式事务提供支持。 XAConnection 对象可以通过 XAResource 对象加入到分布式事务中。 事务管理器通过 XAResource 对象管理 XAConnection 对象。

PooledConnection 对象表示与数据源的物理连接。当应用程序完成连接时,连接会被回收而不是关闭,通过连接复用来减少需要建立的连接数量。

连接池管理器维护一个 PooledConnection 对象池。如果池中有可用的 PooledConnection 对象,则连接池管理器会返回一个 Connection 对象,该对象是该物理连接的句柄。如果没有可用的 PooledConnection 对象,则连接池管理器调用 ConnectionPoolDataSource 方法 getPoolConnection 来创建新的物理连接,实现 ConnectionPoolDataSource 的 JDBC 驱动程序便会创建一个新的 PooledConnection 对象并返回一个句柄。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值