Seata-TM服务启动

588 阅读2分钟

我们知道seata分布式组件中支持AT/XA/TCC/Saga模式,在AT模式下有三个角色:

  • TC (Transaction Coordinator) 维护全局和分支事务的状态,驱动全局事务提交或回滚。
  • TM (Transaction Manager)-定义全局事务的范围:开始全局事务、提交或回滚全局事务
  • RM (Resource Manager)-管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚

本文对TM客户端启动源码进行分析,分为3点

1.seata-spring集成

2.启动netty服务

3.创建代理类

一 、seata-spring集成

这里是大家所熟悉的spring.factories即spring SPI自动装载SeataAutoConfiguration这个配置类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.seata.spring.boot.autoconfigure.SeataAutoConfiguration

了解spring-bean生命周期的同学对下面的内容应该不会陌生,通过@Bean注解将GlobalTransactionScanner注册到spring容器的beanDefinition中,并且在容器启动时初始化该bean

@Bean
    @DependsOn({BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER, BEAN_NAME_FAILURE_HANDLER})
    @ConditionalOnMissingBean(GlobalTransactionScanner.class)
    public GlobalTransactionScanner globalTransactionScanner(SeataProperties seataProperties, FailureHandler failureHandler) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Automatically configure Seata");
        }
        return new GlobalTransactionScanner(seataProperties.getApplicationId(), seataProperties.getTxServiceGroup(), failureHandler);
    }

GlobalTransactionScanner继承了AbstractAutoProxyCreator(对符合条件的bean进行代理)以及实现了InitializingBean接口

public class GlobalTransactionScanner extends AbstractAutoProxyCreator implements InitializingBean, ApplicationContextAware,
    DisposableBean

二、启动netty服务

我们知道实现了InitializingBean接口的bean需要重写afterPropertiesSet,在初始化bean时afterPropertiesSet方法会被调用

@Override
    public void afterPropertiesSet() {
        if (disableGlobalTransaction) {
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("Global transaction is disabled.");
            }
            return;
        }
        initClient(); // @1 
    }

代码@1: initClient()方法完成netty-client的启动并以及channel的注册,分为TMClient以及RMClient的初始化

 private void initClient() {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Initializing Global Transaction Clients ... ");
        }
        if (StringUtils.isNullOrEmpty(applicationId) || StringUtils.isNullOrEmpty(txServiceGroup)) {
            throw new IllegalArgumentException(String.format("applicationId: %s, txServiceGroup: %s", applicationId, txServiceGroup));
        }
        TMClient.init(applicationId, txServiceGroup); // @1
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Transaction Manager Client is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
        }
        //init RM
        RMClient.init(applicationId, txServiceGroup); // @2
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Resource Manager is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
        }

        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Global Transaction Clients are initialized. ");
        }
        registerSpringShutdownHook();
    }

代码@1: 启动TMClient

代码@2: 启动RMClient

io.seata.core.rpc.netty.AbstractNettyRemotingClient#init定时任务链接TC服务端

@Override
    public void init() {
        timerExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                clientChannelManager.reconnect(getTransactionServiceGroup()); // @1
            }
        }, SCHEDULE_DELAY_MILLS, SCHEDULE_INTERVAL_MILLS, TimeUnit.MILLISECONDS);
        if (NettyClientConfig.isEnableClientBatchSendRequest()) {
            mergeSendExecutorService = new ThreadPoolExecutor(MAX_MERGE_SEND_THREAD,
                    MAX_MERGE_SEND_THREAD,
                    KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(),
                    new NamedThreadFactory(getThreadPrefix(), MAX_MERGE_SEND_THREAD));
            //
            mergeSendExecutorService.submit(new MergedSendRunnable()); // @2
        }
        super.init();
        clientBootstrap.start();
    }

代码@1 :获取所有seata-server列表,建立链接

代码@2: 建立一个启动批量发送的线程MergedSendRunnable,具体在后面文章再分析seata的rpc消息发送

Channel acquireChannel(String serverAddress) {
        Channel channelToServer = channels.get(serverAddress);
        if (channelToServer != null) {
            channelToServer = getExistAliveChannel(channelToServer, serverAddress);
            if (channelToServer != null) {
                return channelToServer;
            }
        }
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("will connect to " + serverAddress);
        }
        channelLocks.putIfAbsent(serverAddress, new Object()); //创建一把锁
        synchronized (channelLocks.get(serverAddress)) { 
            return doConnect(serverAddress); // 建立链接
        }
    }

三、创建代理类

作为AbstractAutoProxyCreator的实现类并且重写wrapIfNecessary方法,由@GlobalTransactional修饰的bean, 在spring初始化时,BeanPostProcessor方法postProcessAfterInitialization被执行创建AOP代理类,@GlobalTransactional修饰的方法执行实际由GlobalTransactionalInterceptor.invoke执行。

@Override
    public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
        Class<?> targetClass =
            methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null;
        Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
        if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) {
            final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
            final GlobalTransactional globalTransactionalAnnotation =
                getAnnotation(method, targetClass, GlobalTransactional.class);
            final GlobalLock globalLockAnnotation = getAnnotation(method, targetClass, GlobalLock.class);
            boolean localDisable = disable || (degradeCheck && degradeNum >= degradeCheckAllowTimes);
            if (!localDisable) {
                if (globalTransactionalAnnotation != null) {
                    return handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation); // @1
                } else if (globalLockAnnotation != null) { 
                    return handleGlobalLock(methodInvocation); // @2
                }
            }
        }
        return methodInvocation.proceed();
    }

代码@1: @GlobalTransactional事务注解修饰的方法,会调用handleGlobalTransaction处理事务请求

代码@2: @GlobalLock注解修饰的方法,不用交给事务管理器处理,保证对数据的修改能够加入到seata机制当中;