Tx-lcn LCN分布式事务小细节点(4)

问:关于参与方如何知道已经存在事务的?(从请求头传入事务组信息和 读取事务组信息。通过拦截器)

答:一般我们都是一接口的方式去调用服务,基本就是restTemplate如果用到spring Cloud还会用到Fegin。框架在进行接口调用时都会通过把事务消息放在header中,后边的服务从header中就会得到当前的事务信息。

ClientHttpRequestInterceptor可以对请求进行拦截,并在其被发送至服务端之前修改请求或是增强相应的信息。

一、添加事务组信息到请求头

1、RestTemplate调用实现拦截

RestTemplateTracingTransmitter实现了ClientHttpRequestInterceptor接口。并把此拦截器加入到restTemplate的拦截器链中,当restTemplate去请求别的资源时,会被RestTemplateTracingTransmitter拦截到,在requestheader中加入groupid信息。
 

@ConditionalOnClass(RestTemplate.class)
@Component
@Order
public class RestTemplateTracingTransmitter implements ClientHttpRequestInterceptor {

    @Autowired
    public RestTemplateTracingTransmitter(@Autowired(required = false) List restTemplates) {
        if (Objects.nonNull(restTemplates)) {
            restTemplates.forEach(restTemplate -> {
                List interceptors = restTemplate.getInterceptors();
                //加入拦截器链
                interceptors.add(interceptors.size(), RestTemplateTracingTransmitter.this);
            });
        }
    }

    @Override
    @NonNull
    public ClientHttpResponse intercept(
            @NonNull HttpRequest httpRequest, @NonNull byte[] bytes,
            @NonNull ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
      //把信息存储到header中
        Tracings.transmit(httpRequest.getHeaders()::add);
        return clientHttpRequestExecution.execute(httpRequest, bytes);
    }
}
/**
     * 传输Tracing信息
     *
     * @param tracingSetter Tracing信息设置器
     */
    public static void transmit(TracingSetter tracingSetter) {
        if (TracingContext.tracing().hasGroup()) {
            log.debug("tracing transmit group:{}", TracingContext.tracing().groupId());
            tracingSetter.set(TracingConstants.HEADER_KEY_GROUP_ID, TracingContext.tracing().groupId());
            tracingSetter.set(TracingConstants.HEADER_KEY_APP_MAP,
                    Base64Utils.encodeToString(TracingContext.tracing().appMapString().getBytes(StandardCharsets.UTF_8)));
        }
    }

2、fegin实现请求拦截   RequestInterceptor可以实现对请求的拦截

@ConditionalOnClass(Feign.class)
@Component
@Order
public class FeignTracingTransmitter implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        //把请求头中的数据 关于groupid的信息放到tracingSetter 信息设置器中
        Tracings.transmit(requestTemplate::header);
    }
}
/**
     * 传输Tracing信息
     *
     * @param tracingSetter Tracing信息设置器
     */
    public static void transmit(TracingSetter tracingSetter) {
        if (TracingContext.tracing().hasGroup()) {
            log.debug("tracing transmit group:{}", TracingContext.tracing().groupId());

            //把信息存储到header中
            tracingSetter.set(TracingConstants.HEADER_KEY_GROUP_ID, TracingContext.tracing().groupId());
            tracingSetter.set(TracingConstants.HEADER_KEY_APP_MAP,
                    Base64Utils.encodeToString(TracingContext.tracing().appMapString().getBytes(StandardCharsets.UTF_8)));
        }
    }

二、从请求头中获取事务信息

spring boot中要实现拦截请求要用HandlerInterceptor,并且需要在WebMvcConfigurer把此拦截器注册到InterceptorRegistry。

@ConditionalOnClass(HandlerInterceptor.class)
@Component
public class SpringTracingApplier implements com.codingapi.txlcn.tracing.http.spring.HandlerInterceptor, WebMvcConfigurer {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        Tracings.apply(request::getHeader);
        return true;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this);
    }
}
/**
     * 获取传输的Tracing信息
     *
     * @param tracingGetter Tracing信息获取器
     */
    public static void apply(TracingGetter tracingGetter) {
        String groupId = Optional.ofNullable(tracingGetter.get(TracingConstants.HEADER_KEY_GROUP_ID)).orElse("");
        String appList = Optional.ofNullable(tracingGetter.get(TracingConstants.HEADER_KEY_APP_MAP)).orElse("");
       

        //事务组信息加载到本地上下文

        TracingContext.init(Maps.newHashMap(TracingConstants.GROUP_ID, groupId, TracingConstants.APP_MAP,
                StringUtils.isEmpty(appList) ? appList : new String(Base64Utils.decodeFromString(appList), StandardCharsets.UTF_8)));
        if (TracingContext.tracing().hasGroup()) {
            log.debug("tracing apply group:{}, app map:{}", groupId, appList);
        }
    }

 

你可能感兴趣的:(分布式事务,TX-LCN)