Seata 全局事务不生效问题
大约 2 分钟
Seata 全局事务不生效问题
提示
记录工作中遇到到 Seata 全局事务不回滚的问题
开发环境
- Spring Boot
- Seata 分布式事务解决方案
实际场景
在工作中遇到需要调用其他服务接口,由于项目结构问题,没有使用其他的框架去调用接口,而是自己写 HTTP 请求接口调用
在事务发起方贴上注解 @GlobalTransactional 启动 Seata 全局事务,发生错误后事务回滚
但是在测试时发现出现异常时 被调用的服务并没有回滚事务
问题解决
通过查看日志发现被调用服务并没有启动全局事务,百思不得其解,一开始以为是因为配置了全局异常拦截器导致 Seata 并没有捕获到异常也就没有回滚事务,一顿折腾才发现不是这个原因
后面想到跨服务调用的事务传播是通过 传递事务 ID来开启的,那么会不会是事务 ID 没有传过来
跨服务调用场景下的事务传播,本质上就是把 XID 通过服务调用传递到服务提供方,并绑定到 RootContext 中
因为我是自己封装 HTTP 请求调用,所以只需要把 XID 放到请求中,那么事务就可以启动了
// 添加事务 ID
headers.add(RootContext.KEY_XID, RootContext.getXID());
后面也是去了解了一下 SeataHandlerInterceptor 中的源码,就可以发现拦截器也是直接获取请求中的 XID,所以说理论上只要把事务 ID 提供出来,Seata 可以支持任意的微服务框架
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();
// 这里获取 请求中事务 ID
String rpcXid = request.getHeader(RootContext.KEY_XID);
if (log.isDebugEnabled()) {
log.debug("xid in RootContext {} xid in RpcContext {}", xid, rpcXid);
}
if (StringUtils.isBlank(xid) && rpcXid != null) {
// 如果事务ID为空 直接绑定
RootContext.bind(rpcXid);
if (log.isDebugEnabled()) {
log.debug("bind {} to RootContext", rpcXid);
}
}
return true;
}
}