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基础(五)函数式接口-复用,解耦之利刃\

相关推荐
Yeats_Liao3 分钟前
Spring缓存(二):解决缓存雪崩、击穿、穿透问题
java·spring·缓存
猿究院-赵晨鹤14 分钟前
String、StringBuffer 和 StringBuilder 的区别
java·开发语言
葵野寺24 分钟前
【RelayMQ】基于 Java 实现轻量级消息队列(九)
java·开发语言·rabbitmq·java-rabbitmq
代码不停42 分钟前
MySQL联合查询
java·数据库·mysql
nightunderblackcat43 分钟前
新手向:C语言、Java、Python 的选择与未来指南
java·c语言·python
纯真时光1 小时前
Maven高级
java
好多171 小时前
《微服务事务管理》
java·微服务·架构
llp11101 小时前
MQTT Dashboard
java
浪扼飞舟2 小时前
c#基础二(类和对象,构造器调用顺序、访问级别、重写和多态、抽象类和接口)
java·开发语言·c#
失散132 小时前
分布式专题——10.5 ShardingSphere的CosID主键生成框架
java·分布式·架构·分库分表·shadingsphere