开发者社区 > 云原生 > 正文

RM执行本地事务抛出异常之后,没有执行清除xid,导致线程一直绑定着错误请求的xid

问题描述

AT模式 RM执行本地事务操作时出现异常,发现 RootContext的CONTEXT_HOLDER内线程对应的XID没有被清除, 跟踪发现没有执行以下两个地方的清除xid代码: · io.seata.integration.http.TransactionPropagationInterceptor#postHandle · io.seata.integration.http.HttpHandlerExceptionResolver#doResolveException

导致线程池内的线程处理完成请求之后回收到池内,但是依然在RootContext的CONTEXT_HOLDER关联了一个XID,出现的问题如下: 新的GlobalTransaction进来时执行io.seata.integration.http.TransactionPropagationInterceptor#preHandle方法的如下代码时: `

@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String xid = RootContext.getXID(); String rpcXid = request.getHeader(RootContext.KEY_XID);

if (LOGGER.isDebugEnabled()) {
    LOGGER.debug("xid in RootContext[{}] xid in HttpContext[{}]", xid, rpcXid);
}
if (**xid == null && rpcXid != null**) {
    RootContext.bind(rpcXid);
    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("bind[{}] to RootContext", rpcXid);
    }
}

return true;

}

当上次出现异常的线程再次从Pool被分配来处理一个新的@GlobalTransaction请求时,由于从RootContext内已经获取得到xid, 按 TransactionPropagationInterceptor#preHandle 的逻辑,这时候会忽略从Header带进来的xid(request.getHeader(RootContext.KEY_XID)) 带着Context内绑定的旧xid执行本地事务,当执行完成需要注册分支事务时,由于xid是旧的,会导致抛出这个错误: Could not found global transaction xid = (旧xid), may be has finished `

我们项目内有 @RestControllerAdvice 处理的全局异常处理(但是处理时正常返回Response 并未Throw Exception): `

@RestControllerAdvice public class LepuRestExceptionHandler {

@ExceptionHandler(Throwable.class) @ResponseStatus(HttpStatus.OK) public R handleError(Throwable e) { log.error("服务器异常", e); return R.fail(ResultCode.INTERFACE_INNER_INVOKE_ERROR, ResultCode.INTERFACE_INNER_INVOKE_ERROR.getMessage()); }

注:我在Isses内有发现不少 Could not found global transaction xid = xxx, may be has finished 的问题,但是没有提到我上述的这种场景,是否是我这边的用法不对?

经过测试 发现异常走了 @RestControllerAdvice 处理之后转换成普通的Result,就到不了HttpHandlerExceptionResolver。

环境信息

JDK version : 1.8 Seata version: spring-boot-seata-starter version 1.4.2 OS : Linux Others: SpringBoot 2.3.8

目前通过在 @RestControllerAdvice 异常处理内,发现异常时手动执行的XID清除 暂时解决了这个问题: @ExceptionHandler(Throwable.class) @ResponseStatus(HttpStatus.OK) public R handleError(Throwable e) { log.error("服务器异常", e); HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); if (RootContext.inGlobalTransaction()) { XidResource.cleanXid(request.getHeader(RootContext.KEY_XID)); } return R.fail(ResultCode.INTERFACE_INNER_INVOKE_ERROR, ResultCode.INTERFACE_INNER_INVOKE_ERROR.getMessage()); }

原提问者GitHub用户Dawnxzc

展开
收起
学习娃 2023-06-14 17:06:33 119 0
1 条回答
写回答
取消 提交回答
  • 在1.5已修复

    原回答者GitHub用户a364176773

    2023-06-14 17:34:22
    赞同 展开评论 打赏

阿里云拥有国内全面的云原生产品技术以及大规模的云原生应用实践,通过全面容器化、核心技术互联网化、应用 Serverless 化三大范式,助力制造业企业高效上云,实现系统稳定、应用敏捷智能。拥抱云原生,让创新无处不在。

相关电子书

更多
多IO线程优化版 立即下载
低代码开发师(初级)实战教程 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载