一、概要
这篇文章重点分析RM(resource manager)的源码。
- RM 如何集成到业务进程
- RM和TC的通讯过程(xid,branch id的传递和生成过程)
二、配置
我们摘取dubbo-account-service.xml的核心配置看看
<bean name="accountDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="xxx"/>
<property name="username" value="xxx"/>
<property name="password" value="xxx"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="initialSize" value="0" />
<property name="maxActive" value="180" />
<property name="minIdle" value="0" />
<property name="maxWait" value="60000" />
<property name="validationQuery" value="Select 'x' from DUAL" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="testWhileIdle" value="true" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="25200000" />
<property name="removeAbandoned" value="true" />
<property name="removeAbandonedTimeout" value="1800" />
<property name="logAbandoned" value="true" />
<property name="filters" value="mergeStat" />
</bean>
<bean id="accountDataSourceProxy" class="com.alibaba.fescar.rm.datasource.DataSourceProxy">
<constructor-arg ref="accountDataSource" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="accountDataSourceProxy" />
</bean>
我们可以看到jdbcTemplate没有直接使用DruidDataSource作为数据源,而是使用了DataSourceProxy在外面封装了一层。我们可以猜到应该是在DataSourceProxy里针对RM的特性做了扩展。
三、源码分析
public class DataSourceProxy extends AbstractDataSourceProxy implements Resource {
@Override
public ConnectionProxy getConnection() throws SQLException {
assertManaged();
Connection targetConnection = targetDataSource.getConnection();
return new ConnectionProxy(this, targetConnection, targetDataSource.getDbType());
}
可以看到这里返回的是RM使用代理模式实现的ConnectionProxy。
我们先看事务提交的操作。
public class ConnectionProxy extends AbstractConnectionProxy {
@Override
public void commit() throws SQLException {
if (context.inGlobalTransaction()) {
try {
register();
} catch (TransactionException e) {
recognizeLockKeyConflictException(e);
}
try {
if (context.hasUndoLog()) {
UndoLogManager.flushUndoLogs(this);
}
targetConnection.commit();
} catch (Throwable ex) {
report(false);
if (ex instanceof SQLException) {
throw (SQLException) ex;
} else {
throw new SQLException(ex);
}
}
report(true);
context.reset();
} else {
targetConnection.commit();
}
}
这里有几个核心方法。
- register()
请求TC注册分支事务。在这里会生成branch id。这里会涉及到锁冲突的问题。后面再抽时间分析这里加锁的策略 - UndoLogManager.flushUndoLogs(this);
写回滚日志undo_log。注意这里的undo_log跟业务的事务是同一个事务,这也就是fescar博客提到的保证undo_log写入成功的依据。 - report(true);
请求TC更新分支事务状态。
四、问题
- 锁的作用和锁的范围?
- xid的传递?在注册分支事务的时候xid是在哪一步保存下来的?
- register和report在TC的实现是如何?
- report超时或者失败分布式事务的后续处理如何?
带着这些问题接下来会继续去看源码分析。
网友评论