开启了分布式事务后,下一步就需要执行目标方法了。
1、执行目标方法
1.1 seata代理数据源
seata要实现分布式事务,前提之一就是我们要把数据源交给seata进行管理。
在上篇seata AT模式源码(一)中,提到的seata整合springboot部分,seata向容器中注入了SeataAutoDataSourceProxyCreator,完成了对数据源的代理。
SeataAutoDataSourceProxyCreator是一个BeanPostProcessor类型的bean,继承了AbstractAutoProxyCreator类,重写了shouldSkip方法(表示哪些对象要进行代理)和getAdvicesAndAdvisorsForBean方法(表示代理逻辑,即要添加到拦截器链里的拦截器)
public class SeataAutoDataSourceProxyCreator extends AbstractAutoProxyCreator {
private static final Logger LOGGER = LoggerFactory.getLogger(SeataAutoDataSourceProxyCreator.class);
private final String[] excludes;
/**
* 代表代理逻辑的拦截器
*/
private final Advisor advisor = new DefaultIntroductionAdvisor(new SeataAutoDataSourceProxyAdvice());
public SeataAutoDataSourceProxyCreator(boolean useJdkProxy, String[] excludes) {
this.excludes = excludes;
setProxyTargetClass(!useJdkProxy);
}
//需要添加到拦截器链里的拦截器
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource customTargetSource) throws BeansException {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Auto proxy of [{}]", beanName);
}
return new Object[]{advisor};
}
/**
* 哪些bean需要跳过(不进行代理)
* @param beanClass
* @param beanName
* @return
*/
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
//DataSource类型的bean不跳过(即需要代理)
return SeataProxy.class.isAssignableFrom(beanClass) ||
!DataSource.class.isAssignableFrom(beanClass) ||
Arrays.asList(excludes).contains(beanClass.getName());
}
}
可以看到,SeataAutoDataSourceProxyCreator代理DataSource类型的bean,代理逻辑是SeataAutoDataSourceProxyCreator这个拦截器。
public class SeataAutoDataSourceProxyAdvice implements MethodInterceptor, IntroductionInfo {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
/**
* 将dataSource和DataSourceProxy的映射关系放到map中
* 即一个DataSourceProxy对应一个DataSource
* DataSourceProxy创建时,会初始化一个定时任务,定数刷新数据表的元数据信息(感兴趣的可以尝试看看,这里就不进行展开)
*/
DataSourceProxy dataSourceProxy = DataSourceProxyHolder.get().putDataSource((DataSource) invocation.getThis());
Method method = invocation.getMethod();
Object[] args = invocation.getArguments();
Method m = BeanUtils.findDeclaredMethod(DataSourceProxy.class, method.getName(), method.getParameterTypes());
if (null != m) {
//执行目标方法时 实际执行的是代理对象的方法
//代理对象也实现了DataSource接口
return m.invoke(dataSourceProxy, args);
} else {
return invocation.proceed();
}
}
@Override
public Class<?>[] getInterfaces() {
return new Class[]{SeataProxy.class};
}
}
注:只有目标方法有对数据库的操作时,才会进入到数据源的代理逻辑;正常情况下,开启了分布式事务,就肯定会有对数据库的操作。
2 数据源代理逻辑
回想一下,jdbc对数据库的操作都是通过Statement来进行的,我们更熟悉的PreparedStatement也是Statement的子接口,而无论是Statement,还是PreparedStatement,都是通过Connection创建的。seata同时也提供了对Connection的代理--ConnectionProxy,同理,对PreparedStatement的代理--PreparedStatementProxy。
public class DataSourceProxy extends AbstractDataSourceProxy implements Resource {
/**
* 目标数据源--被代理的数据源
*/
protected DataSource targetDataSource;
/**
* seata对Connection对象做了包装 持久层获取到的连接实际上是ConnectionProxy
*
* 在ConnectionProxy的父类AbstractConnectionProxyseata中
* 对PreparedStatement做了包装 持久层获取到的实际上是PreparedStatementProxy
* @return
* @throws SQLException
*/
@Override
public ConnectionProxy getConnection() throws SQLException {
Connection targetConnection = targetDataSource.getConnection();
return new ConnectionProxy(this, targetConnection);
}
}
在ConnectionProxy的父类AbstractConnectionProxy中,提供了创建StatementProxy和PreparedStatementProxy对象的方法。
public abstract class AbstractConnectionProxy implements Connection {
public Statement createStatement() throws SQLException {
Statement targetStatement = getTargetConnection().createStatement();
return new StatementProxy(this, targetStatement);
}
/**
* seata对PreparedStatement做了包装 持久层获取到的实际上是PreparedStatementProxy
* @param sql
* @return
* @throws SQLException
*/
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
String dbType = getDbType();
// support oracle 10.2+
PreparedStatement targetPreparedStatement = null;
if (RootContext.inGlobalTransaction()) {
List<SQLRecognizer> sqlRecognizers = SQLVisitorFactory.get(sql, dbType);
if (sqlRecognizers != null && sqlRecognizers.size() == 1) {
SQLRecognizer sqlRecognizer = sqlRecognizers.get(0);
if (sqlRecognizer != null && sqlRecognizer.getSQLType() == SQLType.INSERT) {
String tableName = ColumnUtils.delEscape(sqlRecognizer.getTableName(), dbType);
TableMeta tableMeta = TableMetaCacheFactory.getTableMetaCache(dbType).getTableMeta(getTargetConnection(),
tableName, getDataSourceProxy().getResourceId());
targetPreparedStatement = getTargetConnection().prepareStatement(sql, new String[]{tableMeta.getPkName()});
}
}
}
if (targetPreparedStatement == null) {
targetPreparedStatement = getTargetConnection().prepareStatement(sql);
}
return new PreparedStatementProxy(this, targetPreparedStatement, sql);
}
}
接下来就看PreparedStatementProxy的代理逻辑。
public class PreparedStatementProxy extends AbstractPreparedStatementProxy
implements PreparedStatement, ParametersHolder {
/**
* Seata对PreparedStatement对象做了包装 所以在执行的时候将会调用到PreparedStatementProxy#execute()
* @return
* @throws SQLException
*/
@Override
public boolean execute() throws SQLException {
//直接调用执行模板
return ExecuteTemplate.execute(this, (statement, args) -> statement.execute());
}
}
ExecuteTemplate.execute
public class ExecuteTemplate {
//PreparedStatementProxy.execute调用的是本方法
public static <T, S extends Statement> T execute(StatementProxy<S> statementProxy,
StatementCallback<T, S> statementCallback,
Object... args) throws SQLException {
return execute(null, statementProxy, statementCallback, args);
}
public static <T, S extends Statement> T execute(List<SQLRecognizer> sqlRecognizers,
StatementProxy<S> statementProxy,
StatementCallback<T, S> statementCallback,
Object... args) throws SQLException {
/**
* RootContext.inGlobalTransaction() 判断此次操作是否是分布式事务 通过xid
* RootContext.requireGlobalLock() 判断是否加了GlobalLock注解
*/
if (!RootContext.inGlobalTransaction() && !RootContext.requireGlobalLock()) {
// Just work as original statement
return statementCallback.execute(statementProxy.getTargetStatement(), args);
}
/**
* 创建sql识别器 就是拿到现在需要执行的sql语句 判断是什么类型的sql
*/
if (sqlRecognizers == null) {
sqlRecognizers = SQLVisitorFactory.get(
statementProxy.getTargetSQL(),
statementProxy.getConnectionProxy().getDbType());
}
Executor<T> executor;
//如果识别器没创建出来 就使用原生的执行器
if (CollectionUtils.isEmpty(sqlRecognizers)) {
executor = new PlainExecutor<>(statementProxy, statementCallback);
} else {
if (sqlRecognizers.size() == 1) {
SQLRecognizer sqlRecognizer = sqlRecognizers.get(0);
/**
* 在这里它会先 根据sql的类型生成不同的执行器 比如是一个insert 语句 那么就是InsertExecutor
*/
switch (sqlRecognizer.getSQLType()) {
case INSERT:
executor = new InsertExecutor<>(statementProxy, statementCallback, sqlRecognizer);
break;
case UPDATE:
executor = new UpdateExecutor<>(statementProxy, statementCallback, sqlRecognizer);
break;
case DELETE:
executor = new DeleteExecutor<>(statementProxy, statementCallback, sqlRecognizer);
break;
case SELECT_FOR_UPDATE:
executor = new SelectForUpdateExecutor<>(statementProxy, statementCallback, sqlRecognizer);
break;
default:
executor = new PlainExecutor<>(statementProxy, statementCallback);
break;
}
} else {
executor = new MultiExecutor<>(statementProxy, statementCallback, sqlRecognizers);
}
}
T rs;
try {
/**
* select语句并且没有 for update : PlainExecutor#execute()
* insert update delete 、select for update : BaseTransactionalExecutor#execute()
*
*/
rs = executor.execute(args);
} catch (Throwable ex) {
if (!(ex instanceof SQLException)) {
// Turn other exception into SQLException
ex = new SQLException(ex);
}
throw (SQLException) ex;
}
return rs;
}
}
2.1 处理加独占锁的sql
insert、update、delete、select for upadte -> BaseTransactionalExecutor#execute
public abstract class BaseTransactionalExecutor<T, S extends Statement> implements Executor<T> {
public T execute(Object... args) throws Throwable {
//保存xid
if (RootContext.inGlobalTransaction()) {
String xid = RootContext.getXID();
statementProxy.getConnectionProxy().bind(xid);
}
//RootContext.requireGlobalLock()第一次为false
statementProxy.getConnectionProxy().setGlobalLockRequire(RootContext.requireGlobalLock());
//增删改 ==> AbstractDMLBaseExecutor.doExecute
//select for update => SelectForUpdateExecutor.doExecute
return doExecute(args);
}
}
2.1.1 更新操作
insert、update、delete -> AbstractDMLBaseExecutor#doExecute
public abstract class AbstractDMLBaseExecutor<T, S extends Statement> extends BaseTransactionalExecutor<T, S> {
public T doExecute(Object... args) throws Throwable {
AbstractConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
//默认为自动提交
if (connectionProxy.getAutoCommit()) {
return executeAutoCommitTrue(args);
} else {
return executeAutoCommitFalse(args);
}
}
protected T executeAutoCommitTrue(Object[] args) throws Throwable {
ConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
try {
//设置为手动提交
connectionProxy.setAutoCommit(false);
/**
* execute():lambda表达式里的内容实在execute方法里面调用的
*/
return new LockRetryPolicy(connectionProxy).execute(() -> {
/**
* 执行前置快照
* 执行真正的业务逻辑
* 执行后置快照
* 准备undolog对象
*/
T result = executeAutoCommitFalse(args);
/**
* 提交
* 1、注册分支事务
* 2、业务sql和undolog一起提交
*/
connectionProxy.commit();
return result;
});
} catch (Exception e) {
// when exception occur in finally,this exception will lost, so just print it here
LOGGER.error("execute executeAutoCommitTrue error:{}", e.getMessage(), e);
if (!LockRetryPolicy.isLockRetryPolicyBranchRollbackOnConflict()) {
connectionProxy.getTargetConnection().rollback();
}
throw e;
} finally {
connectionProxy.getContext().reset();
connectionProxy.setAutoCommit(true);
}
}
protected T executeAutoCommitFalse(Object[] args) throws Exception {
/**
* insert没有前置镜像
* uodate有前置镜像
* delete有前置镜像
*/
//执行前置镜像 前图像表记录
TableRecords beforeImage = beforeImage();
//执行自己的业务逻辑
T result = statementCallback.execute(statementProxy.getTargetStatement(), args);
/**
* insert有后置镜像
* delete没有后置镜像
* update有后置镜像
*/
//执行后置镜像 后图像表记录
TableRecords afterImage = afterImage(beforeImage);
//使用前置镜像和后置镜像生成Undolog对象
prepareUndoLog(beforeImage, afterImage);
//还没有提交
return result;
}
}
AbstractDMLBaseExecutor.LockRetryPolicy#execute
private static class LockRetryPolicy extends ConnectionProxy.LockRetryPolicy {
public <T> T execute(Callable<T> callable) throws Exception {
//读取配置文件中的配置 默认为true
if (LOCK_RETRY_POLICY_BRANCH_ROLLBACK_ON_CONFLICT) {
//调用父类的方法
return doRetryOnLockConflict(callable);
} else {
//直接调用callable
return callable.call();
}
}
}
ConnectionProxy.LockRetryPolicy
public static class LockRetryPolicy {
protected static final boolean LOCK_RETRY_POLICY_BRANCH_ROLLBACK_ON_CONFLICT = ConfigurationFactory
.getInstance().getBoolean(ConfigurationKeys.CLIENT_LOCK_RETRY_POLICY_BRANCH_ROLLBACK_ON_CONFLICT, DEFAULT_CLIENT_LOCK_RETRY_POLICY_BRANCH_ROLLBACK_ON_CONFLICT);
//todo:子类重写了该方法,比较重写的两个方法的逻辑
public <T> T execute(Callable<T> callable) throws Exception {
if (LOCK_RETRY_POLICY_BRANCH_ROLLBACK_ON_CONFLICT) {
//执行lambda
return callable.call();
} else {
return doRetryOnLockConflict(callable);
}
}
protected <T> T doRetryOnLockConflict(Callable<T> callable) throws Exception {
LockRetryController lockRetryController = new LockRetryController();
while (true) {
try {
//实际调用的是那个lambda表达式
return callable.call();
} catch (LockConflictException lockConflict) {
/**
* 捕获全局锁冲突异常
*/
onException(lockConflict);
/**
* 重试机制 每次休眠m ms 休眠 n次后抛异常
*/
lockRetryController.sleep(lockConflict);
} catch (Exception e) {
onException(e);
throw e;
}
}
}
}
callable.call()实际调用的是AbstractDMLBaseExecutor#executeAutoCommitTrue方法中定义的lambda表达式
/**
* 执行前置快照
* 执行真正的业务逻辑
* 执行后置快照
* 准备undolog对象
*/
T result = executeAutoCommitFalse(args);
/**
* 提交
* 1、注册分支事务
* 2、业务sql和undolog一起提交
*/
connectionProxy.commit();
return result;
public abstract class AbstractDMLBaseExecutor<T, S extends Statement> extends BaseTransactionalExecutor<T, S> {
protected T executeAutoCommitFalse(Object[] args) throws Exception {
/**
* insert没有前置镜像 -> InsertExecutor#beforeImage()
* uodate有前置镜像 -> UpdateExecutor#beforeImage()
* delete有前置镜像 -> DeleteExecutor#beforeImage()
*/
//执行前置镜像 前图像表记录
TableRecords beforeImage = beforeImage();
//执行自己的业务逻辑
T result = statementCallback.execute(statementProxy.getTargetStatement(), args);
/**
* insert有后置镜像 -> InsertExecutor#afterImage()
* update有后置镜像 -> UpdateExecutor#afterImage()
* delete没有后置镜像 -> DeleteExecutor#afterImage()
*/
//执行后置镜像 后图像表记录
TableRecords afterImage = afterImage(beforeImage);
//使用前置镜像和后置镜像生成Undolog对象
prepareUndoLog(beforeImage, afterImage);
//还没有提交
return result;
}
protected void prepareUndoLog(TableRecords beforeImage, TableRecords afterImage) throws SQLException {
if (beforeImage.getRows().isEmpty() && afterImage.getRows().isEmpty()) {
return;
}
ConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
TableRecords lockKeyRecords = sqlRecognizer.getSQLType() == SQLType.DELETE ? beforeImage : afterImage;
//构建lockkey
String lockKeys = buildLockKey(lockKeyRecords);
connectionProxy.appendLockKey(lockKeys);
//使用前置镜像和后置镜像构建undolog
SQLUndoLog sqlUndoLog = buildUndoItem(beforeImage, afterImage);
//将undolog暂存
connectionProxy.appendUndoLog(sqlUndoLog);
}
}
ConnectionProxy#commit
public class ConnectionProxy extends AbstractConnectionProxy {
private final static LockRetryPolicy LOCK_RETRY_POLICY = new LockRetryPolicy();
public void commit() throws SQLException {
try {
//ConnectionProxy.LockRetryPolicy.execute
LOCK_RETRY_POLICY.execute(() -> {
//提交
doCommit();
return null;
});
} catch (SQLException e) {
throw e;
} catch (Exception e) {
throw new SQLException(e);
}
}
private void doCommit() throws SQLException {
if (context.inGlobalTransaction()) {
/**
* GlobalTransaction
*/
processGlobalTransactionCommit();
} else if (context.isGlobalLockRequire()) {
/**
* GlobalLock
*/
processLocalCommitWithGlobalLocks();
} else {
targetConnection.commit();
}
}
private void processGlobalTransactionCommit() throws SQLException {
try {
/**
* 注册分支事务
*/
register();
} catch (TransactionException e) {
/**
* 全局锁冲突异常 如果抛出的是SqlException 最终会进行全局回滚(TransactionalTemplate#execute)
*/
recognizeLockKeyConflictException(e, context.buildLockKeys());
}
try {
/**
* 向undolog表插入数据
*/
UndoLogManagerFactory.getUndoLogManager(this.getDbType()).flushUndoLogs(this);
/**
* 本地数据和undolog表插入的数据一起提交
*/
targetConnection.commit();
} catch (Throwable ex) {
LOGGER.error("process connectionProxy commit error: {}", ex.getMessage(), ex);
/**
* 向seata服务端报告一阶段情况
* 如果本地事务提交失败告诉seata server
*/
report(false);
throw new SQLException(ex);
}
if (IS_REPORT_SUCCESS_ENABLE) {
//向seata服务端报告一阶段情况
report(true);
}
context.reset();
}
//注册分支事务
private void register() throws TransactionException {
if (!context.hasUndoLog() || context.getLockKeysBuffer().isEmpty()) {
return;
}
//向seata服务端注册分支事务 将分支事务要锁定的资源传给seata-server,由seata-server判断锁冲突
Long branchId = DefaultResourceManager.get().branchRegister(BranchType.AT, getDataSourceProxy().getResourceId(),
null, context.getXid(), null, context.buildLockKeys());
context.setBranchId(branchId);
}
//全局锁冲突异常判断
private void recognizeLockKeyConflictException(TransactionException te, String lockKeys) throws SQLException {
//如果是全局锁冲突异常
if (te.getCode() == TransactionExceptionCode.LockKeyConflict) {
StringBuilder reasonBuilder = new StringBuilder("get global lock fail, xid:");
reasonBuilder.append(context.getXid());
if (StringUtils.isNotBlank(lockKeys)) {
reasonBuilder.append(", lockKeys:").append(lockKeys);
}
throw new LockConflictException(reasonBuilder.toString());
} else {
throw new SQLException(te);
}
}
//报告一阶段情况
private void report(boolean commitDone) throws SQLException {
if (context.getBranchId() == null) {
return;
}
int retry = REPORT_RETRY_COUNT;
//重试机制
while (retry > 0) {
try {
//报告一阶段提交成功 或 失败
DefaultResourceManager.get().branchReport(BranchType.AT, context.getXid(), context.getBranchId(),
commitDone ? BranchStatus.PhaseOne_Done : BranchStatus.PhaseOne_Failed, null);
return;
} catch (Throwable ex) {
LOGGER.error("Failed to report [" + context.getBranchId() + "/" + context.getXid() + "] commit done ["
+ commitDone + "] Retry Countdown: " + retry);
retry--;
if (retry == 0) {
throw new SQLException("Failed to report branch status " + commitDone, ex);
}
}
}
}
}
2.1.2 查询操作
select for upadte -> SelectForUpdateExecutor.doExecute
public class SelectForUpdateExecutor<T, S extends Statement> extends BaseTransactionalExecutor<T, S> {
public T doExecute(Object... args) throws Throwable {
Connection conn = statementProxy.getConnection();
//数据库的元数据
DatabaseMetaData dbmd = conn.getMetaData();
T rs;
Savepoint sp = null;
LockRetryController lockRetryController = new LockRetryController();
boolean originalAutoCommit = conn.getAutoCommit();
ArrayList<List<Object>> paramAppenderList = new ArrayList<>();
//构建带for update的查询主键的sql
//select id from t_order where username = 'zwj' for update
String selectPKSQL = buildSelectSQL(paramAppenderList);
try {
if (originalAutoCommit) {
/*
* In order to hold the local db lock during global lock checking
* set auto commit value to false first if original auto commit was true
*/
//设置自动提交为false
conn.setAutoCommit(false);
} else if (dbmd.supportsSavepoints()) { //判断数据库是否支持保存点
/*
* In order to release the local db lock when global lock conflict
* create a save point if original auto commit was false, then use the save point here to release db
* lock during global lock checking if necessary
*/
sp = conn.setSavepoint();
} else {
throw new SQLException("not support savepoint. please check your db version");
}
while (true) {
try {
// #870
// execute return Boolean
// executeQuery return ResultSet
//执行目标sql
rs = statementCallback.execute(statementProxy.getTargetStatement(), args);
// Try to get global lock of those rows selected
//执行查询主键的sql
TableRecords selectPKRows = buildTableRecords(getTableMeta(), selectPKSQL, paramAppenderList);
//根据查询出来的主键构建lockkey
String lockKeys = buildLockKey(selectPKRows);
if (StringUtils.isNullOrEmpty(lockKeys)) {
break;
}
if (RootContext.inGlobalTransaction()) {//判断当前方法是否加了@GlobalTransaction注解
//do as usual
/**
* 向seata 服务端发送请求 检查锁是否被占用
*/
statementProxy.getConnectionProxy().checkLock(lockKeys);
} else if (RootContext.requireGlobalLock()) {
//check lock key before commit just like DML to avoid reentrant lock problem(no xid thus can
// not reentrant)
statementProxy.getConnectionProxy().appendLockKey(lockKeys);
} else {
throw new RuntimeException("Unknown situation!");
}
break;
} catch (LockConflictException lce) {
//捕获锁冲突异常
if (sp != null) {
conn.rollback(sp);
} else {
conn.rollback();
}
//睡眠一段时间后重试
lockRetryController.sleep(lce);
}
}
} finally {
if (sp != null) {
try {
conn.releaseSavepoint(sp);
} catch (SQLException e) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("{} does not support release save point, but this is not a error.", getDbType());
}
}
}
if (originalAutoCommit) {
conn.setAutoCommit(true);
}
}
return rs;
}
//构建查询主键的sql
private String buildSelectSQL(ArrayList<List<Object>> paramAppenderList) {
SQLSelectRecognizer recognizer = (SQLSelectRecognizer)sqlRecognizer;
StringBuilder selectSQLAppender = new StringBuilder("SELECT "); //select
selectSQLAppender.append(getColumnNameInSQL(getTableMeta().getPkName())); // select id(主键)
selectSQLAppender.append(" FROM ").append(getFromTableInSQL());// select id from t_order
String whereCondition = buildWhereCondition(recognizer, paramAppenderList);
if (StringUtils.isNotBlank(whereCondition)) {
selectSQLAppender.append(" WHERE ").append(whereCondition);//select id from t_order where username = 'zwj'
}
selectSQLAppender.append(" FOR UPDATE");////select if from t_order where username = 'zwj' for update
return selectSQLAppender.toString();
}
}
2.2 处理不加锁的sql
select语句并且不加for update -> PlainExecutor#execute(
public class PlainExecutor<T, S extends Statement> implements Executor<T> {
private StatementProxy<S> statementProxy;
public T execute(Object... args) throws Throwable {
//直接指向
return statementCallback.execute(statementProxy.getTargetStatement(), args);
}
}
总结 执行目标方法的流程: 数据源代理逻辑:
1、生成前置镜像
2、执行目标sql
3、生成后置镜像
- insert:没有前置镜像,有后置镜像
- update:既有前置镜像,也有后置镜像
- delete:有前置镜像,无后置镜像
4、使用前置镜像和后置镜像生成undolog
5、注册分支事务 seata-server会进行全局锁冲突异常检查
6、将生成的undolog插入到undolog表中
7、报告一阶段情况