由于seata需要兼容现有常用的ORM框架,所以直接使用DataSourceProxy实现DataSource接口,在项目中作为数据源对象。
@Primary
@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
//持有了目标数据源对象,该DataSourceProxy构造函数,会把RootContext的DEFAULT_BRANCH_TYPE设置为BranchType.AT
return new DataSourceProxy(dataSource);
}
先看到dataSourceProxy的重写的getConnection方法
@Override
public ConnectionProxy getConnection() throws SQLException {
//获取targetConnection(目标数据源对象)
Connection targetConnection = targetDataSource.getConnection();
//返回的是ConnectionProxy对象,持有了targetConnection
return new ConnectionProxy(this, targetConnection);
}
再看到ConnectionProxy实现了Connection接口重写的prepareStatement方法
public PreparedStatement prepareStatement(String sql) throws SQLException {
String dbType = getDbType();
// support oracle 10.2+
PreparedStatement targetPreparedStatement = null;
//AT模式
if (BranchType.AT == RootContext.getBranchType()) {
//封裝sql的为SQLRecognizer对象
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) {
//获取表的信息
TableMeta tableMeta = TableMetaCacheFactory.getTableMetaCache(dbType).getTableMeta(getTargetConnection(),
sqlRecognizer.getTableName(), getDataSourceProxy().getResourceId());
String[] pkNameArray = new String[tableMeta.getPrimaryKeyOnlyName().size()];
//获取的到主键的列名填充到pkNameArray中
tableMeta.getPrimaryKeyOnlyName().toArray(pkNameArray);
//使用targetConnection调用prepareStatement
targetPreparedStatement = getTargetConnection().prepareStatement(sql,pkNameArray);
}
}
}
//不是插入的情况
if (targetPreparedStatement == null) {
targetPreparedStatement = getTargetConnection().prepareStatement(sql);
}
//返回了PreparedStatementProxy,持有了targetPreparedStatement
return new PreparedStatementProxy(this, targetPreparedStatement, sql);
}
再看到PreparedStatementProxy实现了PreparedStatement接口重写的execute方法
public boolean execute() throws SQLException {
//(statement, args) -> statement.execute(),传入了目标方法的调用
return ExecuteTemplate.execute(this, (statement, args) -> statement.execute());
}
public static <T, S extends Statement> T execute(List<SQLRecognizer> sqlRecognizers,
StatementProxy<S> statementProxy,
StatementCallback<T, S> statementCallback,
Object... args) throws SQLException {
//如果RootContext的CONTEXT_HOLDER(threadlocal)的TX_LOCK为空(@GlobalLock注解)且
//如果没有XID,或者有XID但不是AT模式,直接调用目标方法
if (!RootContext.requireGlobalLock() && BranchType.AT != RootContext.getBranchType()) {
// Just work as original statement
return statementCallback.execute(statementProxy.getTargetStatement(), args);
}
String dbType = statementProxy.getConnectionProxy().getDbType();
if (CollectionUtils.isEmpty(sqlRecognizers)) {
sqlRecognizers = SQLVisitorFactory.get(
statementProxy.getTargetSQL(),
dbType);
}
Executor<T> executor;
if (CollectionUtils.isEmpty(sqlRecognizers)) {
executor = new PlainExecutor<>(statementProxy, statementCallback);
}
else {
//单条sql
if (sqlRecognizers.size() == 1) {
SQLRecognizer sqlRecognizer = sqlRecognizers.get(0);
switch (sqlRecognizer.getSQLType()) {
//不同类型的sql交给不同的executor
case INSERT:
//spi,加载META-INF/services和META-INF/seata/下的文件(已经加载过的不会重复加载)
//这里获取InsertExecutor的实现类,并使用dbType匹配LoadLevel注解中的name值,取到对应的实现类(mysql对应MySQLInsertExecutor)
executor = EnhancedServiceLoader.load(InsertExecutor.class, dbType,
new Class[]{StatementProxy.class, StatementCallback.class, SQLRecognizer.class},
new Object[]{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;
}
}
//批量sql
else {
executor = new MultiExecutor<>(statementProxy, statementCallback, sqlRecognizers);
}
}
T rs;
try {
//看到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;
}
//BaseTransactionalExecutor
public T execute(Object... args) throws Throwable {
//获取XID
String xid = RootContext.getXID();
//有XID的情况下,设置到ConnectionProxy的context(xid)属性中
if (xid != null) {
statementProxy.getConnectionProxy().bind(xid);
}
//判断CONTEXT_HOLDER(threadlocal)的TX_LOCK是否有值
//如果有值则设置isGlobalLockRequire为true
statementProxy.getConnectionProxy().setGlobalLockRequire(RootContext.requireGlobalLock());
//接着看
return doExecute(args);
}
//AbstractDMLBaseExecutor
public T doExecute(Object... args) throws Throwable {
AbstractConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
//判断是否为自动提交,connect的自动提交属性一般为true
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);
//着重看这里
return new LockRetryPolicy(connectionProxy).execute(() -> {
T result = executeAutoCommitFalse(args);
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);
}
}
先看到LockRetryPolicy的execute方法,
public <T> T execute(Callable<T> callable) throws Exception {
//该属性默认为ture,可以通过client.rm.lock.retryPolicyBranchRollbackOnConflict配置
if (LOCK_RETRY_POLICY_BRANCH_ROLLBACK_ON_CONFLICT) {
//接着看
return doRetryOnLockConflict(callable);
} else {
//传入的方法直接调用
return callable.call();
}
}
protected <T> T doRetryOnLockConflict(Callable<T> callable) throws Exception {
LockRetryController lockRetryController = new LockRetryController();
//死循环调用
while (true) {
try {
//传入的方法直接调用
return callable.call();
} catch (LockConflictException lockConflict) {
onException(lockConflict);
//如果捕获的是LockConflictException异常(全局锁竞争冲突),则休眠重试,重新竞争全局锁
lockRetryController.sleep(lockConflict);
} catch (Exception e) {
onException(e);
throw e;
}
}
}
public void sleep(Exception e) throws LockWaitTimeoutException {
//超过lockRetryTimes重试次数,直接抛出异常了,之后回滚。
if (--lockRetryTimes < 0) {
throw new LockWaitTimeoutException("Global lock wait timeout", e);
}
//休眠lockRetryInternal后重新被while(true)调用callable
try {
Thread.sleep(lockRetryInternal);
} catch (InterruptedException ignore) {
}
}
protected void onException(Exception e) throws Exception {
ConnectionContext context = connection.getContext();
//UndoItems can't use the Set collection class to prevent ABA
context.getUndoItems().clear();
context.getLockKeysBuffer().clear();
//发生异常回滚
connection.getTargetConnection().rollback();
}
再看到executeAutoCommitFalse方法
protected T executeAutoCommitFalse(Object[] args) throws Exception {
if (!JdbcConstants.MYSQL.equalsIgnoreCase(getDbType()) && isMultiPk()) {
throw new NotSupportYetException("multi pk only support mysql!");
}
//获取sql执行之前的对应的表数据的内容
TableRecords beforeImage = beforeImage();
//sql执行
T result = statementCallback.execute(statementProxy.getTargetStatement(), args);
//获取sql执行之后的对应的表数据的内容
TableRecords afterImage = afterImage(beforeImage);
//构建undo_log
prepareUndoLog(beforeImage, afterImage);
return result;
}
以UpdateExecutor为例,看到beforeImage方法,
protected TableRecords beforeImage() throws SQLException {
ArrayList<List<Object>> paramAppenderList = new ArrayList<>();
//获取表的元数据信息,
TableMeta tmeta = getTableMeta();
//构建查询sql,查询业务操作前的数据和主键
String selectSQL = buildBeforeImageSQL(tmeta, paramAppenderList);
//将查询参数,设置到sql中
return buildTableRecords(tmeta, selectSQL, paramAppenderList);
}
private String buildBeforeImageSQL(TableMeta tableMeta, ArrayList<List<Object>> paramAppenderList) {
//大段拼接sql的过程
SQLUpdateRecognizer recognizer = (SQLUpdateRecognizer) sqlRecognizer;
List<String> updateColumns = recognizer.getUpdateColumns();
StringBuilder prefix = new StringBuilder("SELECT ");
StringBuilder suffix = new StringBuilder(" FROM ").append(getFromTableInSQL());
String whereCondition = buildWhereCondition(recognizer, paramAppenderList);
if (StringUtils.isNotBlank(whereCondition)) {
suffix.append(WHERE).append(whereCondition);
}
String orderBy = recognizer.getOrderBy();
if (StringUtils.isNotBlank(orderBy)) {
suffix.append(orderBy);
}
ParametersHolder parametersHolder = statementProxy instanceof ParametersHolder ? (ParametersHolder)statementProxy : null;
String limit = recognizer.getLimit(parametersHolder, paramAppenderList);
if (StringUtils.isNotBlank(limit)) {
suffix.append(limit);
}
//会加上FOR UPDATE,锁住数据
suffix.append(" FOR UPDATE");
StringJoiner selectSQLJoin = new StringJoiner(", ", prefix.toString(), suffix.toString());
//ONLY_CARE_UPDATE_COLUMNS默认为true,只会查询被更新的字段
if (ONLY_CARE_UPDATE_COLUMNS) {
//需要把主键一起查询出来
if (!containsPK(updateColumns)) {
selectSQLJoin.add(getColumnNamesInSQL(tableMeta.getEscapePkNameList(getDbType())));
}
for (String columnName : updateColumns) {
selectSQLJoin.add(columnName);
}
} else {
for (String columnName : tableMeta.getAllColumns().keySet()) {
selectSQLJoin.add(ColumnUtils.addEscape(columnName, getDbType()));
}
}
//拼接好的sql返回回去
return selectSQLJoin.toString();
}
protected TableRecords buildTableRecords(TableMeta tableMeta, String selectSQL, ArrayList<List<Object>> paramAppenderList) throws SQLException {
ResultSet rs = null;
//为查询sql构建新的PreparedStatement
try (PreparedStatement ps = statementProxy.getConnection().prepareStatement(selectSQL)) {
if (CollectionUtils.isNotEmpty(paramAppenderList)) {
for (int i = 0, ts = paramAppenderList.size(); i < ts; i++) {
List<Object> paramAppender = paramAppenderList.get(i);
for (int j = 0, ds = paramAppender.size(); j < ds; j++) {
//将查询参数设置到sql中
ps.setObject(i * ds + j + 1, paramAppender.get(j));
}
}
}
//查询出数据
rs = ps.executeQuery();
//数据封装为TableRecords
return TableRecords.buildRecords(tableMeta, rs);
} finally {
IOUtil.close(rs);
}
}
再看到afterImage方法
protected TableRecords afterImage(TableRecords beforeImage) throws SQLException {
TableMeta tmeta = getTableMeta();
if (beforeImage == null || beforeImage.size() == 0) {
return TableRecords.empty(getTableMeta());
}
//构建查询sql,查询业务操作后的对应的表的数据
String selectSQL = buildAfterImageSQL(tmeta, beforeImage);
ResultSet rs = null;
//设置查询参数,直接从beforeImage获取到主键值,设置进来
try (PreparedStatement pst = statementProxy.getConnection().prepareStatement(selectSQL)) {
SqlGenerateUtils.setParamForPk(beforeImage.pkRows(), getTableMeta().getPrimaryKeyOnlyName(), pst);
rs = pst.executeQuery();
return TableRecords.buildRecords(tmeta, rs);
} finally {
IOUtil.close(rs);
}
}
private String buildAfterImageSQL(TableMeta tableMeta, TableRecords beforeImage) throws SQLException {
StringBuilder prefix = new StringBuilder("SELECT ");
//此时的where条件,只需要主键作为条件,可以从beforeImage拿到主键
String whereSql = SqlGenerateUtils.buildWhereConditionByPKs(tableMeta.getPrimaryKeyOnlyName(), beforeImage.pkRows().size(), getDbType());
String suffix = " FROM " + getFromTableInSQL() + " WHERE " + whereSql;
StringJoiner selectSQLJoiner = new StringJoiner(", ", prefix.toString(), suffix);
//是否只查询更新的列
if (ONLY_CARE_UPDATE_COLUMNS) {
SQLUpdateRecognizer recognizer = (SQLUpdateRecognizer) sqlRecognizer;
List<String> updateColumns = recognizer.getUpdateColumns();
//一样要查询出主键
if (!containsPK(updateColumns)) {
selectSQLJoiner.add(getColumnNamesInSQL(tableMeta.getEscapePkNameList(getDbType())));
}
for (String columnName : updateColumns) {
selectSQLJoiner.add(columnName);
}
} else {
for (String columnName : tableMeta.getAllColumns().keySet()) {
selectSQLJoiner.add(ColumnUtils.addEscape(columnName, getDbType()));
}
}
return selectSQLJoiner.toString();
}
再看到prepareUndoLog方法
protected void prepareUndoLog(TableRecords beforeImage, TableRecords afterImage) throws SQLException {
if (beforeImage.getRows().isEmpty() && afterImage.getRows().isEmpty()) {
return;
}
//如果是更新,则更新前后的数据条数需要是一样的
if (SQLType.UPDATE == sqlRecognizer.getSQLType()) {
if (beforeImage.getRows().size() != afterImage.getRows().size()) {
throw new ShouldNeverHappenException("Before image size is not equaled to after image size, probably because you updated the primary keys.");
}
}
ConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
//如果是删除,则为beforeImage , 删除时的afterImage为空
TableRecords lockKeyRecords = sqlRecognizer.getSQLType() == SQLType.DELETE ? beforeImage : afterImage;
//将表名和主键值拼接成一个字符串,后续传递给服务端,控制全局锁
String lockKeys = buildLockKey(lockKeyRecords);
connectionProxy.appendLockKey(lockKeys);
//构建undolog对象
SQLUndoLog sqlUndoLog = buildUndoItem(beforeImage, afterImage);
connectionProxy.appendUndoLog(sqlUndoLog);
}
protected SQLUndoLog buildUndoItem(TableRecords beforeImage, TableRecords afterImage) {
//存放了beforeImage,afterImage,tableName,sqlType
SQLType sqlType = sqlRecognizer.getSQLType();
String tableName = sqlRecognizer.getTableName();
SQLUndoLog sqlUndoLog = new SQLUndoLog();
sqlUndoLog.setSqlType(sqlType);
sqlUndoLog.setTableName(tableName);
sqlUndoLog.setBeforeImage(beforeImage);
sqlUndoLog.setAfterImage(afterImage);
return sqlUndoLog;
}
executeAutoCommitFalse方法执行完毕后,看到connectionProxy.commit方法。
public void commit() throws SQLException {
try {
LOCK_RETRY_POLICY.execute(() -> {
doCommit();
return null;
});
} catch (SQLException e) {
//commit操作发生异常,回滚
if (targetConnection != null && !getAutoCommit()) {
rollback();
}
throw e;
} catch (Exception e) {
throw new SQLException(e);
}
}
public <T> T execute(Callable<T> callable) throws Exception {
//该配置为true,则直接调用doCommit()
if (LOCK_RETRY_POLICY_BRANCH_ROLLBACK_ON_CONFLICT) {
return callable.call();
} else {
return doRetryOnLockConflict(callable);
}
}
private void doCommit() throws SQLException {
//判断xid是否为空
if (context.inGlobalTransaction()) {
processGlobalTransactionCommit();
}
//判断isGlobalLockRequire是否为true
else if (context.isGlobalLockRequire()) {
processLocalCommitWithGlobalLocks();
}
else {
targetConnection.commit();
}
}
先看到processGlobalTransactionCommit方法
private void processGlobalTransactionCommit() throws SQLException {
try {
//看这里,和seata服务端通信,获取全局锁
register();
} catch (TransactionException e) {
//包装异常接着抛出,可能抛出LockConflictException (全局锁冲突异常)
recognizeLockKeyConflictException(e, context.buildLockKeys());
}
try {
//flushUndoLogs插入本地undo_log表
UndoLogManagerFactory.getUndoLogManager(this.getDbType()).flushUndoLogs(this);
//目标连接对象,提交事务,本地锁(for update)被放开,业务sql被提交,undo_log被提交
targetConnection.commit();
} catch (Throwable ex) {
LOGGER.error("process connectionProxy commit error: {}", ex.getMessage(), ex);
//向seata服务端报告分支事务执行失败。
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服务端(TC)通信,获得全局锁,并且接收服务端返回的branchId (分支事务id)
//TC 数据库持久化模式下
//此时lock_table就会记录,xid , branchid , 操作的表名和主键值,作为全局锁标志。
//此时branch_table就会记录,xid , branchid , 控制和记录分支事务
Long branchId = DefaultResourceManager.get().branchRegister(BranchType.AT, getDataSourceProxy().getResourceId(),
null, context.getXid(), null, context.buildLockKeys());
context.setBranchId(branchId);
}
public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid, String applicationData, String lockKeys) throws TransactionException {
try {
//构建分支事务注册请求
BranchRegisterRequest request = new BranchRegisterRequest();
request.setXid(xid);
request.setLockKey(lockKeys);
request.setResourceId(resourceId);
request.setBranchType(branchType);
request.setApplicationData(applicationData);
//netty通信将消息发送给TC
BranchRegisterResponse response = (BranchRegisterResponse) RmNettyRemotingClient.getInstance().sendSyncRequest(request);
//分支事务注册失败
if (response.getResultCode() == ResultCode.Failed) {
//抛出异常RmTransactionException,继承自TransactionException
//接收到服务端相应的Code
throw new RmTransactionException(response.getTransactionExceptionCode(), String.format("Response[ %s ]", response.getMsg()));
}
return response.getBranchId();
} catch (TimeoutException toe) {
throw new RmTransactionException(TransactionExceptionCode.IO, "RPC Timeout", toe);
} catch (RuntimeException rex) {
throw new RmTransactionException(TransactionExceptionCode.BranchRegisterFailed, "Runtime", rex);
}
}
private void recognizeLockKeyConflictException(TransactionException te, String lockKeys) throws SQLException {
//如果服务端返回的code为LockKeyConflict,则抛出LockConflictException,使得本次事务可以休眠重试
if (te.getCode() == TransactionExceptionCode.LockKeyConflict) {
StringBuilder reasonBuilder = new StringBuilder("get global lock fail, xid:" + context.getXid());
if (StringUtils.isNotBlank(lockKeys)) {
reasonBuilder.append(", lockKeys:" + lockKeys);
}
throw new LockConflictException(reasonBuilder.toString());
} else {
throw new SQLException(te);
}
}
看到flushUndoLogs方法,插入undo_log表
public void flushUndoLogs(ConnectionProxy cp) throws SQLException {
ConnectionContext connectionContext = cp.getContext();
if (!connectionContext.hasUndoLog()) {
return;
}
String xid = connectionContext.getXid();
long branchId = connectionContext.getBranchId();
BranchUndoLog branchUndoLog = new BranchUndoLog();
branchUndoLog.setXid(xid);
branchUndoLog.setBranchId(branchId);
branchUndoLog.setSqlUndoLogs(connectionContext.getUndoItems());
UndoLogParser parser = UndoLogParserFactory.getInstance();
byte[] undoLogContent = parser.encode(branchUndoLog);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Flushing UNDO LOG: {}", new String(undoLogContent, Constants.DEFAULT_CHARSET));
}
//插入undo_log表
insertUndoLogWithNormal(xid, branchId, buildContext(parser.getName()), undoLogContent,
cp.getTargetConnection());
}
protected void insertUndoLogWithNormal(String xid, long branchId, String rollbackCtx,
byte[] undoLogContent, Connection conn) throws SQLException {
insertUndoLog(xid, branchId, rollbackCtx, undoLogContent, State.Normal, conn);
}
private void insertUndoLog(String xid, long branchId, String rollbackCtx,
byte[] undoLogContent, State state, Connection conn) throws SQLException {
//插入undo_log表
try (PreparedStatement pst = conn.prepareStatement(INSERT_UNDO_LOG_SQL)) {
pst.setLong(1, branchId);
pst.setString(2, xid);
pst.setString(3, rollbackCtx);
pst.setBlob(4, BlobUtils.bytes2Blob(undoLogContent));
pst.setInt(5, state.getValue());
pst.executeUpdate();
} catch (Exception e) {
if (!(e instanceof SQLException)) {
e = new SQLException(e);
}
throw (SQLException) e;
}
}
看到report方法,向服务端告知分支事务是否提交成功
private void report(boolean commitDone) throws SQLException {
if (context.getBranchId() == null) {
return;
}
//默认5次 可以通过配置client.rm.reportRetryCount修改
int retry = REPORT_RETRY_COUNT;
while (retry > 0) {
try {
//commitDone为true,则报告PhaseOne_Done 分支事务一阶段完成
//否则报告PhaseOne_Failed 分支事务一阶段失败
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);
}
}
}
}
processGlobalTransactionCommit看完了,再来看到processLocalCommitWithGlobalLocks的逻辑
private void processLocalCommitWithGlobalLocks() throws SQLException {
checkLock(context.buildLockKeys());
try {
//直接提交
targetConnection.commit();
} catch (Throwable ex) {
throw new SQLException(ex);
}
context.reset();
}
public void checkLock(String lockKeys) throws SQLException {
if (StringUtils.isBlank(lockKeys)) {
return;
}
//携带lockKeys和TC通信,看是否有全局锁占用这些数据
// Just check lock without requiring lock by now.
try {
boolean lockable = DefaultResourceManager.get().lockQuery(BranchType.AT,
getDataSourceProxy().getResourceId(), context.getXid(), lockKeys);
if (!lockable) {
//抛出LockConflictException异常,则可以休眠重试本次事务
throw new LockConflictException();
}
} catch (TransactionException e) {
//包装异常接着抛出,可能抛出LockConflictException (全局锁冲突异常)
recognizeLockKeyConflictException(e, lockKeys);
}
}
public boolean lockQuery(BranchType branchType, String resourceId, String xid, String lockKeys)
throws TransactionException {
try {
GlobalLockQueryRequest request = new GlobalLockQueryRequest();
request.setXid(xid);
request.setLockKey(lockKeys);
request.setResourceId(resourceId);
GlobalLockQueryResponse response = null;
//发送加全局锁的请求给服务端
if (RootContext.inGlobalTransaction() || RootContext.requireGlobalLock()) {
response = (GlobalLockQueryResponse) RmNettyRemotingClient.getInstance().sendSyncRequest(request);
} else {
throw new RuntimeException("unknow situation!");
}
//tc端返回失败状态,抛出TransactionException异常
if (response.getResultCode() == ResultCode.Failed) {
throw new TransactionException(response.getTransactionExceptionCode(),
"Response[" + response.getMsg() + "]");
}
//tc告知是否加上了全局锁
return response.isLockable();
} catch (TimeoutException toe) {
throw new RmTransactionException(TransactionExceptionCode.IO, "RPC Timeout", toe);
} catch (RuntimeException rex) {
throw new RmTransactionException(TransactionExceptionCode.LockableCheckFailed, "Runtime", rex);
}
}