Seata客户端的启动过程 学习记录

Seata客户端的启动过程

1.自动装配4个配置类

image-20230611160505833

将在SpringBoot启动时往容器中添加4个类

1. 自动配置类 SeataAutoConfiguration

SeataAutoConfiguration将会往容器中添加两个bean

  1. failureHandler 事务处理失败执行器
  2. globalTransactionScanner

failureHandler

failureHandler 实现了 FailureHandler 接口,将在事务处理出现异常时,执行相应阶段失败时的方法,我们可以自定类实现此接口,

在失败时执行对应的方法,比如发送邮件通知。但是要注意的是,自定义的事务失败处理器的beanName必须叫failureHandler,否则GlobalTransactionScanner将不会添加到容器中。

image-20230611161812868

public interface FailureHandler {
    void onBeginFailure(GlobalTransaction tx, Throwable cause);

    void onCommitFailure(GlobalTransaction tx, Throwable cause);

    void onRollbackFailure(GlobalTransaction tx, Throwable originalException);

    void onRollbackRetrying(GlobalTransaction tx, Throwable originalException);

    void onTimeoutRollback(GlobalTransaction tx, Throwable originalException);
}

failureHandler 并且会启动周期任务,去检查事务状态是不是正常完成,否则将继续打印警告信息

 public void onBeginFailure(GlobalTransaction tx, Throwable cause) {
        LOGGER.warn("Failed to begin transaction. ", cause);
    }

    public void onCommitFailure(GlobalTransaction tx, Throwable cause) {
        LOGGER.warn("Failed to commit transaction[" + tx.getXid() + "]", cause);
        this.timer.newTimeout(new CheckTimerTask(tx, GlobalStatus.Committed), 10L, TimeUnit.SECONDS);
    }

    public void onRollbackFailure(GlobalTransaction tx, Throwable originalException) {
        LOGGER.warn("Failed to rollback transaction[" + tx.getXid() + "]", originalException);
        this.timer.newTimeout(new CheckTimerTask(tx, GlobalStatus.Rollbacked), 10L, TimeUnit.SECONDS);
    }

    public void onRollbackRetrying(GlobalTransaction tx, Throwable originalException) {
        StackTraceLogger.warn(LOGGER, originalException, "Retrying to rollback transaction[{}]", new String[]{tx.getXid()});
        this.timer.newTimeout(new CheckTimerTask(tx, GlobalStatus.RollbackRetrying), 10L, TimeUnit.SECONDS);
    }

    public void onTimeoutRollback(GlobalTransaction tx, Throwable originalException) {
        StackTraceLogger.warn(LOGGER, originalException, "Transaction timeout rollback[{}]", new String[]{tx.getXid()});
    }

globalTransactionScanner

实现了4个接口,继承了一个父类

image-20230611162354082

  1. AbstractAutoProxyCreator 创建代理类
  2. ConfigurationChangeListener 监听配置文件变更
  3. initializingBean 初始化bean执行回调方法
  4. ApplicationContextAware 获取spring容器
  5. DisposableBean 可丢弃bean

首先看initializingBean 的afterPropertiesSet()回调方法

image-20230611162628536

扫描二维码关注公众号,回复: 15743195 查看本文章

disableGlobalTransaction 默认是false,走 initClient方法

private void initClient() {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Initializing Global Transaction Clients ... ");
        }
        if (DEFAULT_TX_GROUP_OLD.equals(txServiceGroup)) {
            LOGGER.warn("the default value of seata.tx-service-group: {} has already changed to {} since Seata 1.5, " +
                    "please change your default configuration as soon as possible " +
                    "and we don't recommend you to use default tx-service-group's value provided by seata",
                    DEFAULT_TX_GROUP_OLD, DEFAULT_TX_GROUP);
        }
        if (StringUtils.isNullOrEmpty(applicationId) || StringUtils.isNullOrEmpty(txServiceGroup)) {
            throw new IllegalArgumentException(String.format("applicationId: %s, txServiceGroup: %s", applicationId, txServiceGroup));
        }
        //init TM
        TMClient.init(applicationId, txServiceGroup, accessKey, secretKey);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Transaction Manager Client is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
        }
        //init RM
        RMClient.init(applicationId, txServiceGroup);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Resource Manager is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
        }

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

    }

其中完成了TM与RM的初始化

首先看TM的初始化

image-20230611163340797

创建了TmNettyRemotingClient实列,而TmNettyRemotingClient 的成员变量 instance 则是一个TmNettyRemotingClient 对象,用于客户端与服务端通信的netty客户端,消息处理器为io.seata.core.rpc.netty.AbstractNettyRemotingClient.ClientHandler,同样也是一个双向消息处理器

    @Sharable
    class ClientHandler extends ChannelDuplexHandler {

        @Override
        public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
            if (!(msg instanceof RpcMessage)) {
                return;
            }
            processMessage(ctx, (RpcMessage) msg);
        }

        @Override
        public void channelWritabilityChanged(ChannelHandlerContext ctx) {
            synchronized (lock) {
                if (ctx.channel().isWritable()) {
                    lock.notifyAll();
                }
            }
            ctx.fireChannelWritabilityChanged();
        }
        
        ......
        
        }

调用了TmNettyRemotingClient.init方法

   @Override
    public void init() {
        // registry processor
        registerProcessor();
        if (initialized.compareAndSet(false, true)) {
            super.init();
            if (io.seata.common.util.StringUtils.isNotBlank(transactionServiceGroup)) {
                getClientChannelManager().reconnect(transactionServiceGroup);
            }
        }
    }

registerProcessor() 注册消息处理器,同样封装成Pair放入map中

super.registerProcessor(MessageType.TYPE_SEATA_MERGE_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_GLOBAL_BEGIN_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_GLOBAL_COMMIT_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_GLOBAL_REPORT_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_GLOBAL_ROLLBACK_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_GLOBAL_STATUS_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_REG_CLT_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_BATCH_RESULT_MSG, onResponseProcessor, null);

getClientChannelManager().reconnect(transactionServiceGroup)与服务端建立连接

void reconnect(String transactionServiceGroup) {
        List<String> availList = null;
        try {
            availList = getAvailServerList(transactionServiceGroup);
        } catch (Exception e) {
            LOGGER.error("Failed to get available servers: {}", e.getMessage(), e);
            return;
        }
        if (CollectionUtils.isEmpty(availList)) {
            RegistryService registryService = RegistryFactory.getInstance();
            String clusterName = registryService.getServiceGroup(transactionServiceGroup);

            if (StringUtils.isBlank(clusterName)) {
                LOGGER.error("can not get cluster name in registry config '{}{}', please make sure registry config correct",
                        ConfigurationKeys.SERVICE_GROUP_MAPPING_PREFIX,
                        transactionServiceGroup);
                return;
            }

            if (!(registryService instanceof FileRegistryServiceImpl)) {
                LOGGER.error("no available service found in cluster '{}', please make sure registry config correct and keep your seata server running", clusterName);
            }
            return;
        }
        Set<String> channelAddress = new HashSet<>(availList.size());
        try {
            for (String serverAddress : availList) {
                try {
                    acquireChannel(serverAddress);
                    channelAddress.add(serverAddress);
                } catch (Exception e) {
                    LOGGER.error("{} can not connect to {} cause:{}", FrameworkErrorCode.NetConnect.getErrCode(),
                        serverAddress, e.getMessage(), e);
                }
            }
        } finally {
            if (CollectionUtils.isNotEmpty(channelAddress)) {
                List<InetSocketAddress> aliveAddress = new ArrayList<>(channelAddress.size());
                for (String address : channelAddress) {
                    String[] array = address.split(":");
                    aliveAddress.add(new InetSocketAddress(array[0], Integer.parseInt(array[1])));
                }
                RegistryFactory.getInstance().refreshAliveLookup(transactionServiceGroup, aliveAddress);
            } else {
                RegistryFactory.getInstance().refreshAliveLookup(transactionServiceGroup, Collections.emptyList());
            }
        }
    }

seata服务收到TM注册消息后,会调用io.seata.core.rpc.processor.server.RegTmProcessor#onRegTmMessage,并给客户端返回注册成功的消息

    private void onRegTmMessage(ChannelHandlerContext ctx, RpcMessage rpcMessage) {
        RegisterTMRequest message = (RegisterTMRequest) rpcMessage.getBody();
        String ipAndPort = NetUtil.toStringAddress(ctx.channel().remoteAddress());
        Version.putChannelVersion(ctx.channel(), message.getVersion());
        boolean isSuccess = false;
        String errorInfo = StringUtils.EMPTY;
        try {
            if (null == checkAuthHandler || checkAuthHandler.regTransactionManagerCheckAuth(message)) {
                ChannelManager.registerTMChannel(message, ctx.channel());
                Version.putChannelVersion(ctx.channel(), message.getVersion());
                isSuccess = true;
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("TM checkAuth for client:{},vgroup:{},applicationId:{} is OK",
                        ipAndPort, message.getTransactionServiceGroup(), message.getApplicationId());
                }
            } else {
                if (LOGGER.isWarnEnabled()) {
                    LOGGER.warn("TM checkAuth for client:{},vgroup:{},applicationId:{} is FAIL",
                            ipAndPort, message.getTransactionServiceGroup(), message.getApplicationId());
                }
            }
        } catch (Exception exx) {
            isSuccess = false;
            errorInfo = exx.getMessage();
            LOGGER.error("TM register fail, error message:{}", errorInfo);
        }
        RegisterTMResponse response = new RegisterTMResponse(isSuccess);
        if (StringUtils.isNotEmpty(errorInfo)) {
            response.setMsg(errorInfo);
        }
        remotingServer.sendAsyncResponse(rpcMessage, ctx.channel(), response);
        if (isSuccess && LOGGER.isInfoEnabled()) {
            LOGGER.info("TM register success,message:{},channel:{},client version:{}", message, ctx.channel(),
                message.getVersion());
        }
    }

RM的初始化

    public static void init(String applicationId, String transactionServiceGroup) {
        //实例化RM的netty客户端
        RmNettyRemotingClient rmNettyRemotingClient = RmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup);
        //设置RM资源管理
        rmNettyRemotingClient.setResourceManager(DefaultResourceManager.get());
        //设置RM事务管理器
        rmNettyRemotingClient.setTransactionMessageHandler(DefaultRMHandler.get());
        rmNettyRemotingClient.init();
    }

ResourceManager 用于管理数据库资源

TransactionMessageHandler用来处理RM事务消息

rmNettyRemotingClient.init()方法中注册消息处理器,需要注意的是,此时并不会与seata服务器建立连接,因为此时还比没有管理数据库资源

    public void init() {
        // registry processor
        registerProcessor();
        if (initialized.compareAndSet(false, true)) {
            super.init();

            // Found one or more resources that were registered before initialization
            if (resourceManager != null
                    && !resourceManager.getManagedResources().isEmpty()
                    && StringUtils.isNotBlank(transactionServiceGroup)) {
                getClientChannelManager().reconnect(transactionServiceGroup);
            }
        }
    }

遍历四种模式下的资源管理器,获取其所管理的资源(此时全为空),因此并不会与seata服务端在此时建立连接。 RM与TC建立连接可以看后面创建代理数据源的过程。RM是用来管理代理数据源,当没有数据源的时候,也没必要建立连接,真正向TC注册RM的时机在初始化代理数据源后。image-20230611173320067

seata服务端io.seata.core.rpc.processor.server.RegRmProcessor#onRegRmMessage 返回RM注册成功消息

    private void onRegRmMessage(ChannelHandlerContext ctx, RpcMessage rpcMessage) {
        RegisterRMRequest message = (RegisterRMRequest) rpcMessage.getBody();
        String ipAndPort = NetUtil.toStringAddress(ctx.channel().remoteAddress());
        boolean isSuccess = false;
        String errorInfo = StringUtils.EMPTY;
        try {
            if (null == checkAuthHandler || checkAuthHandler.regResourceManagerCheckAuth(message)) {
                ChannelManager.registerRMChannel(message, ctx.channel());
                Version.putChannelVersion(ctx.channel(), message.getVersion());
                isSuccess = true;
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("RM checkAuth for client:{},vgroup:{},applicationId:{} is OK", ipAndPort, message.getTransactionServiceGroup(), message.getApplicationId());
                }
            } else {
                if (LOGGER.isWarnEnabled()) {
                    LOGGER.warn("RM checkAuth for client:{},vgroup:{},applicationId:{} is FAIL", ipAndPort, message.getTransactionServiceGroup(), message.getApplicationId());
                }
            }
        } catch (Exception exx) {
            isSuccess = false;
            errorInfo = exx.getMessage();
            LOGGER.error("RM register fail, error message:{}", errorInfo);
        }
        RegisterRMResponse response = new RegisterRMResponse(isSuccess);
        if (StringUtils.isNotEmpty(errorInfo)) {
            response.setMsg(errorInfo);
        }
        remotingServer.sendAsyncResponse(rpcMessage, ctx.channel(), response);
        if (isSuccess && LOGGER.isInfoEnabled()) {
            LOGGER.info("RM register success,message:{},channel:{},client version:{}", message, ctx.channel(),
                message.getVersion());
        }
    }

2.自动配置类 SeataDataSourceAutoConfiguration

此自动配置类只会往容器中添加一个Bean :SeataAutoDataSourceProxyCreator,来自动创建数据源代理对象

   @Bean({"seataAutoDataSourceProxyCreator"})
    @ConditionalOnMissingBean({SeataAutoDataSourceProxyCreator.class})
    public static SeataAutoDataSourceProxyCreator seataAutoDataSourceProxyCreator(SeataProperties seataProperties) {
        return new SeataAutoDataSourceProxyCreator(seataProperties.isUseJdkProxy(), seataProperties.getExcludesForAutoProxying(), seataProperties.getDataSourceProxyMode());
    }

SeataDataSourceAutoConfiguration继承org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator 抽象类,并且重写了父类的wrapIfNecessary方法

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
				//判断当前bean是否是数据源类,不是不需要代理,直接返回
        if (!(bean instanceof DataSource)) {
            return bean;
        } else {
            DataSource origin;
            //判断是否是数据源代理类,如果是,则不需要er 
            if (!(bean instanceof SeataDataSourceProxy)) {
            		//获取缓存代理
                Object enhancer = super.wrapIfNecessary(bean, beanName, cacheKey);
                //如果当前bean就是缓存代理,直接返回
                if (bean == enhancer) {
                    return bean;
                } else {
                		//创建代理数据源
                    origin = (DataSource)bean;
                    SeataDataSourceProxy proxy = this.buildProxy(origin, this.dataSourceProxyMode);
                    DataSourceProxyHolder.put(origin, proxy);
                    return enhancer;
                }
            } else {
                LOGGER.warn("Manually register SeataDataSourceProxy(or its subclass) bean is discouraged! bean name: {}", beanName);
                SeataDataSourceProxy proxy = (SeataDataSourceProxy)bean;
                origin = proxy.getTargetDataSource();
                Object originEnhancer = super.wrapIfNecessary(origin, beanName, cacheKey);
                if (origin == originEnhancer) {
                    return origin;
                } else {
                    DataSourceProxyHolder.put(origin, proxy);
                    return originEnhancer;
                }
            }
        }
    }

调用buildProxy方法将当前数据源封装到DataSourceProxy对象中

    SeataDataSourceProxy buildProxy(DataSource origin, String proxyMode) {
        if (BranchType.AT.name().equalsIgnoreCase(proxyMode)) {
            return new DataSourceProxy(origin);
        } else if (BranchType.XA.name().equalsIgnoreCase(proxyMode)) {
            return new DataSourceProxyXA(origin);
        } else {
            throw new IllegalArgumentException("Unknown dataSourceProxyMode: " + proxyMode);
        }
    }

并且跟进 构造,找到init方法

 private void init(DataSource dataSource, String resourceGroupId) {
        this.resourceGroupId = resourceGroupId;

        try {
            Connection connection = dataSource.getConnection();
            Throwable var4 = null;

            try {
                this.jdbcUrl = connection.getMetaData().getURL();
                this.dbType = JdbcUtils.getDbType(this.jdbcUrl);
                if ("oracle".equals(this.dbType)) {
                    this.userName = connection.getMetaData().getUserName();
                } else if ("mariadb".equals(this.dbType)) {
                    this.dbType = "mysql";
                }

                this.version = this.selectDbVersion(connection);
            } catch (Throwable var14) {
                var4 = var14;
                throw var14;
            } finally {
                if (connection != null) {
                    if (var4 != null) {
                        try {
                            connection.close();
                        } catch (Throwable var13) {
                            var4.addSuppressed(var13);
                        }
                    } else {
                        connection.close();
                    }
                }

            }
        } catch (SQLException var16) {
            throw new IllegalStateException("can not init dataSource", var16);
        }

        this.initResourceId();
        //向TC注册RM
        DefaultResourceManager.get().registerResource(this);
        if (ENABLE_TABLE_META_CHECKER_ENABLE) {
            this.tableMetaExecutor.scheduleAtFixedRate(() -> {
                try {
                    Connection connection = dataSource.getConnection();
                    Throwable var3 = null;

                    try {
                        TableMetaCacheFactory.getTableMetaCache(this.getDbType()).refresh(connection, this.getResourceId());
                    } catch (Throwable var13) {
                        var3 = var13;
                        throw var13;
                    } finally {
                        if (connection != null) {
                            if (var3 != null) {
                                try {
                                    connection.close();
                                } catch (Throwable var12) {
                                    var3.addSuppressed(var12);
                                }
                            } else {
                                connection.close();
                            }
                        }

                    }
                } catch (Exception var15) {
                }

            }, 0L, TABLE_META_CHECKER_INTERVAL, TimeUnit.MILLISECONDS);
        }

        RootContext.setDefaultBranchType(this.getBranchType());
    }

可以看到此时完成了RM向TC的注册

3.自动配置类 SeataHttpAutoConfiguration

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnWebApplication
@ConditionalOnMissingBean({SeataWebMvcConfigurer.class})
@ConditionalOnProperty(
    prefix = "seata.client.http",
    name = {"interceptor-enabled"},
    havingValue = "true",
    matchIfMissing = true
)
@AutoConfigureOrder(Integer.MAX_VALUE)
public class SeataHttpAutoConfiguration {
    public SeataHttpAutoConfiguration() {
    }

    @Bean
    @ConditionalOnClass(
        name = {"jakarta.servlet.http.HttpServletRequest"}
    )
    public JakartaSeataWebMvcConfigurer jakartaSeataWebMvcConfigurer() {
        return new JakartaSeataWebMvcConfigurer();
    }

    @Bean
    @ConditionalOnMissingBean({JakartaSeataWebMvcConfigurer.class})
    public SeataWebMvcConfigurer seataWebMvcConfigurer() {
        return new SeataWebMvcConfigurer();
    }
}
public class SeataWebMvcConfigurer implements WebMvcConfigurerAdapter {
    public SeataWebMvcConfigurer() {
    }

    public void addInterceptors(InterceptorRegistry registry) {
    		//添加拦截器
        registry.addInterceptor(new TransactionPropagationInterceptor());
    }
}

主要是为了在传递xid

image-20230611203840479

4.自动配置类SeataSagaAutoConfiguration

SeataSagaAutoConfiguration主要是为了出来saga模式下的分布式事务,基本不用跳过。

猜你喜欢

转载自blog.csdn.net/JAVAlife2021/article/details/131195726