Seata源码(十七)XID传递


铿然架构 | 作者 / 铿然一叶 这是 铿然架构 的第 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基础(五)函数式接口-复用,解耦之利刃\

相关推荐
陌路物是人非7 分钟前
记一个controller入参为null的奇怪问题
java·开发语言
小瓦码J码12 分钟前
Spring boot 如何自定义加密解密数据库连接配置
java
XiYang-DING12 分钟前
【Java EE】JUC的常见类(Callable、ReentrantLock、Semaphore和CountDownLatch )
java·java-ee
RuoyiOffice14 分钟前
2026 年开源 BPM/工作流引擎大盘点:Flowable vs Camunda vs Activiti vs Turbo——谁才是企业级首选?
java·spring boot·后端·开源·流程图·ruoyi·anti-design-vue
SamDeepThinking19 分钟前
别把业务逻辑塞进存储过程,适当用表驱动法
java·后端·架构
HZY1618yzh21 分钟前
洛谷题解:P16304 [蓝桥杯 2026 省 Java C 组] 抽奖活动
java·c++·算法·蓝桥杯
java1234_小锋30 分钟前
Spring AI 2.0 开发Java Agent智能体 - Advisors —— 拦截器模式增强AI能力
java·人工智能·spring·ai·spring ai2.0
Komore31534 分钟前
商户查询缓存
java·redis·缓存
ch.ju40 分钟前
Java程序设计(第3版)第二章——函数的返回值
java
架构源启1 小时前
OpenClaw 只能命令行触发?自研企业微信实现发消息即执行
java·chrome·自动化·企业微信