铿然架构 | 作者 / 铿然一叶 这是 铿然架构 的第 107 篇原创文章
1. XID作用
全局事务处理过程中,XID将一次业务请求涉及的所有微服务的数据库事务关联起来,XID也用于全局session的管理。
也因为如此,XID需要在多个微服务之间传递,给微服务内的RM和TM使用。
2.源码
源码来自:spring-cloud-starter-alibaba-seata
序号 | 类型 |
---|---|
groupId | com.alibaba.cloud |
artifactId | spring-cloud-alibaba-starters |
module | spring-cloud-starter-alibaba-seata |
2.1 类结构和核心逻辑
XID传递的类结构如下:
XID传递的基本逻辑如下:
● 客户端将xid放入http请求的header属性"TX_XID"中,服务端收到HTTP请求后,从此属性获取。
● Restful请求和Feign请求都有各自的拦截器来填充xid到header属性中,对应上图分别是:Restful-SeataRestTemplateInterceptor;Feign-SeataFeignRequestInterceptor
● 服务端通过拦截器SeataHandlerInterceptor获取xid取值后,bind到线程变量RootContext中,使用时从此线程变量中获取。
2.2 客户端Restful拦截器
SeataRestTemplateInterceptor.java
java
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes,
ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest);
String xid = RootContext.getXID();
if (StringUtils.hasLength(xid)) {
requestWrapper.getHeaders().add(RootContext.KEY_XID, xid);
}
return clientHttpRequestExecution.execute(requestWrapper, bytes);
}
2.3 客户端Feign拦截器
SeataFeignRequestInterceptor.java
java
public void apply(RequestTemplate template) {
String xid = RootContext.getXID();
if (!StringUtils.hasLength(xid)) {
return;
}
List<String> seataXid = new ArrayList<>();
seataXid.add(xid);
template.header(RootContext.KEY_XID, xid);
}
2.4 服务端拦截器
SeataHandlerInterceptor.java
java
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 (StringUtils.isBlank(xid) && 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) {
if (StringUtils.isNotBlank(RootContext.getXID())) {
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);
}
}
}
}
其他阅读:
萌新快速成长之路
如何编写软件设计文档
JAVA编程思想(一)通过依赖注入增加扩展性
JAVA编程思想(二)如何面向接口编程
JAVA编程思想(三)去掉别扭的if,自注册策略模式优雅满足开闭原则
JAVA编程思想(四)Builder模式经典范式以及和工厂模式如何选?
Java编程思想(七)使用组合和继承的场景
JAVA基础(一)简单、透彻理解内部类和静态内部类
JAVA基础(二)内存优化-使用Java引用做缓存
JAVA基础(三)ClassLoader实现热加载
JAVA基础(四)枚举(enum)和常量定义,工厂类使用对比
JAVA基础(五)函数式接口-复用,解耦之利刃\