Java极客 | 作者 / 铿然一叶 这是Java极客的第 93 篇原创文章
相关阅读:
萌新快速成长之路
JAVA编程思想(一)通过依赖注入增加扩展性
JAVA编程思想(二)如何面向接口编程
JAVA编程思想(三)去掉别扭的if,自注册策略模式优雅满足开闭原则
JAVA编程思想(四)Builder模式经典范式以及和工厂模式如何选?
Java编程思想(五)事件通知模式解耦过程
Java编程思想(六)事件通知模式解耦过程
Java编程思想(七)使用组合和继承的场景
JAVA基础(一)简单、透彻理解内部类和静态内部类
JAVA基础(二)内存优化-使用Java引用做缓存
JAVA基础(三)ClassLoader实现热加载
JAVA基础(四)枚举(enum)和常量定义,工厂类使用对比
JAVA基础(五)函数式接口-复用,解耦之利刃
HikariPool源码(二)设计思想借鉴
【极客源码】JetCache源码(一)开篇
【极客源码】JetCache源码(二)顶层视图
人在职场(一)IT大厂生存法则
1. 全局事务注解
属性 | 描述 |
---|---|
timeoutMills | 全局事务超时时间,默认值60秒,配置参数:client.tm.default-global-transaction-timeout将重载默认值 |
name | 全局事务名称,若不设置,取注解的实际方法名 |
rollbackFor | 要做回滚的异常类 |
rollbackForClassName | 要做回滚的异常类名 |
noRollbackFor | 不做异常回滚的异常类 |
noRollbackForClassName | 不做回滚处理的异常类名 |
propagation | 事务传播级别,默认值:REQUIRED |
lockRetryInternal | 获取全局锁重试间隔时间(ms),重载配置参数:client.rm.lock.retryInterval |
lockRetryTimes | 获取全局锁的重试次数,重载配置参数:client.rm.lock.retryTimes |
2. GlobalLock注解
属性 | 描述 |
---|---|
lockRetryInternal | 获取全局锁重试间隔(ms),对应配置参数:client.rm.lock.retryInterval |
lockRetryTimes | 获取全局锁重试次数,对一个配置参数:client.rm.lock.retryTimes |
3. 事务传播级别
Propagation.java
传播级别 | 描述 |
---|---|
REQUIRED | 默认级别,如果事务存在,则执行当前事务,否则开启新事务 |
REQUIRES_NEW | 如果事务存在,则挂起当前事务,开启一个新任务,类似事务嵌套 |
NOT_SUPPORTED | 如果事务存在,则挂起当前事务,待执行的业务方法不开启事务 |
SUPPORTS | 如果事务不存在,执行业务方法不开启事务,否则执行业务方法使用当前事务 |
NEVER | 如果事务存在,则抛出异常,否则待执行业务方法不开启事务 |
MANDATORY | 如果事务不存在,则抛出异常,否则使用当前事务执行业务方法 |
4. 事务状态
GlobalStatus.java 事务状态如下:
其他状态都好理解,主要描述下面几种状态:
状态 | 描述 |
---|---|
Unknown | 默认的状态,此时事务还没开启,没有生成XID |
Begin | 事务开始 |
Finished | GlobalSession为null时将置为此状态。Saga一阶段提交失败也会置为此状态。 |
5. 事务角色
GlobalTransactionRole.java
角色 | 描述 |
---|---|
Launcher | 全局事务开始者,根据此角色判断是否开启全局事务 |
Participant | 全局事务参与者,根据此角色判断是否做commit和rollback |
5.1 Launcher角色
5.1.1 初始化
TransactionalTemplate.execute方法中,事务传播级别为REQUIRES_NEW时,创建新事务:
java
case REQUIRES_NEW:
// If transaction is existing, suspend it, and then begin new transaction.
if (existingTransaction(tx)) {
suspendedResourcesHolder = tx.suspend();
tx = GlobalTransactionContext.createNew();
}
TransactionalTemplate.execute方法中,如果事务不存在,则创建新事务,简而言之是在入口方法上调用时创建新事务:
java
if (tx == null) {
tx = GlobalTransactionContext.createNew();
}
具体的创建事务方法: GlobalTransactionContext.java
java
public static GlobalTransaction createNew() {
return new DefaultGlobalTransaction();
}
接着设置角色为Launcher: DefaultGlobalTransaction.java
java
DefaultGlobalTransaction() {
this(null, GlobalStatus.UnKnown, GlobalTransactionRole.Launcher);
}
5.1.2 使用
在开启事务中使用到Launcher角色: TransactionalTemplate.java
java
private void beginTransaction(TransactionInfo txInfo, GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
try {
triggerBeforeBegin();
// 在开启事务中使用到Launcher角色
tx.begin(txInfo.getTimeOut(), txInfo.getName());
triggerAfterBegin();
} catch (TransactionException txe) {
throw new TransactionalExecutor.ExecutionException(tx, txe,
TransactionalExecutor.Code.BeginFailure);
}
}
如果不是Launcher角色,则直接返回,不开启事务,否则获取XID,开启事务: DefaultGlobalTransaction.java
java
public void begin(int timeout, String name) throws TransactionException {
// 如果不是Launcher角色,则直接返回,不开启事务
if (role != GlobalTransactionRole.Launcher) {
assertXIDNotNull();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Ignore Begin(): just involved in global transaction [{}]", xid);
}
return;
}
assertXIDNull();
String currentXid = RootContext.getXID();
if (currentXid != null) {
throw new IllegalStateException("Global transaction already exists," +
" can't begin a new global transaction, currentXid = " + currentXid);
}
xid = transactionManager.begin(null, null, name, timeout);
status = GlobalStatus.Begin;
RootContext.bind(xid);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Begin new global transaction [{}]", xid);
}
}
5.2 Participant角色
5.2.1 初始化
获取当前事务时判断XID是否存在,存在则返回null,否则创建新事物,角色为Participant: GlobalTransactionContext.java
java
public static GlobalTransaction getCurrent() {
String xid = RootContext.getXID();
if (xid == null) {
return null;
}
return new DefaultGlobalTransaction(xid, GlobalStatus.Begin, GlobalTransactionRole.Participant);
}
获取当前事务调用点: TransactionalTemplate.java
java
public Object execute(TransactionalExecutor business) throws Throwable {
// 1. Get transactionInfo
TransactionInfo txInfo = business.getTransactionInfo();
if (txInfo == null) {
throw new ShouldNeverHappenException("transactionInfo does not exist");
}
// 1.1 Get current transaction, if not null, the tx role is 'GlobalTransactionRole.Participant'.
GlobalTransaction tx = GlobalTransactionContext.getCurrent();
5.2.2 使用
在事务提交和回滚时,如果是Participant角色则不做处理,
事务提交: DefaultGlobalTransaction.java
java
public void commit() throws TransactionException {
// commit,非Launcher角色则直接返回
if (role == GlobalTransactionRole.Participant) {
// Participant has no responsibility of committing
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Ignore Commit(): just involved in global transaction [{}]", xid);
}
return;
}
assertXIDNotNull();
int retry = COMMIT_RETRY_COUNT <= 0 ? DEFAULT_TM_COMMIT_RETRY_COUNT : COMMIT_RETRY_COUNT;
try {
while (retry > 0) {
try {
status = transactionManager.commit(xid);
break;
} catch (Throwable ex) {
LOGGER.error("Failed to report global commit [{}],Retry Countdown: {}, reason: {}", this.getXid(), retry, ex.getMessage());
retry--;
if (retry == 0) {
throw new TransactionException("Failed to report global commit", ex);
}
}
}
} finally {
if (xid.equals(RootContext.getXID())) {
suspend();
}
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("[{}] commit status: {}", xid, status);
}
}
commit被调用点: TransactionalTemplate.java
java
private void commitTransaction(GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
try {
triggerBeforeCommit();
tx.commit();
triggerAfterCommit();
} catch (TransactionException txe) {
// 4.1 Failed to commit
throw new TransactionalExecutor.ExecutionException(tx, txe,
TransactionalExecutor.Code.CommitFailure);
}
}
事务回滚:
java
public void rollback() throws TransactionException {
if (role == GlobalTransactionRole.Participant) {
// Participant has no responsibility of rollback
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Ignore Rollback(): just involved in global transaction [{}]", xid);
}
return;
}
assertXIDNotNull();
int retry = ROLLBACK_RETRY_COUNT <= 0 ? DEFAULT_TM_ROLLBACK_RETRY_COUNT : ROLLBACK_RETRY_COUNT;
try {
while (retry > 0) {
try {
status = transactionManager.rollback(xid);
break;
} catch (Throwable ex) {
LOGGER.error("Failed to report global rollback [{}],Retry Countdown: {}, reason: {}", this.getXid(), retry, ex.getMessage());
retry--;
if (retry == 0) {
throw new TransactionException("Failed to report global rollback", ex);
}
}
}
} finally {
if (xid.equals(RootContext.getXID())) {
suspend();
}
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("[{}] rollback status: {}", xid, status);
}
}
rollback被调用点: TransactionalTemplate.java
java
private void rollbackTransaction(GlobalTransaction tx, Throwable originalException) throws TransactionException, TransactionalExecutor.ExecutionException {
triggerBeforeRollback();
tx.rollback();
triggerAfterRollback();
// 3.1 Successfully rolled back
throw new TransactionalExecutor.ExecutionException(tx, GlobalStatus.RollbackRetrying.equals(tx.getLocalStatus())
? TransactionalExecutor.Code.RollbackRetrying : TransactionalExecutor.Code.RollbackDone, originalException);
}
end.