问题描述
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
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
阿里云拥有国内全面的云原生产品技术以及大规模的云原生应用实践,通过全面容器化、核心技术互联网化、应用 Serverless 化三大范式,助力制造业企业高效上云,实现系统稳定、应用敏捷智能。拥抱云原生,让创新无处不在。