seata作为一款优秀的分布式事务解决方案,了解的它底层原理是非常有必要的,这篇的源码分析只分析它的AT模式,它支持很多的模式,比如XA模式,TCC模式、SAGA模式,但是现在用的最多的还是AT模式,AT模式是业务零侵入,而且在性能方面也是得到了保证的,需要注意的是AT模式保证的是最终一致性,而不是强一致性,我们都知道在CAP架构中,如果是AP架构就是最终一致性,如果是CP架构,那么就是强一致性,但是为了高可用,有些开源中间件对外开放的是最终一致性,而强一致性导致的后果就是如果出现了问题为了保证强一致性,那么是没有办法实现高可用的,所以为了保证高可用,采用AP架构是比较常用的,比如nacos注册中心;而AT、TCC、 XA这三个模式算是用的比较多的:
AT:事务期间的分支异步处理,及时释放资源,性能更好;
TCC:提供了二阶段的预处理、提交和回滚,开发者可以指定提交和回滚的方法,业务的侵入性较强,需要开发者去处理具体的事务提交和回滚;
XA:这种模式保证的是强一致性,也就是在事务期间,所有的分支事务都是对资源强拥有,是不会释放资源的,只有最后事务完成才会释放资源,所以性能上会差点,但是用于金钱转账的场景是必须的,因为转账的场景必须是强一致性。
作为一款分布式事务的解决方案,当然AT模式是首推的模式,对业务的零侵入,一个注解搞定,而且性能上也有保证,所以也是使用的最多的一种模式,所以这里就只分析AT模式。
seata的源码从哪里入手呢?上一篇比较已经知道了如何使用,只要TC配置好了以后,使用就非常简单,就是一个注解搞定,@GlobalTransactional这注解加在方法上就是一个TM,TM就是事务的发起者也是事务管理器,分支事务是一个RM,就是一个资源管理器,分析过这么多的开源框架,用脚指头都知道了这个注解肯定被spring切了,肯定会要想要是aop做的处理,其实对这个分布式事务的简单理解可以认为是:
TM(事务发起者):
try{
//开始事务,注册全局事务XID
begin();
//调用远程接口(如果是open fegin 将xid传入到下一个分支事务节点)
process();
//事务完成以后提交所有的分支事务
commit();
}catch(。。。){
//出现异常,回滚所有的分支事务
rollback();
}finally{
//关闭事务管理器TM
close();
}
seata的分布式事务其实就是上面的伪代码的逻辑差不多的,当然了它做了很多的处理
1.首先要扫描到加了@GlobalTransactional注解的bean做一个切面,也就是一个advice,然后将这个bean做一个切面aop放入容器;
2.当你调用加了@GlobalTransactional注解的方法的时候,会进入bean的切面aop去执行,执行的逻辑就如上面的伪代码。
现在的问题是要找到源码的入口,我这里有两种方式,一种很直接,还有一种就是通过spring boot的自动装配去找,最简单还是猜测去找,但是不知道其原理,我们都知道spring的aop的切面有一个方法拦截器接口MethodInterceptor,所以可以通过这种方式快速定位
这样找就非常快,seata的命名规则还是非常规范的,这样就非常快的找到了这个拦截器了,但是这种模式是建立在你对其很熟悉而且作者的类的命名很规范的情况下可以这样做;还有一种方式就是一步一步的去找,这种方式适用于所有的开源框架;
1.根据引入的开源框架seata,找到的它的包,打开如:
2.直接看spring.factories自动装配文件就可以去找到了GlobalTransactionAutoConfiguration这个自动装配的注入类,然后在里面有一个方法
@Bean
public GlobalTransactionScanner globalTransactionScanner() {
String applicationName = applicationContext.getEnvironment()
.getProperty("spring.application.name");
String txServiceGroup = seataProperties.getTxServiceGroup();
if (StringUtils.isEmpty(txServiceGroup)) {
txServiceGroup = applicationName + "-seata-service-group";
seataProperties.setTxServiceGroup(txServiceGroup);
}
return new GlobalTransactionScanner(applicationName, txServiceGroup);
}
很显然,构建这个拦截器就在GlobalTransactionScanner中;
3、我们看这个类GlobalTransactionScanner的实现
很自然就想到了spring的aop了,所以看到这个继承,就可以直接看它的wrapIfNecessary,这个方法是在spring bean的初始化后会调用是否有切面advise的时候来调用的,所以就要看这个方法
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
try {
synchronized (PROXYED_SET) {
//缓存的实现,如果找到了直接返回
if (PROXYED_SET.contains(beanName)) {
return bean;
}
interceptor = null;
//check TCC proxy
//TCC模式的aop实现
if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
//TCC interceptor, proxy bean of sofa:reference/dubbo:reference, and LocalTCC
interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
(ConfigurationChangeListener)interceptor);
} else {
//AT模式的实现
Class> serviceInterface = SpringProxyUtils.findTargetClass(bean);
Class>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);
//这个if判断的是否这个bean或者方法是否加了@GlobalTransactional注解,如果加了这个注解
//如果加了这个注解就代表要做一个advise,也就是切面逻辑
if (!existsAnnotation(new Class[]{serviceInterface})
&& !existsAnnotation(interfacesIfJdk)) {
return bean;
}
//构建advise的切面类GlobalTransactionalInterceptor,所以这里就是找到切面的逻辑拦截器
if (interceptor == null) {
if (globalTransactionalInterceptor == null) {
globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
ConfigurationCache.addConfigListener(
ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
(ConfigurationChangeListener)globalTransactionalInterceptor);
}
interceptor = globalTransactionalInterceptor;
}
}
LOGGER.info("Bean[{}] with name [{}] would use interceptor [{}]", bean.getClass().getName(), beanName, interceptor.getClass().getName());
if (!AopUtils.isAopProxy(bean)) {
bean = super.wrapIfNecessary(bean, beanName, cacheKey);
} else {
//构建advise返回
AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
for (Advisor avr : advisor) {
advised.addAdvisor(0, avr);
}
}
PROXYED_SET.add(beanName);
return bean;
}
} catch (Exception exx) {
throw new RuntimeException(exx);
}
}
所以这种方式也是可以找到GlobalTransactionalInterceptor的,我建议是通过这种方式去找,当然你熟悉了它的尿性,就可以直接定位。
GlobalTransactionalInterceptor是一个方法拦截器,那么方法拦截器肯定要执行里面的invoke方法了,执行所以这里直接切到invoke方法,seata我这里也只能分析它的主体流程,太细的不会去分析,没有必要,所以这里直接切到它的invoke方法
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
Class> targetClass =
methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null;
Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) {
//找到方法
final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
//得到方法上的注解
final GlobalTransactional globalTransactionalAnnotation =
getAnnotation(method, targetClass, GlobalTransactional.class);
final GlobalLock globalLockAnnotation = getAnnotation(method, targetClass, GlobalLock.class);
boolean localDisable = disable || (degradeCheck && degradeNum >= degradeCheckAllowTimes);
if (!localDisable) {
//如果加了@GlobalTransactional注解,调用handleGlobalTransaction
if (globalTransactionalAnnotation != null) {
return handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation);
} else if (globalLockAnnotation != null) {
return handleGlobalLock(methodInvocation, globalLockAnnotation);
}
}
}
return methodInvocation.proceed();
}
handleGlobalTransaction中处理的就是全局事务的核心逻辑,比如事务的传播特性的判断、全局事务的注册,业务逻辑的执行、事务的提交和回滚等等
Object handleGlobalTransaction(final MethodInvocation methodInvocation,
final GlobalTransactional globalTrxAnno) throws Throwable {
boolean succeed = true;
try {
//采用模板方法处理全局事务
return transactionalTemplate.execute(new TransactionalExecutor() {
@Override
public Object execute() throws Throwable {
return methodInvocation.proceed();
}
public String name() {
String name = globalTrxAnno.name();
if (!StringUtils.isNullOrEmpty(name)) {
return name;
}
return formatMethod(methodInvocation.getMethod());
}
/**
*获取事务对象信息
* @return
*/
@Override
public TransactionInfo getTransactionInfo() {
// reset the value of timeout
int timeout = globalTrxAnno.timeoutMills();
if (timeout <= 0 || timeout == DEFAULT_GLOBAL_TRANSACTION_TIMEOUT) {
timeout = defaultGlobalTransactionTimeout;
}
TransactionInfo transactionInfo = new TransactionInfo();
transactionInfo.setTimeOut(timeout);
transactionInfo.setName(name());
transactionInfo.setPropagation(globalTrxAnno.propagation());
transactionInfo.setLockRetryInternal(globalTrxAnno.lockRetryInternal());
transactionInfo.setLockRetryTimes(globalTrxAnno.lockRetryTimes());
Set rollbackRules = new LinkedHashSet<>();
for (Class> rbRule : globalTrxAnno.rollbackFor()) {
rollbackRules.add(new RollbackRule(rbRule));
}
for (String rbRule : globalTrxAnno.rollbackForClassName()) {
rollbackRules.add(new RollbackRule(rbRule));
}
for (Class> rbRule : globalTrxAnno.noRollbackFor()) {
rollbackRules.add(new NoRollbackRule(rbRule));
}
for (String rbRule : globalTrxAnno.noRollbackForClassName()) {
rollbackRules.add(new NoRollbackRule(rbRule));
}
transactionInfo.setRollbackRules(rollbackRules);
return transactionInfo;
}
});
......
接下来调用模板方法进行全局事务的处理
io.seata.tm.api.TransactionalTemplate#execute
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'.
/**
* 获取当前事务,
* 1.当前事务getCurrent不为空:如果当前事务不为空则证明本事务是分支事务,那么返回一个事务对象GlobalTransaction的
* 角色就是一个参与者(GlobalTransactionRole.Participant),结论:分支事务参与者;
* 2.当前事务getcurrent为空:如果获取的当前事务是空的,则证明是事务的发起者TM(Launcher),结论:事务发起者
*/
GlobalTransaction tx = GlobalTransactionContext.getCurrent();
// 1.2 Handle the transaction propagation.
Propagation propagation = txInfo.getPropagation();
SuspendedResourcesHolder suspendedResourcesHolder = null;
try {
//下面是处理事务的传播特性,如果没有指定,默认是REQUIRED
switch (propagation) {
case NOT_SUPPORTED:
// If transaction is existing, suspend it.
//传播特性如果是NOT_SUPPORTED表示不支持事务,当不支持事务的时候,如果当前是有事务的,那么挂起当前事务
//执行完业务方法business.execute()以后在finally中恢复当前事务
//判断当前事务是否为空,不为空调用suspend挂起当前事务
if (existingTransaction(tx)) {
//挂起当前事务,返回挂起对象suspendedResourcesHolder
suspendedResourcesHolder = tx.suspend();
}
// Execute without transaction and return.
//执行业务方法
return business.execute();
case REQUIRES_NEW:
// If transaction is existing, suspend it, and then begin new transaction.
/**
* REQUIRES_NEW方式的事务是判断当前事务是否为空:
* 1.为空,和REQUIRES一样创建一个新的事务;
* 2.不为空,挂起当前事务,开启一个新的事务,新的事务完成以后,恢复当前事务
*/
if (existingTransaction(tx)) {
suspendedResourcesHolder = tx.suspend();
tx = GlobalTransactionContext.createNew();
}
// Continue and execute with new transaction
break;
case SUPPORTS:
// If transaction is not existing, execute without transaction.
//SUPPORTS特性是当前事务为空的时候以非事务的方式运行,如果不为空,跟随当前事务运行
if (notExistingTransaction(tx)) {
return business.execute();
}
// Continue and execute with new transaction
break;
case REQUIRED:
//新创建事务运行
// If current transaction is existing, execute with current transaction,
// else continue and execute with new transaction.
break;
case NEVER:
// If transaction is existing, throw exception.
//NEVER方式是不用任何事务,如果当前存在事务,则直接抛出异常,表示不以事务的方式运行
if (existingTransaction(tx)) {
throw new TransactionException(
String.format("Existing transaction found for transaction marked with propagation 'never', xid = %s"
, tx.getXid()));
} else {
// Execute without transaction and return.
return business.execute();
}
case MANDATORY:
// If transaction is not existing, throw exception.
//MANDATORY强制当前业务在当前事务中运行,就是强制运行在当前事务中,如果当前事务不为空则抛出异常
if (notExistingTransaction(tx)) {
throw new TransactionException("No existing transaction found for transaction marked with propagation 'mandatory'");
}
// Continue and execute with current transaction.
break;
default:
throw new TransactionException("Not Supported Propagation:" + propagation);
}
// 1.3 If null, create new transaction with role 'GlobalTransactionRole.Launcher'.
if (tx == null) {
//如果事务为空,则代表是新的事务,也就是事务的发起者TM,创建一个事务角色为Launcher
tx = GlobalTransactionContext.createNew();
}
// set current tx config to holder
//将当前事务的一些属性封装到GlobalLockConfig中
GlobalLockConfig previousConfig = replaceGlobalLockConfig(txInfo);
try {
// 2. If the tx role is 'GlobalTransactionRole.Launcher', send the request of beginTransaction to TC,
// else do nothing. Of course, the hooks will still be triggered.
/**
* 开启事务,这个是比较重要的,这里如果是TM发起者,那么会调用远程的TC进行事务注册,简单来说就是注册
* 一个全局事务XID,通过netty调用TC事务协调者注册事务ID,并且将XID注册到上下文中
*/
beginTransaction(txInfo, tx);
Object rs;
try {
// Do Your Business
//业务方法的执行
rs = business.execute();
} catch (Throwable ex) {
// 3. The needed business exception to rollback.
//事务回滚
completeTransactionAfterThrowing(txInfo, tx, ex);
throw ex;
}
// 4. everything is fine, commit.
//事务的提交
commitTransaction(tx);
return rs;
} finally {
//5. clear
resumeGlobalLockConfig(previousConfig);
triggerAfterCompletion();
cleanUp();
}
} finally {
// If the transaction is suspended, resume it.
//如果有被挂起的事务,这里执行事务回复
if (suspendedResourcesHolder != null) {
tx.resume(suspendedResourcesHolder);
}
}
}
开始事务beginTransaction(txInfo, tx);
private void beginTransaction(TransactionInfo txInfo, GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
try {
triggerBeforeBegin();
tx.begin(txInfo.getTimeOut(), txInfo.getName());
triggerAfterBegin();
} catch (TransactionException txe) {
throw new TransactionalExecutor.ExecutionException(tx, txe,
TransactionalExecutor.Code.BeginFailure);
}
}
io.seata.tm.api.DefaultGlobalTransaction#begin(int, java.lang.String)
public void begin(int timeout, String name) throws TransactionException {
//如果事务的角色不是launcher,则直接返回,只有launcher才具有全局事务XID的注册权限
if (role != GlobalTransactionRole.Launcher) {
assertXIDNotNull();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Ignore Begin(): just involved in global transaction [{}]", xid);
}
return;
}
assertXIDNull();
//获取全局事务xid,如果不为空,抛出异常,因为这里是全局事务的注册,xid现在还是空的
String currentXid = RootContext.getXID();
if (currentXid != null) {
throw new IllegalStateException("Global transaction already exists," +
" can't begin a new global transaction, currentXid = " + currentXid);
}
//开始事务,调用远程的TC事务协调者进行事务注册,其实就是远程生成一个xid,然后返回,这个xid会贯穿到整个事务的生命周期中
//调用的地方是io.seata.tm.DefaultTransactionManager.begin
xid = transactionManager.begin(null, null, name, timeout);
//设置事务的状态为开始
status = GlobalStatus.Begin;
//将xid绑定到上下文
RootContext.bind(xid);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Begin new global transaction [{}]", xid);
}
}
io.seata.tm.DefaultTransactionManager#begin
public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
throws TransactionException {
//封装远程事务请求注册对象GlobalBeginRequest
GlobalBeginRequest request = new GlobalBeginRequest();
request.setTransactionName(name);
request.setTimeout(timeout);
//通过netty远程调用,这里使用的是TM的netty客户端发起的全局事务XID的注册
GlobalBeginResponse response = (GlobalBeginResponse) syncCall(request);
if (response.getResultCode() == ResultCode.Failed) {
throw new TmTransactionException(TransactionExceptionCode.BeginFailed, response.getMsg());
}
//TM远程调用完成以后返回事务的全局XID
return response.getXid();
}
服务端的处理是在server这个模块下面的Server.java这个启动类里面,这个启动类里面注册了Netty server服务端,上面的syncCall方法就是调用到了server的netty server进行事务的注册;
这里有一个协调类DefaultCoordinator就是处理的事务的注册、提交、回滚等操作,我这里分析的是全局事务的注册,所以看里面的全局事务注册的方法
io.seata.server.coordinator.DefaultCoordinator#doGlobalBegin
```java
/**
* 全局事务xid的注册,也就是全局事务的开启
* @param request the request
* @param response the response
* @param rpcContext the rpc context
* @throws TransactionException
*/
@Override
protected void doGlobalBegin(GlobalBeginRequest request, GlobalBeginResponse response, RpcContext rpcContext)
throws TransactionException {
//全局事务的注册,调用core.begin进行注册,xid生成的同时会保存到数据库/nacos/redis
response.setXid(core.begin(rpcContext.getApplicationId(), rpcContext.getTransactionServiceGroup(),
request.getTransactionName(), request.getTimeout()));
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Begin new global transaction applicationId: {},transactionServiceGroup: {}, transactionName: {},timeout:{},xid:{}",
rpcContext.getApplicationId(), rpcContext.getTransactionServiceGroup(), request.getTransactionName(), request.getTimeout(), response.getXid());
}
}
io.seata.server.coordinator.DefaultCore#begin
```java
public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
throws TransactionException {
//创建全局事务的session,在这里面就包含了xid的创建,事务id的创建
GlobalSession session = GlobalSession.createGlobalSession(applicationId, transactionServiceGroup, name,
timeout);
session.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
//开始事务,持久化到数据库
session.begin();
// transaction start event
eventBus.post(new GlobalTransactionEvent(session.getTransactionId(), GlobalTransactionEvent.ROLE_TC,
session.getTransactionName(), session.getBeginTime(), null, session.getStatus()));
//最后返回xid
return session.getXid();
}
io.seata.server.session.GlobalSession#GlobalSession(java.lang.String, java.lang.String, java.lang.String, int)
public GlobalSession(String applicationId, String transactionServiceGroup, String transactionName, int timeout) {
//事务id是用的uuid,其实使用的是雪花算法生成的
this.transactionId = UUIDGenerator.generateUUID();
//设置事务的状态为开始事务
this.status = GlobalStatus.Begin;
this.applicationId = applicationId;
this.transactionServiceGroup = transactionServiceGroup;
this.transactionName = transactionName;
this.timeout = timeout;
//生成全局事务XID,采用的格式是
//xid=ip:port:transactionId
this.xid = XID.generateXID(transactionId);
}
io.seata.server.session.GlobalSession#begin
@Override
public void begin() throws TransactionException {
this.status = GlobalStatus.Begin;
this.beginTime = System.currentTimeMillis();
this.active = true;
//这里采用的是生命周期监听器实现的xid的保存
//最终调用的是io.seata.server.storage.db.store.DataBaseTransactionStoreManager.writeSession进行xid的保存
for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onBegin(this);
}
}
我们来思考一个问题,seata的全局事务和分支事务是需要绑定的,那么有一个问题,如果采用了open feign的远程调用时候,那么分支事务是如何得到全局事务ID的呢?如果全局事务XID不传到分支事务,那么分支事务注册的时候是如何绑定全局事务的呢?其他这里seata对Feign的扩展Client进行了扩展,其实调用Feign远程调用的时候的客户端是Feign提供的客户端,seata在这个扩展Client中将全局事务的XID放入到了head中,我们来看下源码入口:
GlobalTransactionAutoConfiguration:我们已经知道了是全局事务的处理aop注册的自动配置类;
SeataFeignClientAutoConfiguration:这个就是feign远程调用的自动配置,在这里面将全局事务进行绑定到header中,我们来看下具体的实现:
com.alibaba.cloud.seata.feign.SeataFeignClientAutoConfiguration#feignBuilder
@Bean
@ConditionalOnMissingBean
@Scope("prototype")
Feign.Builder feignBuilder(BeanFactory beanFactory) {
//创建Feign Builder
return SeataFeignBuilder.builder(beanFactory);
}
static Feign.Builder builder(BeanFactory beanFactory) {
//这里创建了Feign的Client调用的具体实现SeataFeignClient
return Feign.builder().client(new SeataFeignClient(beanFactory));
}
所以对于Feign远程调用来说,其实如果引入了seata的话,那么每次的原创调用,如果开启了全局事务,那么全局事务的XID会在SeataFeignClient中进行绑定到header
com.alibaba.cloud.seata.feign.SeataFeignClient#execute
public Response execute(Request request, Request.Options options) throws IOException {
Request modifiedRequest = getModifyRequest(request);
return this.delegate.execute(modifiedRequest, options);
}
private Request getModifyRequest(Request request) {
//获取xid
String xid = RootContext.getXID();
if (StringUtils.isEmpty(xid)) {
return request;
}
Map<String, Collection<String>> headers = new HashMap<>(MAP_SIZE);
headers.putAll(request.headers());
//绑定xid到header
List<String> seataXid = new ArrayList<>();
seataXid.add(xid);
headers.put(RootContext.KEY_XID, seataXid);
return Request.create(request.method(), request.url(), headers, request.body(),
request.charset());
}
那么我们思考一个问题就是feign远程调用绑定了xid,那么远程接受到是如何取到这个值的呢?取当然很简单,但是对方也不知道自己是全局事务的,所以肯定不能让业务逻辑方法中自己去取,这个AT模式是零侵入的实现,所以肯定有个地方去取,我们能想到肯定是拦截器,一个web拦截器取到放入RootContext就可以了,所以SeataHandlerInterceptorConfiguration就是干的这个事情
com.alibaba.cloud.seata.web.SeataHandlerInterceptorConfiguration#addInterceptors
@ConditionalOnWebApplication
public class SeataHandlerInterceptorConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SeataHandlerInterceptor()).addPathPatterns("/**");
}
}
添加了一个web拦截器
com.alibaba.cloud.seata.web.SeataHandlerInterceptor#preHandle
@ConditionalOnWebApplication
public class SeataHandlerInterceptor implements HandlerInterceptor {
private static final Logger log = LoggerFactory
.getLogger(SeataHandlerInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
String xid = RootContext.getXID();
String rpcXid = request.getHeader(RootContext.KEY_XID);
if (log.isDebugEnabled()) {
log.debug("xid in RootContext {} xid in RpcContext {}", xid, rpcXid);
}
if (xid == null && rpcXid != null) {
RootContext.bind(rpcXid);
if (log.isDebugEnabled()) {
log.debug("bind {} to RootContext", rpcXid);
}
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception e) {
String rpcXid = request.getHeader(RootContext.KEY_XID);
if (StringUtils.isEmpty(rpcXid)) {
return;
}
String unbindXid = RootContext.unbind();
if (log.isDebugEnabled()) {
log.debug("unbind {} from RootContext", unbindXid);
}
if (!rpcXid.equalsIgnoreCase(unbindXid)) {
log.warn("xid in change during RPC from {} to {}", rpcXid, unbindXid);
if (unbindXid != null) {
RootContext.bind(unbindXid);
log.warn("bind {} back to RootContext", unbindXid);
}
}
}
}