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

相关推荐
小叶学C++2 分钟前
【C++】类与对象(下)
java·开发语言·c++
2401_854391086 分钟前
高效开发:SpringBoot网上租赁系统实现细节
java·spring boot·后端
Cikiss14 分钟前
微服务实战——SpringCache 整合 Redis
java·redis·后端·微服务
wxin_VXbishe15 分钟前
springboot合肥师范学院实习实训管理系统-计算机毕业设计源码31290
java·spring boot·python·spring·servlet·django·php
Cikiss16 分钟前
微服务实战——平台属性
java·数据库·后端·微服务
无敌の星仔25 分钟前
一个月学会Java 第2天 认识类与对象
java·开发语言
OEC小胖胖29 分钟前
Spring Boot + MyBatis 项目中常用注解详解(万字长篇解读)
java·spring boot·后端·spring·mybatis·web
2401_857617621 小时前
SpringBoot校园资料平台:开发与部署指南
java·spring boot·后端
quokka561 小时前
Springboot 整合 logback 日志框架
java·spring boot·logback
计算机学姐1 小时前
基于SpringBoot+Vue的在线投票系统
java·vue.js·spring boot·后端·学习·intellij-idea·mybatis