seata 1.2版本集成Feign和ribbon组件源码深入分析(三)

如果微服务采用feign或ribbon组件做服务调用,seata框架有和feign、ribbon集成的功能,这是微服务的常用组合;

一、下面分析一下feign相关源码:

因为使用的是spring-cloud-starter-alibaba-seata,自带了Feign 远程传递xid 的支持,在发起Feign 远程请求时,可以看到将xid 塞入了消息头中。

1、spi入口:

SeataFeignClientAutoConfiguration 点击进入,找到和fegin相关的核心代码:

@Configuration
	protected static class FeignBeanPostProcessorConfiguration {

		@Bean
		SeataBeanPostProcessor seataBeanPostProcessor(
				SeataFeignObjectWrapper seataFeignObjectWrapper) {
			return new SeataBeanPostProcessor(seataFeignObjectWrapper);
		}

		@Bean
		SeataContextBeanPostProcessor seataContextBeanPostProcessor(
				BeanFactory beanFactory) {
			return new SeataContextBeanPostProcessor(beanFactory);
		}

		@Bean
		SeataFeignObjectWrapper seataFeignObjectWrapper(BeanFactory beanFactory) {
			return new SeataFeignObjectWrapper(beanFactory);//重点代码
		}
	}
2、SeataFeignObjectWrapper 点击进入,找到核心代码:
SeataFeignObjectWrapper(BeanFactory beanFactory) {
		this.beanFactory = beanFactory;
	}

	Object wrap(Object bean) {
		if (bean instanceof Client && !(bean instanceof SeataFeignClient)) {
			if (bean instanceof LoadBalancerFeignClient) {
				LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean);
				return new SeataLoadBalancerFeignClient(client.getDelegate(), factory(),
						clientFactory(), this.beanFactory);
			}
//通过feign调用RM 客户端,核心入口
			return new SeataFeignClient(this.beanFactory, (Client) bean);
		}
		return bean;
	}

3、SeataFeignClient 进入,找到核心代码,此类  public class SeataFeignClient implements Client  实现Client类接口的 execute 方法,

@Override
	public Response execute(Request request, Request.Options options) throws IOException {

		Request modifiedRequest = getModifyRequest(request);//继续进入
		return this.delegate.execute(modifiedRequest, options);
	}

//此方法通过feign调用RM服务,同时把全局事务id封装到header里,传到下游。
	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> fescarXid = new ArrayList<>();
		fescarXid.add(xid);
		headers.put(RootContext.KEY_XID, fescarXid);//xid 封装到header里

        //调用RM对象
		return Request.create(request.method(), request.url(), headers, request.body(),
				request.charset());
	}

继续:

继续:

到这里就明白了全局事务id是怎么通过feign组件传播的了

二、seata集成ribbon组件源码入口:

在被调用方,spring-cloud-starter-alibaba-seata也提供了支持,使用Spring MVC 中的HandlerInterceptor将消息头中的TX_XID绑定到RootContext中。

1、点击进入:SeataRestTemplateAutoConfiguration 

@Configuration
public class SeataRestTemplateAutoConfiguration {

    //初始化拦截器类
	@Bean
	public SeataRestTemplateInterceptor seataRestTemplateInterceptor() {
		return new SeataRestTemplateInterceptor();
	}

    //依赖注入
	@Autowired(required = false)
	private Collection<RestTemplate> restTemplates;

    //依赖注入
	@Autowired
	private SeataRestTemplateInterceptor seataRestTemplateInterceptor;

    //初始化接口
	@PostConstruct
	public void init() {
		if (this.restTemplates != null) {
			for (RestTemplate restTemplate : restTemplates) {
				List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>(
						restTemplate.getInterceptors());
				interceptors.add(this.seataRestTemplateInterceptor);//核心代码,点击进入
				restTemplate.setInterceptors(interceptors);
			}
		}
	}

}

2、点击  SeataRestTemplateInterceptor 进入

public class SeataRestTemplateInterceptor implements ClientHttpRequestInterceptor {
	@Override
	public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes,
			ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
		HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest);

		String xid = RootContext.getXID();

		if (!StringUtils.isEmpty(xid)) {
            //这里同样把全局事务id放到 header里
			requestWrapper.getHeaders().add(RootContext.KEY_XID, xid);
		}
		return clientHttpRequestExecution.execute(requestWrapper, bytes);//继续调用,进入
	}
}

3、进入

@Override
		public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
			if (this.iterator.hasNext()) {
				ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
				return nextInterceptor.intercept(request, body, this);
			}
			else {
				HttpMethod method = request.getMethod();
				Assert.state(method != null, "No standard HTTP method");
				ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
				request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));
				if (body.length > 0) {
					if (delegate instanceof StreamingHttpOutputMessage) {
						StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
						streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream));
					}
					else {
						StreamUtils.copy(body, delegate.getBody());
					}
				}
				return delegate.execute();
			}
		}

4、到这里一目了然,知道了全局事务id是怎么传播的了。

和之前一样,远程的分支事务,执行时因为远程这个方法是没有@GlobalTransactional注解的,所以不会进入到拦截器,但是代理了数据源,所以在执行SQL 时,还是会进入到代理的Executor中。执行SQL 时,发现存在xid ,则表明这是一个分布式事务,则会进行分支事务注册处理,之后和第二步一样。调用TC 注册分支事务时,TC 会查询数据库表中全局事务信息,创建分支事务,可以看到被调用方也生成了当前操作记录的全局锁。
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寅灯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值