分布式事务XID是如何将所有微服务串联起来的?| 🏆 技术专题第五期征文 ......

2,293 阅读3分钟

今天Solomon_肖哥弹架构,来跟大家聊聊XID如何在所有微服务中进行传递,利用了什么技术完成该任务,该问题在微服务面试中属于高频题。

分布式事务XID如何传递

Solomon_肖哥弹架构

Seata 框架的XID传递策略

分布式事务XID在Dubbo中的传递

XID上下文容器结构

Dubbo XID整合核心代码

"ApacheDubboTransactionPropagationFilter"

@Activate(group = {Constants.PROVIDER, Constants.CONSUMER}, order = 100)
public class TransactionPropagationFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        //获取本地XID
        String xid = RootContext.getXID();
        String xidInterceptorType = RootContext.getXIDInterceptorType();
        //获取Dubbo隐式传参中的XID
        String rpcXid = getRpcXid();
        String rpcXidInterceptorType = RpcContext.getContext().getAttachment(RootContext.KEY_XID_INTERCEPTOR_TYPE);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("xid in RootContext[{}] xid in RpcContext[{}]", xid, rpcXid);
        }
        boolean bind = false;
        if (xid != null) {
            //传递XID
            RpcContext.getContext().setAttachment(RootContext.KEY_XID, xid);
            RpcContext.getContext().setAttachment(RootContext.KEY_XID_INTERCEPTOR_TYPE, xidInterceptorType);
        } else {
            if (rpcXid != null) {
                //绑定XID
                RootContext.bind(rpcXid);
                RootContext.bindInterceptorType(rpcXidInterceptorType);
                bind = true;
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("bind[{}] interceptorType[{}] to RootContext", rpcXid, rpcXidInterceptorType);
                }
            }
        }
        try {
            return invoker.invoke(invocation);
        } finally {
            if (bind) {
                //进行剔除已完成事务的XID
                String unbindInterceptorType = RootContext.unbindInterceptorType();
                String unbindXid = RootContext.unbind();
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("unbind[{}] interceptorType[{}] from RootContext", unbindXid, unbindInterceptorType);
                }
                //如果发现解绑的XID并不是当前接收到的XID
                if (!rpcXid.equalsIgnoreCase(unbindXid)) {
                    LOGGER.warn("xid in change during RPC from {} to {}, xidInterceptorType from {} to {} ", rpcXid, unbindXid, rpcXidInterceptorType, unbindInterceptorType);
                    if (unbindXid != null) {
                        //重新绑定XID
                        RootContext.bind(unbindXid);
                        RootContext.bindInterceptorType(unbindInterceptorType);
                        LOGGER.warn("bind [{}] interceptorType[{}] back to RootContext", unbindXid, unbindInterceptorType);
                    }
                }
            }
        }
    }

    /**
     * get rpc xid
     * @return
     */
    private String getRpcXid() {
        String rpcXid = RpcContext.getContext().getAttachment(RootContext.KEY_XID);
        if (rpcXid == null) {
            rpcXid = RpcContext.getContext().getAttachment(RootContext.KEY_XID.toLowerCase());
        }
        return rpcXid;
    }

}

SPI 整合方式

io.seata.integration.dubbo.ApacheDubboTransactionPropagationFilter

分布式事务XID在 Http中的传递

Spring Servlet拦截器整合方式

Http 服务XID整合核心代码

/**
*事务传递拦截器
**/
public class TransactionPropagationIntercepter extends HandlerInterceptorAdapter {

    //前置处理
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        //上下文获取xid
        String xid = RootContext.getXID();
        //请求头获取远程XID
        String rpcXid = request.getHeader(RootContext.KEY_XID);

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("xid in RootContext[{}] xid in HttpContext[{}]", xid, rpcXid);
        }
        if (rpcXid != null) {
            //上下文绑定
            RootContext.bind(rpcXid);
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("bind[{}] to RootContext", rpcXid);
            }
        }
        return true;
    }

    //后置处理
    public void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {      
        //清理XID
        XidResource.cleanXid(request.getHeader(RootContext.KEY_XID));
    }
}

Spring 工厂配置

SPI 整合方式

# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.seata.integration.http.HttpAutoConfiguration

分布式事务XID在 Motan中的传递

Motan XID 整合核心代码

@Spi(scope = Scope.SINGLETON)
@Activation(key = {MotanConstants.NODE_TYPE_SERVICE, MotanConstants.NODE_TYPE_REFERER}, sequence = 100)
public class MotanTransactionFilter implements Filter {

    public MotanTransactionFilter(){}
    @Override
    public Response filter(final Caller<?> caller, final Request request) {
        //从上下文中获取已有的XID
        String currentXid = RootContext.getXID();
        //获取远程传递的XID
        String requestXid = getRpcXid(request);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("xid in RootContext [" + currentXid + "] xid in Request [" + requestXid + "]");
        }
        boolean bind = false;
        if (currentXid != null) {
            //从请求对象中获取XID
            request.getAttachments().put(RootContext.KEY_XID, currentXid);
        } else if (null != requestXid) {
            //上下文绑定XID
            RootContext.bind(requestXid);
            bind = true;

        }
        try {
            //远程调用
            return caller.call(request);
        } finally {
            if (bind) {
                //上下文解绑XID
                String unbindXid = RootContext.unbind();
                if (!requestXid.equalsIgnoreCase(unbindXid)) {
                    if (unbindXid != null) {
                        RootContext.bind(unbindXid);
                        LOGGER.warn("bind [" + unbindXid + "] back to RootContext");
                    }
                }
            }
        }
    }

    /**
     * 获取 rpc xid
     * @param request
     * @return
     */
    private String getRpcXid(Request request) {
        String rpcXid = request.getAttachments().get(RootContext.KEY_XID);
        if (rpcXid == null) {
            rpcXid = request.getAttachments().get(RootContext.KEY_XID.toLowerCase());
        }
        return rpcXid;
    }

}

SPI 整合方式

#拦截器配置

io.seata.integration.motan.MotanTransactionFilter

分布式事务XID在 Sofa-rpc中的传递

Sofa-rpc 服务提供者XID 整合核心代码

@Extension(value = "transactionContextProvider")
@AutoActive(providerSide = true)
public class TransactionContextProviderFilter extends Filter {
    @Override
    public SofaResponse invoke(FilterInvoker filterInvoker, SofaRequest sofaRequest) throws SofaRpcException {
        //从上下文中获取XID
        String xid = RootContext.getXID();
        //从请求中获取XID
        String rpcXid = getRpcXid(sofaRequest);
        boolean bind = false;
        if (xid != null) {
            //从RPC上下文中设置XID
            RpcInternalContext.getContext().setAttachment(RootContext.KEY_XID, xid);
        } else {
            if (rpcXid != null) {
                //上下文绑定XID
                RootContext.bind(rpcXid);
                bind = true;
            }
        }
        try {
            //远程调用
            return filterInvoker.invoke(sofaRequest);
        } finally {
            if (bind) {
                //上下文释放XID
                String unbindXid = RootContext.unbind();
                if (!rpcXid.equalsIgnoreCase(unbindXid)) {
                    if (unbindXid != null) {
                        RootContext.bind(unbindXid);
                    }
                }
            }
        }
    }

    /**
     * 获取 rpc xid
     * @return
     */
    private String getRpcXid(SofaRequest sofaRequest) {
        String rpcXid = (String) sofaRequest.getRequestProp(RootContext.KEY_XID);
        if (rpcXid == null) {
            rpcXid = (String) sofaRequest.getRequestProp(RootContext.KEY_XID.toLowerCase());
        }
        return rpcXid;
    }

}

Sofa-rpc 服务消费者XID 整合核心代码

@Extension(value = "transactionContextConsumer")
@AutoActive(consumerSide = true)
public class TransactionContextConsumerFilter extends Filter {
 
    @Override
    public SofaResponse invoke(FilterInvoker filterInvoker, SofaRequest sofaRequest) throws SofaRpcException {
        //获取已有上下文中的XID
        String xid = RootContext.getXID();
        //从请求中获取XID
        String rpcXid = getRpcXid();
 
        boolean bind = false;
        if (xid != null) {
            //请求对象获取XID
            sofaRequest.addRequestProp(RootContext.KEY_XID, xid);
        } else {
            if (rpcXid != null) {
                //上下文绑定XID
                RootContext.bind(rpcXid);
                bind = true;
            }
        }
        try {
            //远程调用
            return filterInvoker.invoke(sofaRequest);
        } finally {
            if (bind) {
                //上下文释放XID绑定
                String unbindXid = RootContext.unbind();
                if (!rpcXid.equalsIgnoreCase(unbindXid)) {
                    if (unbindXid != null) {
                        RootContext.bind(unbindXid);
                    }
                }
            }
        }
    }

    /**
     * 获取 rpc xid
     * @return
     */
    private String getRpcXid() {
        String rpcXid = (String) RpcInternalContext.getContext().getAttachment(RootContext.KEY_XID);
        if (rpcXid == null) {
            rpcXid = (String) RpcInternalContext.getContext().getAttachment(RootContext.KEY_XID.toLowerCase());
        }
        return rpcXid;
    }

}

SPI 整合方式

io.seata.integration.sofa.rpc.TransactionContextProviderFilter
io.seata.integration.sofa.rpc.TransactionContextConsumerFilter

你的点赞就是对我最好的支持与动力。 关注Solomon_肖哥弹架构,后续努力推出优质的内容。

🏆 技术专题第五期 | 聊聊分布式的那些事......