事务参与方 - 969251639/study GitHub Wiki

从上图可以看到,事务发起方发起事务后,会传递事务组传递给参与方,下面以spring cloud为例

1. 发起方发送在头信息发送事务组
用springcloud其实很简单,不管用feign还是resttemplate都可以在springcloud发起http请求前通过拦截器设置头部信息

@ConditionalOnClass(Feign.class)
@Component
@Order
public class FeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        if (TracingContext.tracing().hasGroup()) {
            requestTemplate.header(TracingConstants.HEADER_KEY_GROUP_ID, TracingContext.tracing().groupId());
            requestTemplate.header(TracingConstants.HEADER_KEY_APP_MAP, TracingContext.tracing().appMapBase64String());
        }
    }
}

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

    @Override
    @NonNull
    public ClientHttpResponse intercept(
            @NonNull HttpRequest httpRequest, @NonNull byte[] bytes,
            @NonNull ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
        if (TracingContext.tracing().hasGroup()) {
            httpRequest.getHeaders().add(TracingConstants.HEADER_KEY_GROUP_ID, TracingContext.tracing().groupId());
            httpRequest.getHeaders().add(TracingConstants.HEADER_KEY_APP_MAP, TracingContext.tracing().appMapBase64String());
        }
        return clientHttpRequestExecution.execute(httpRequest, bytes);
    }
}

2. 参与方参与该事务组
参与方会从头部中获取事务组信息进行分布式参与。其实内部也是通过拦截器拦截在进入前将其设置到LCN事务的上下文中

@ConditionalOnClass(HandlerInterceptor.class)
@Component
public class TracingHandlerInterceptor implements com.codingapi.txlcn.tracing.http.spring.HandlerInterceptor, WebMvcConfigurer {
    ...
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String groupId = Optional.ofNullable(request.getHeader(TracingConstants.HEADER_KEY_GROUP_ID)).orElse("");
        String appList = Optional.ofNullable(request.getHeader(TracingConstants.HEADER_KEY_APP_MAP)).orElse("");
        TracingContext.tracing().init(Maps.newHashMap(TracingConstants.GROUP_ID, groupId, TracingConstants.APP_MAP, appList));//设置到事务上下文中
        return true;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this);
    }
}

这样,在进入事务处理时就可以拿到这个组信息

@Component
@Slf4j
public class DTXLogicWeaver {
    ...
    public Object runTransaction(DTXInfo dtxInfo, BusinessCallback business) throws Throwable {        
        ...
        DTXLocalContext dtxLocalContext = DTXLocalContext.getOrNew();
        TxContext txContext;
        if (globalContext.hasTxContext()) {//这里可以获取到事务组信息
            // 有事务上下文的获取父上下文
            txContext = globalContext.txContext();
            dtxLocalContext.setInGroup(true);
            log.debug("Unit[{}] used parent's TxContext[{}].", dtxInfo.getUnitId(), txContext.getGroupId());
        } else {
            // 没有的开启本地事务上下文
            txContext = globalContext.startTx();
        }
        ...
    }
}