一、代理Connect的获取
TX-LCN中的LCN模式是通过代理数据库连接,进而对事务进行控制的。通过静态代理的方式包装的原本来的Connection,并设置为手动提交,根据事务状态控制提交与回滚。
请记住:state为事务状态,state=1表示分布式事务成功,本地连接提交,其它为失败进行本地连接回滚。
代理连接代码如下:
package com.codingapi.txlcn.tc.core.transaction.lcn.resource;
import com.codingapi.txlcn.txmsg.dto.RpcResponseState;
import lombok.extern.slf4j.Slf4j;
import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
/**
* @author CoderTnT
*/
@Slf4j
public class LcnConnectionProxy implements Connection {
private Connection connection;
public LcnConnectionProxy(Connection connection) {
this.connection = connection;
}
/**
* notify connection
*
* @param state transactionState
* @return RpcResponseState RpcResponseState
*/
public RpcResponseState notify(int state) {
try {
if (state == 1) {
log.debug("commit transaction type[lcn] proxy connection:{}.", this);
connection.commit();
} else {
log.debug("rollback transaction type[lcn] proxy connection:{}.", this);
connection.rollback();
}
connection.close();
log.debug("transaction type[lcn] proxy connection:{} closed.", this);
return RpcResponseState.success;
} catch (Exception e) {
log.error(e.getLocalizedMessage(), e);
return RpcResponseState.fail;
}
}
// autoCommit设置false,表示 连接关闭自动提交,采用手动提交模式
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
connection.setAutoCommit(false);
}
@Override
public void commit() throws SQLException {
connection.commit();
}
@Override
public void rollback() throws SQLException {
connection.rollback();
}
@Override
public void close() throws SQLException {
connection.close();
}
@Override
public boolean getAutoCommit() throws SQLException {
return connection.getAutoCommit();
}
/*
* 忽略其他无关代码。。。
*/
}
LcnConnectionProxy代理连接的获取是通过AOP实现的
@Aspect
@Component
@Slf4j
public class DataSourceAspect implements Ordered {
private final TxClientConfig txClientConfig;
private final DTXResourceWeaver dtxResourceWeaver;
public DataSourceAspect(TxClientConfig txClientConfig, DTXResourceWeaver dtxResourceWeaver) {
this.txClientConfig = txClientConfig;
this.dtxResourceWeaver = dtxResourceWeaver;
}
//环绕通知获取代理connection连接
@Around("execution(* javax.sql.DataSource.getConnection(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
return dtxResourceWeaver.getConnection(() -> (Connection) point.proceed());
}
@Override
public int getOrder() {
return txClientConfig.getResourceOrder();
}
}
@Component
@Slf4j
public class DTXResourceWeaver {
private final TxLcnBeanHelper txLcnBeanHelper;
public DTXResourceWeaver(TxLcnBeanHelper txLcnBeanHelper) {
this.txLcnBeanHelper = txLcnBeanHelper;
}
public Object getConnection(ConnectionCallback connectionCallback) throws Throwable {
//获取事务本地上下文 dtxLocalContext
DTXLocalContext dtxLocalContext = DTXLocalContext.cur();
if (Objects.nonNull(dtxLocalContext) && dtxLocalContext.isProxy()) {
//获取事务本地上下文的事务类型 dtxLocalContext的事务类型
String transactionType = dtxLocalContext.getTransactionType();
//根据事务类型获取事务代理器:LcnTransactionResourceProxy或TccTransactionResourceProxy或TxcTransactionResourceProxy
TransactionResourceProxy resourceProxy = txLcnBeanHelper.loadTransactionResourceProxy(transactionType);
//由事务代理器根据connectionCallback Connection提供者 构造生成代理连接connection对象
Connection connection = resourceProxy.proxyConnection(connectionCallback);
log.debug("proxy a sql connection: {}.", connection);
return connection;
}
return connectionCallback.call();
}
}
二、整体正常执行的代码流程
实现分布式事务需要在特定执行的service方法上增加一个注解@LcnTransaction,这也是一个入口,整个流程的开始就在这里。
TransactionAspect类是一个切面类,对@LcnTransaction注解设置了环绕通知,在碰到注解@LcnTransaction开始了整个流程。
2.1 分布式事务执行方法的环绕
@Aspect //切面类注解
@Component
@Slf4j
public class TransactionAspect implements Ordered {
private final TxClientConfig txClientConfig;
private final DTXLogicWeaver dtxLogicWeaver;
public TransactionAspect(TxClientConfig txClientConfig, DTXLogicWeaver dtxLogicWeaver) {
this.txClientConfig = txClientConfig;
this.dtxLogicWeaver = dtxLogicWeaver;
}
/**
* DTC Aspect Common type() can lcn,tcc,txc and custom DTX type
*/
@Pointcut("@annotation(com.codingapi.txlcn.tc.annotation.TxTransaction)")
public void txTransactionPointcut() {
}
/**
* DTC Aspect (Type of LCN)
*/
@Pointcut("@annotation(com.codingapi.txlcn.tc.annotation.LcnTransaction)")
public void lcnTransactionPointcut() {
}
/**
* DTC Aspect (Type of TXC)
*/
@Pointcut("@annotation(com.codingapi.txlcn.tc.annotation.TxcTransaction)")
public void txcTransactionPointcut() {
}
/**
* DTC Aspect (Type of TCC)
*/
@Pointcut("@annotation(com.codingapi.txlcn.tc.annotation.TccTransaction)")
public void tccTransactionPointcut() {
}
@Around("txTransactionPointcut()")
public Object transactionRunning(ProceedingJoinPoint point) throws Throwable {
//见2.1.1
DTXInfo dtxInfo = DTXInfo.getFromCache(point);
TxTransaction txTransaction = dtxInfo.getBusinessMethod().getAnnotation(TxTransaction.class);
dtxInfo.setTransactionType(txTransaction.type());
dtxInfo.setTransactionPropagation(txTransaction.propagation());
return dtxLogicWeaver.runTransaction(dtxInfo, point::proceed);
}
@Around("lcnTransactionPointcut() && !txcTransactionPointcut()" +
"&& !tccTransactionPointcut() && !txTransactionPointcut()")
public Object runWithLcnTransaction(ProceedingJoinPoint point) throws Throwable {
//根据ProceedingJoinPoint 从缓存中获取事务信息dtxInfo
DTXInfo dtxInfo = DTXInfo.getFromCache(point);
//获取注解LcnTransaction,因为注解LcnTransaction上配置不同的传播行为propagation,如REQUIRED,SUPPORTS等
LcnTransaction lcnTransaction = dtxInfo.getBusinessMethod().getAnnotation(LcnTransaction.class);
//设置dtxInfo的事务类型为LCN
dtxInfo.setTransactionType(Transactions.LCN);
//设置事务传播行为,发起方是REQUIRED,参与方式是SUPPORTS
dtxInfo.setTransactionPropagation(lcnTransaction.propagation());
//织入后 开始执行事务
//见2.1.2
return dtxLogicWeaver.runTransaction(dtxInfo, point::proceed);
}
/*
* 忽略其他代码。。。
*/
}
2.1.1 在注解的开始从缓存中获取了事务信息dtxInfo
public static DTXInfo getFromCache(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// 根据切入点 获取方法签名信息signature
String signature = proceedingJoinPoint.getSignature().toString();
//根据方法签名生成事务单元ID
String unitId = Transactions.unitId(signature);
//根据事务单元ID获取事务信息dtxInfo
DTXInfo dtxInfo = dtxInfoCache.get(unitId);
if (Objects.isNull(dtxInfo)) {
// 根据切入点 获取方法签名信息methodSignature
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
//获取要执行的方法,这是接口的方法并不是我们要的方法
Method method = methodSignature.getMethod();
//获取目标类
Class> targetClass = proceedingJoinPoint.getTarget().getClass();
//获取真实的实现方法
Method thisMethod = targetClass.getMethod(method.getName(), method.getParameterTypes());
//构造dtxInfo 对象
dtxInfo = new DTXInfo(thisMethod, proceedingJoinPoint.getArgs(), targetClass);
//缓存dtxInfo 对象 键:事务单元ID,值:dtxInfo
dtxInfoCache.put(unitId, dtxInfo);
}
dtxInfo.reanalyseMethodArgs(proceedingJoinPoint.getArgs());
return dtxInfo;
}
2.1.2 事务执行runTransaction方法
public Object runTransaction(DTXInfo dtxInfo, BusinessCallback business) throws Throwable {
// 1、获取或新建一个线程变量DTXLocalContext
if (Objects.isNull(DTXLocalContext.cur())) {
DTXLocalContext.getOrNew();
} else {
return business.call();
}
log.debug("<---- TxLcn start ---->");
DTXLocalContext dtxLocalContext = DTXLocalContext.getOrNew();
TxContext txContext;
// ---------- 保证每个模块在一个DTX下只会有一个TxContext ---------- //
//2、如果已经存在事务上下文则获取,否则构建事务上下文TxContext
if (globalContext.hasTxContext()) {
// 有全局TC事务上下文的获取上下文
txContext = globalContext.txContext();
dtxLocalContext.setInGroup(true);
log.debug("Unit[{}] used parent's TxContext[{}].", dtxInfo.getUnitId(), txContext.getGroupId());
} else {
// 没有的开启本地事务上下文
// 见2.1.4
txContext = globalContext.startTx();
}
// 本地事务调用
if (Objects.nonNull(dtxLocalContext.getGroupId())) {
dtxLocalContext.setDestroy(false);
}
//3、为DTXLocalContext设置参数
dtxLocalContext.setUnitId(dtxInfo.getUnitId());
dtxLocalContext.setGroupId(txContext.getGroupId());
dtxLocalContext.setTransactionType(dtxInfo.getTransactionType());
// 事务参数
//4、构造TxTransactionInfo 并设置参数
TxTransactionInfo info = new TxTransactionInfo();
info.setBusinessCallback(business);
info.setGroupId(txContext.getGroupId());
info.setUnitId(dtxInfo.getUnitId());
info.setPointMethod(dtxInfo.getBusinessMethod());
info.setPropagation(dtxInfo.getTransactionPropagation());
info.setTransactionInfo(dtxInfo.getTransactionInfo());
info.setTransactionType(dtxInfo.getTransactionType());
info.setTransactionStart(txContext.isDtxStart());
//LCN事务处理器
try {
//执行5、执行事务 事务业务执行
return transactionServiceExecutor.transactionRunning(info);
}
//6、finally做一些通知与销毁操作。
//见2.1.5
finally {
// 线程执行业务完毕清理本地数据
if (dtxLocalContext.isDestroy()) {
// 通知事务执行完毕
synchronized (txContext.getLock()) {
txContext.getLock().notifyAll();
}
// TxContext生命周期是和事务组一样(不与具体模块相关的)
if (!dtxLocalContext.isInGroup()) {
globalContext.destroyTx();
}
DTXLocalContext.makeNeverAppeared();
TracingContext.tracing().destroy();
}
log.debug("<---- TxLcn end ---->");
}
}
2.1.3 主要说下2.1.2代码注释中的第2点什么情况下要获取已有的TxContext ,什么时候构造新的
如果一个调用链是这样的A->B->C 三个都是不同的模块则会为每个模块都构造新的TxContext,保证一个模块一个。
如果三个是两个模块,如A是一个模块,B和C是一个模块根据调用链的情况肯定是C的用B的TxContext。
2.1.4 构建新的TxContext
public TxContext startTx() {
TxContext txContext = new TxContext();
// 事务发起方判断
txContext.setDtxStart(!TracingContext.tracing().hasGroup());
// 是否为事务发起方
if (txContext.isDtxStart()) {
//是的话开始 初始化事务组操作,主要是构造事务组groupId,并放入以groupId和appMap为键的Map 到tracingContext对象缓存中
TracingContext.tracing().beginTransactionGroup();
}
//设置txContext事务组groupId
txContext.setGroupId(TracingContext.tracing().groupId());
String txContextKey = txContext.getGroupId() + ".dtx";
//key与txContext缓存起来
attachmentCache.attach(txContextKey, txContext);
log.debug("Start TxContext[{}]", txContext.getGroupId());
return txContext;
}
2.1.5 线程执行完的一些销毁动作 放到finally 块中
finally {
// 线程执行业务完毕清理本地数据
if (dtxLocalContext.isDestroy()) {
// 通知事务执行完毕
synchronized (txContext.getLock()) {
txContext.getLock().notifyAll();
}
// TxContext生命周期是和事务组一样(不与具体模块相关的)
if (!dtxLocalContext.isInGroup()) {
globalContext.destroyTx();
}
DTXLocalContext.makeNeverAppeared();
TracingContext.tracing().destroy();
}
log.debug("<---- TxLcn end ---->");
}
@Override
public void destroyTx() {
if (!hasTxContext()) {
throw new IllegalStateException("non TxContext.");
}
// 销毁缓存中的 以groupId.dtx 为键的事务组信息
destroyTx(txContext().getGroupId());
}
业务执行完会把attachmentCache的key移除掉,后面在参与方clean事务的时候才不会await。
@Override
public void destroyTx(String groupId) {
attachmentCache.remove(groupId + ".dtx");
log.debug("Destroy TxContext[{}]", groupId);
}
三、LCN分布式事务业务执行器 开始执行事务:transactionServiceExecutor.transactionRunning(info)
/**
* 事务业务执行
*
* @param info info
* @return Object
* @throws Throwable Throwable
*/
public Object transactionRunning(TxTransactionInfo info) throws Throwable {
// 1. 获取事务类型
String transactionType = info.getTransactionType();
// 2. 获取事务传播状态
DTXPropagationState propagationState = propagationResolver.resolvePropagationState(info);
// 2.1 如果不参与分布式事务立即终止
if (propagationState.isIgnored()) {
return info.getBusinessCallback().call();
}
// 3. 获取本地分布式事务控制器 dtxLocalControl :事务发起方控制器是LcnStartingTransaction,事务参与方控制器是LcnRunningTransaction
DTXLocalControl dtxLocalControl = txLcnBeanHelper.loadDTXLocalControl(transactionType, propagationState);
// 4. 织入事务操作
try {
// 4.1 记录事务类型到事务上下文
Set transactionTypeSet = globalContext.txContext(info.getGroupId()).getTransactionTypes();
transactionTypeSet.add(transactionType);
dtxLocalControl.preBusinessCode(info);
// 4.2 业务执行前
txLogger.txTrace(
info.getGroupId(), info.getUnitId(), "pre business code, unit type: {}", transactionType);
// 4.3 执行业务
Object result = dtxLocalControl.doBusinessCode(info);
// 4.4 业务执行成功
txLogger.txTrace(info.getGroupId(), info.getUnitId(), "business success");
dtxLocalControl.onBusinessCodeSuccess(info, result);
return result;
} catch (TransactionException e) {
txLogger.error(info.getGroupId(), info.getUnitId(), "before business code error");
throw e;
} catch (Throwable e) {
// 4.5 业务执行失败
txLogger.error(info.getGroupId(), info.getUnitId(), Transactions.TAG_TRANSACTION,
"business code error");
dtxLocalControl.onBusinessCodeError(info, e);
throw e;
} finally {
// 4.6 业务执行完毕
dtxLocalControl.postBusinessCode(info);
}
}
3.1 事务控制器根据事务类型与传播状态执行的也不同。获取传播类型:发起方是create,参与方是join
@Override
public DTXPropagationState resolvePropagationState(TxTransactionInfo txTransactionInfo) throws TransactionException {
// 本地已在DTX,根据事务传播,静默加入
if (DTXLocalContext.cur().isInGroup()) {
log.info("SILENT_JOIN group!");
return DTXPropagationState.SILENT_JOIN;
}
// 发起方之前没有事务
if (txTransactionInfo.isTransactionStart()) {
// 根据事务传播,对于 SUPPORTS 不参与事务
if (DTXPropagation.SUPPORTS.equals(txTransactionInfo.getPropagation())) {
return DTXPropagationState.NON;
}
// 根据事务传播,创建事务
return DTXPropagationState.CREATE;
}
// 已经存在DTX,根据事务传播,加入
return DTXPropagationState.JOIN;
}
3.2 事务发起方控制器是LcnStartingTransaction,事务参与方控制器是LcnRunningTransaction
3.3 这里分为四步执行业务前、执行业务,执行业务后、最后finally。
3.3.1 执行业务前preBusinessCode方法:dtxLocalControl.preBusinessCode(info);
1)发起方:LcnStartingTransaction
通过netty调用服务端创建事务组
设置DTXLocalContext的proxy为true。
@Override
public void preBusinessCode(TxTransactionInfo info) throws TransactionException {
// create DTX group
transactionControlTemplate.createGroup(
info.getGroupId(), info.getUnitId(), info.getTransactionInfo(), info.getTransactionType());
// lcn type need connection proxy
DTXLocalContext.makeProxy();
}
/**
* Client创建事务组操作集合
*
* @param groupId groupId
* @param unitId unitId
* @param transactionInfo txTrace
* @param transactionType transactionType
* @throws TransactionException 创建group失败时抛出
*/
public void createGroup(String groupId, String unitId, TransactionInfo transactionInfo, String transactionType)
throws TransactionException {
//创建事务组
try {
// 日志
txLogger.txTrace(groupId, unitId,
"create group > transaction type: {}", transactionType);
// 创建事务组消息
reliableMessenger.createGroup(groupId);
// 缓存发起方切面信息
aspectLogger.trace(groupId, unitId, transactionInfo);
} catch (RpcException e) {
// 通讯异常
dtxExceptionHandler.handleCreateGroupMessageException(groupId, e);
} catch (LcnBusinessException e) {
// 创建事务组业务失败
dtxExceptionHandler.handleCreateGroupBusinessException(groupId, e.getCause());
}
txLogger.txTrace(groupId, unitId, "create group over");
}
@Override
public void createGroup(String groupId) throws RpcException, LcnBusinessException {
// TxManager创建事务组
MessageDto messageDto = request(MessageCreator.createGroup(groupId));
if (!MessageUtils.statusOk(messageDto)) {
throw new LcnBusinessException(messageDto.loadBean(Throwable.class));
}
}
lcn-manager服务端接收到创建事务组信息后会用CreateGroupExecuteService的execute方法进行处理
@Service("rpc_create-group")
public class CreateGroupExecuteService implements RpcExecuteService {
private static final TxLogger txLogger = TxLogger.newLogger(CreateGroupExecuteService.class);
private final TransactionManager transactionManager;
@Autowired
public CreateGroupExecuteService(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Override
public Serializable execute(TransactionCmd transactionCmd) throws TxManagerException {
try {
transactionManager.begin(transactionCmd.getGroupId());
} catch (TransactionException e) {
throw new TxManagerException(e);
}
txLogger.txTrace(transactionCmd.getGroupId(), null, "created group");
return null;
}
}
@Override
public void begin(String groupId) throws TransactionException {
try {
dtxContextRegistry.create(groupId);
} catch (TransactionException e) {
throw new TransactionException(e);
}
}
@Override
public DTXContext create(String groupId) throws TransactionException {
try {
fastStorage.initGroup(groupId);
} catch (FastStorageException e) {
// idempotent processing
if (e.getCode() != FastStorageException.EX_CODE_REPEAT_GROUP) {
throw new TransactionException(e);
}
}
return get(groupId);
}
@Override
public void initGroup(String groupId) {
redisTemplate.opsForHash().put(REDIS_GROUP_PREFIX + groupId, "root", "");
redisTemplate.expire(REDIS_GROUP_PREFIX + groupId, managerConfig.getDtxTime() + 10000, TimeUnit.MILLISECONDS);
}
lcn-manager服务端会以hash结构在redis创建一个key。hashkey为“tm:group:”+groupId,key为root,value为空。超时时间为分布式事务超时时间dtxTime(默认值8秒)外加上10s 一共18秒。
2)参与方 :LcnRunningTransaction
@Override
public void preBusinessCode(TxTransactionInfo info) {
// lcn type need connection proxy
DTXLocalContext.makeProxy();
}
3.3.2 执行业务方法doBusinessCode :dtxLocalControl.doBusinessCode(info);
对于执行业务方法,就是执行我们的方法的内容。主要是调用其他服务,而其他服务又会有@LcnTransaction注解,又会走同样的流程,只是处理类不一样了,这里都会写出不同处理类的处理方式。
/**
* 执行业务代码
*
* @param info info
* @return Object Object
* @throws Throwable Throwable
*/
default Object doBusinessCode(TxTransactionInfo info) throws Throwable {
// 调用执行服务
return info.getBusinessCallback().call();
}
3.3.3 业务执行成功方法onBusinessCodeSuccess:dtxLocalControl.onBusinessCodeSuccess(info, result);
1)发起方:LcnStartingTransaction
设置系统分布式事务状态为1,代表标识成功
@Override
public void onBusinessCodeSuccess(TxTransactionInfo info, Object result) {
DTXLocalContext.cur().setSysTransactionState(1);
}
2)参与方:LcnRunningTransaction
加入到事务组
@Override
public void onBusinessCodeSuccess(TxTransactionInfo info, Object result) throws TransactionException {
log.debug("join group: [GroupId: {},Method: {}]" , info.getGroupId(),
info.getTransactionInfo().getMethodStr());
// join DTX group
transactionControlTemplate.joinGroup(info.getGroupId(), info.getUnitId(), info.getTransactionType(),
info.getTransactionInfo());
}
/**
* Client加入事务组操作集合
*
* @param groupId groupId
* @param unitId unitId
* @param transactionType transactionType
* @param transactionInfo txTrace
* @throws TransactionException 加入事务组失败时抛出
*/
public void joinGroup(String groupId, String unitId, String transactionType, TransactionInfo transactionInfo)
throws TransactionException {
try {
txLogger.txTrace(groupId, unitId, "join group > transaction type: {}", transactionType);
reliableMessenger.joinGroup(groupId, unitId, transactionType, DTXLocalContext.transactionState(globalContext.dtxState(groupId)));
txLogger.txTrace(groupId, unitId, "join group message over.");
// 异步检测
dtxChecking.startDelayCheckingAsync(groupId, unitId, transactionType);
// 缓存参与方切面信息
aspectLogger.trace(groupId, unitId, transactionInfo);
} catch (RpcException e) {
dtxExceptionHandler.handleJoinGroupMessageException(Arrays.asList(groupId, unitId, transactionType), e);
} catch (LcnBusinessException e) {
dtxExceptionHandler.handleJoinGroupBusinessException(Arrays.asList(groupId, unitId, transactionType), e);
}
txLogger.txTrace(groupId, unitId, "join group logic over");
}
@Override
public void joinGroup(String groupId, String unitId, String unitType, int transactionState) throws RpcException, LcnBusinessException {
JoinGroupParams joinGroupParams = new JoinGroupParams();
joinGroupParams.setGroupId(groupId);
joinGroupParams.setUnitId(unitId);
joinGroupParams.setUnitType(unitType);
joinGroupParams.setTransactionState(transactionState);
MessageDto messageDto = request(MessageCreator.joinGroup(joinGroupParams));
if (!MessageUtils.statusOk(messageDto)) {
throw new LcnBusinessException(messageDto.loadBean(Throwable.class));
}
}
lcn-client客户端会调用lcn-manager服务端去把当前操作加入到事务组中。服务端收到后会调用JoinGroupExecuteService类的execute进行处理。
@Service("rpc_join-group")
public class JoinGroupExecuteService implements RpcExecuteService {
private static final TxLogger txLogger = TxLogger.newLogger(JoinGroupExecuteService.class);
private final TransactionManager transactionManager;
private final DTXContextRegistry dtxContextRegistry;
private final RpcClient rpcClient;
@Autowired
public JoinGroupExecuteService(TransactionManager transactionManager,
DTXContextRegistry dtxContextRegistry, RpcClient rpcClient) {
this.transactionManager = transactionManager;
this.dtxContextRegistry = dtxContextRegistry;
this.rpcClient = rpcClient;
}
@Override
public Serializable execute(TransactionCmd transactionCmd) throws TxManagerException {
try {
DTXContext dtxContext = dtxContextRegistry.get(transactionCmd.getGroupId());
JoinGroupParams joinGroupParams = transactionCmd.getMsg().loadBean(JoinGroupParams.class);
txLogger.txTrace(transactionCmd.getGroupId(), joinGroupParams.getUnitId(), "unit:{} try join group:{}",
joinGroupParams.getUnitId(), transactionCmd.getGroupId());
transactionManager.join(dtxContext, joinGroupParams.getUnitId(), joinGroupParams.getUnitType(),
rpcClient.getAppName(transactionCmd.getRemoteKey()), joinGroupParams.getTransactionState());
txLogger.txTrace(transactionCmd.getGroupId(), joinGroupParams.getUnitId(), "unit:{} joined.",
joinGroupParams.getUnitId());
} catch (TransactionException e) {
txLogger.error(this.getClass().getSimpleName(), e.getMessage());
throw new TxManagerException(e.getLocalizedMessage());
}
// non response
return null;
}
}
@Override
public void join(DTXContext dtxContext, String unitId, String unitType, String modId, int userState) throws TransactionException {
//手动回滚时设置状态为回滚状态 0
if (userState == 0) {
dtxContext.resetTransactionState(0);
}
TransactionUnit transactionUnit = new TransactionUnit();
transactionUnit.setModId(modId);
transactionUnit.setUnitId(unitId);
transactionUnit.setUnitType(unitType);
dtxContext.join(transactionUnit);
}
@Override
public void join(TransactionUnit transactionUnit) throws TransactionException {
try {
fastStorage.saveTransactionUnitToGroup(groupId, transactionUnit);
} catch (FastStorageException e) {
throw new TransactionException("attempts to join the non-existent transaction group. ["
+ transactionUnit.getUnitId() + '@' + transactionUnit.getModId() + ']');
}
}
@Override
public void saveTransactionUnitToGroup(String groupId, TransactionUnit transactionUnit) throws FastStorageException {
if (Optional.ofNullable(redisTemplate.hasKey(REDIS_GROUP_PREFIX + groupId)).orElse(false)) {
redisTemplate.opsForHash().put(REDIS_GROUP_PREFIX + groupId, transactionUnit.getUnitId(), transactionUnit);
return;
}
throw new FastStorageException("attempts to the non-existent transaction group " + groupId,
FastStorageException.EX_CODE_NON_GROUP);
}
lcn-manager服务端收到信息后会在redis与creategroup的hashkey相同数据下(hashkey为“tm:group:”+groupId)保存事务单元信息,key为事务单元id,值为事务单元信息。
3.3.4 业务执行成功后finally中的postBusinessCode:dtxLocalControl.postBusinessCode(info);
1)客户端发起方 LcnStartingTransaction 通知lcn-manager 服务端 处理事务组,然后服务端返回状态值state,根据状态值,客户端发起方再决定真正的提交事务还是回滚事务。客户端参与方 不加额外处理。
@Override
public void postBusinessCode(TxTransactionInfo info) {
// RPC close DTX group
transactionControlTemplate.notifyGroup(
info.getGroupId(), info.getUnitId(), info.getTransactionType(),
DTXLocalContext.transactionState(globalContext.dtxState(info.getGroupId())));
}
/**
* Client通知事务组操作集合
*
* @param groupId groupId
* @param unitId unitId
* @param transactionType transactionType
* @param state transactionState
*/
public void notifyGroup(String groupId, String unitId, String transactionType, int state) {
try {
txLogger.txTrace(
groupId, unitId, "notify group > transaction type: {}, state: {}.", transactionType, state);
if (globalContext.isDTXTimeout()) {
throw new LcnBusinessException("dtx timeout.");
}
//服务端返回不同的状态值 state
state = reliableMessenger.notifyGroup(groupId, state);
//清理事务 包含本地事务的提交和回滚
//执行不同代理LcnConnectionProxy的notify(int state)方法
//细节见3.3.5
transactionCleanTemplate.clean(groupId, unitId, transactionType, state);
} catch (TransactionClearException e) {
txLogger.trace(groupId, unitId, Transactions.TE, "clean transaction fail.");
} catch (RpcException e) {
dtxExceptionHandler.handleNotifyGroupMessageException(Arrays.asList(groupId, state, unitId, transactionType), e);
} catch (LcnBusinessException e) {
// 关闭事务组失败
dtxExceptionHandler.handleNotifyGroupBusinessException(Arrays.asList(groupId, state, unitId, transactionType), e.getCause());
}
txLogger.txTrace(groupId, unitId, "notify group exception state {}.", state);
}
注意:通知事务组进行提交事务这边比较绕。
@Override
public int notifyGroup(String groupId, int transactionState) throws RpcException, LcnBusinessException {
NotifyGroupParams notifyGroupParams = new NotifyGroupParams();
notifyGroupParams.setGroupId(groupId);
notifyGroupParams.setState(transactionState);
MessageDto messageDto = request0(MessageCreator.notifyGroup(notifyGroupParams),
clientConfig.getTmRpcTimeout() * clientConfig.getChainLevel());
// 成功清理发起方事务
if (!MessageUtils.statusOk(messageDto)) {
throw new LcnBusinessException(messageDto.loadBean(Throwable.class));
}
return messageDto.loadBean(Integer.class);
}
同样是向lcn-manager服务端发送请求,服务端接收到请求后用NotifyGroupExecuteService类的execute方法进行处理。
@Service("rpc_notify-group")
public class NotifyGroupExecuteService implements RpcExecuteService {
private static final TxLogger txLogger = TxLogger.newLogger(NotifyGroupExecuteService.class);
private final TransactionManager transactionManager;
private final DTXContextRegistry dtxContextRegistry;
@Autowired
public NotifyGroupExecuteService(TransactionManager transactionManager, DTXContextRegistry dtxContextRegistry) {
this.transactionManager = transactionManager;
this.dtxContextRegistry = dtxContextRegistry;
}
@Override
public Serializable execute(TransactionCmd transactionCmd) throws TxManagerException {
try {
DTXContext dtxContext = dtxContextRegistry.get(transactionCmd.getGroupId());
// 解析参数
NotifyGroupParams notifyGroupParams = transactionCmd.getMsg().loadBean(NotifyGroupParams.class);
//如果状态为1则事务成功,通知参与模块进行提交
int commitState = notifyGroupParams.getState();
// 获取事务状态(当手动回滚时会先设置状态)
int transactionState = transactionManager.transactionStateFromFastStorage(transactionCmd.getGroupId());
if (transactionState == 0) {
commitState = 0;
}
// 系统日志
txLogger.txTrace(
transactionCmd.getGroupId(), "", "notify group state: {}", notifyGroupParams.getState());
//如果状态为1则事务成功,通知参与模块进行提交
if (commitState == 1) {
transactionManager.commit(dtxContext);
}
//如果状态为0则事务失败,通知参与模块进行回滚
else if (commitState == 0) {
transactionManager.rollback(dtxContext);
}
if (transactionState == 0) {
txLogger.txTrace(transactionCmd.getGroupId(), "", "mandatory rollback for user.");
}
return commitState;
} catch (TransactionException e) {
throw new TxManagerException(e);
} finally {
transactionManager.close(transactionCmd.getGroupId());
// 系统日志
txLogger.txTrace(transactionCmd.getGroupId(), "", "notify group successfully.");
}
}
}
@Override
public void commit(DTXContext dtxContext) throws TransactionException {
notifyTransaction(dtxContext, 1);
}
private void notifyTransaction(DTXContext dtxContext, int transactionState) throws TransactionException {
List transactionUnits = dtxContext.transactionUnits();
log.debug("group[{}]'s transaction units: {}", dtxContext.getGroupId(), transactionUnits);
for (TransactionUnit transUnit : transactionUnits) {
NotifyUnitParams notifyUnitParams = new NotifyUnitParams();
notifyUnitParams.setGroupId(dtxContext.getGroupId());
notifyUnitParams.setUnitId(transUnit.getUnitId());
notifyUnitParams.setUnitType(transUnit.getUnitType());
notifyUnitParams.setState(transactionState);
txLogger.txTrace(dtxContext.getGroupId(), notifyUnitParams.getUnitId(), "notify {}'s unit: {}",
transUnit.getModId(), transUnit.getUnitId());
try {
List modChannelKeys = rpcClient.remoteKeys(transUnit.getModId());
if (modChannelKeys.isEmpty()) {
// record exception
throw new RpcException("offline mod.");
}
MessageDto respMsg =
rpcClient.request(modChannelKeys.get(0), MessageCreator.notifyUnit(notifyUnitParams));
if (!MessageUtils.statusOk(respMsg)) {
// 提交/回滚失败的消息处理
List
notifyTransaction方法又会调用客户端,客户端用DefaultNotifiedUnitService的execute方法进行处理
@Service("rpc_lcn_notify-unit")
@Slf4j
public class LcnNotifiedUnitService extends DefaultNotifiedUnitService {
@Autowired
public LcnNotifiedUnitService(TransactionCleanTemplate transactionCleanTemplate, TCGlobalContext context) {
super(transactionCleanTemplate, context);
}
}
public class DefaultNotifiedUnitService implements RpcExecuteService {
private static final TxLogger txLogger = TxLogger.newLogger(DefaultNotifiedUnitService.class);
private final TransactionCleanTemplate transactionCleanTemplate;
private TCGlobalContext globalContext;
public DefaultNotifiedUnitService(TransactionCleanTemplate transactionCleanTemplate, TCGlobalContext globalContext) {
this.transactionCleanTemplate = transactionCleanTemplate;
this.globalContext = globalContext;
}
@Override
public Serializable execute(TransactionCmd transactionCmd) throws TxClientException {
try {
NotifyUnitParams notifyUnitParams = transactionCmd.getMsg().loadBean(NotifyUnitParams.class);
// 保证业务线程执行完毕后执行事务清理操作
TxContext txContext = globalContext.txContext(transactionCmd.getGroupId());
if (Objects.nonNull(txContext)) {
synchronized (txContext.getLock()) {
txLogger.txTrace(transactionCmd.getGroupId(), notifyUnitParams.getUnitId(),
"clean transaction cmd waiting for business code finish.");
txContext.getLock().wait();
}
}
// 事务清理操作
// 见3.3.5
transactionCleanTemplate.clean(
notifyUnitParams.getGroupId(),
notifyUnitParams.getUnitId(),
notifyUnitParams.getUnitType(),
notifyUnitParams.getState());
return true;
} catch (TransactionClearException | InterruptedException e) {
throw new TxClientException(e);
}
}
}
3.3.5 根据服务端返回的状态值state 客户端决定真正执行提交本地事务还是回滚
调用的方法为:transactionCleanTemplate.clean(groupId, unitId, transactionType, state);
/**
* 清理事务
*
* @param groupId groupId
* @param unitId unitId
* @param unitType unitType
* @param state transactionState
* @throws TransactionClearException TransactionClearException
*/
public void clean(String groupId, String unitId, String unitType, int state) throws TransactionClearException {
txLogger.txTrace(groupId, unitId, "clean transaction");
try {
cleanWithoutAspectLog(groupId, unitId, unitType, state);
aspectLogger.clearLog(groupId, unitId);
} catch (TransactionClearException e) {
if (!e.isNeedCompensation()) {
aspectLogger.clearLog(groupId, unitId);
}
} catch (Throwable throwable) {
aspectLogger.clearLog(groupId, unitId);
}
txLogger.txTrace(groupId, unitId, "clean transaction over");
}
/**
* 清理事务(不清理切面日志)
*
* @param groupId groupId
* @param unitId unitId
* @param unitType unitType
* @param state transactionState
* @throws TransactionClearException TransactionClearException
*/
public void cleanWithoutAspectLog(String groupId, String unitId, String unitType, int state) throws TransactionClearException {
try {
transactionBeanHelper.loadTransactionCleanService(unitType).clear(
groupId, state, unitId, unitType
);
} finally {
globalContext.clearGroup(groupId);
dtxChecking.stopDelayChecking(groupId, unitId);
}
}
/**
* 事务清理业务
*
* @param groupId groupId
* @param state 事务状态 1 提交 0 回滚
* @param unitId unitId
* @param unitType 事务类型
* @throws TransactionClearException TransactionClearException
*/
void clear(String groupId, int state, String unitId, String unitType) throws TransactionClearException;
@Component
@Slf4j
public class LcnTransactionCleanService implements TransactionCleanService {
private final TCGlobalContext globalContext;
@Autowired
public LcnTransactionCleanService(TCGlobalContext globalContext) {
this.globalContext = globalContext;
}
@Override
public void clear(String groupId, int state, String unitId, String unitType) throws TransactionClearException {
try {
LcnConnectionProxy connectionProxy = globalContext.getLcnConnection(groupId);
connectionProxy.notify(state);
// todo notify exception
} catch (TCGlobalContextException e) {
log.warn("Non lcn connection when clear transaction.");
}
}
}
@Slf4j
public class LcnConnectionProxy implements Connection {
private Connection connection;
public LcnConnectionProxy(Connection connection) {
this.connection = connection;
}
/**
* notify connection
*
* @param state transactionState
* @return RpcResponseState RpcResponseState
*/
public RpcResponseState notify(int state) {
try {
if (state == 1) {
log.debug("commit transaction type[lcn] proxy connection:{}.", this);
connection.commit();
} else {
log.debug("rollback transaction type[lcn] proxy connection:{}.", this);
connection.rollback();
}
connection.close();
log.debug("transaction type[lcn] proxy connection:{} closed.", this);
return RpcResponseState.success;
} catch (Exception e) {
log.error(e.getLocalizedMessage(), e);
return RpcResponseState.fail;
}
}
/*
* 省略其他代码
*
*/
}