运行低代码jeecg-boot的微服务中的分布式seata示例的的时候,发现了一个问题,库存服务在分布式事务失败的问题,没有回滚,这就尴尬了。进一步说明之前,先简单了解一下该示例的顺序图。
可以看出这是一个典型的seata的分布式事务的例子,用户下单给订单服务,订单服务先生成一个一阶段订单,然后调用库存服务扣除商品库存服务,再调用钱包扣款,订单服务修改订单修改为下单成功,最后用户就可以看到接口返回下单成功。
我在测试的时候,发现库存服务执行成功,但是由于钱包服务的余额不足,扣款失败,整个分布式事务是失败的,但是库存服务扣掉的库存没有加回来。赶紧进行排查。先在每个微服务里头添加日志打印。
log.info("RootContext.ID:{}",RootContext.getXID());
发现除了订单服务可以正常打印出
RootContext.ID:192.168.233.1:8091:3522101009186471944
其他服务的RootContext的ID为空,根据官方文档,这个值为空,表示该进程没有纳入分布式事务管理,这是为什么呢。折腾了一周,在官方的spring-cloud-alibaba的github代码中找到了seata的例子。spring-cloud-alibaba仓库
他那边没有这样的问题,对比了一下依赖,发现官方例子有spring-cloud-starter-alibaba-seata,而该例子没有,赶紧加上
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
加了之后 ,原来为空的XID值非空了,库存服务在整个事务失败的时候,也能还原回来了。最后再分析一下代码,
spring-cloud-starter-alibaba-seata的包有SeataFeignClient 这个类,里头有这样一段代码,我给贴出来
/**
* @author xiaojing
*/
public class SeataFeignClient implements Client {
@Override
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) {
String xid = RootContext.getXID();
if (StringUtils.isEmpty(xid)) {
return request;
}
Map<String, Collection<String>> headers = new HashMap<>(MAP_SIZE);
headers.putAll(request.headers());
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());
}
}
上面的代码就是往FeignClient的header添加一个头TX_XID,把XID 的值传递下去,根据官方说明Seata 全局事务的传播机制就是指事务上下文的传播,根本上,就是 XID 的应用运行时的传播方式。seata官方说明
之前由于没有加spring-cloud-starter-alibaba-seata,导致XID没有在微服务的feign调用之前传递,导致事务回滚失败。原因找到了