seata AT模式之tm注册到seata server的流程

tm什么时候注册到seata server的

  • seata的核心类GlobalTransactionScanner,也是一个BeanPostProcessor,如果使用的是seata-spring-boot-start.jar这个包,spring会给我们自动装配GlobalTransactionScanner,如果用的是seata-all.jar这个包,就需要我们手动把GlobalTransactionScanner注册到spring ioc上去。
  • GlobalTransactionScanner又实现了InitializingBean这个接口,spring ioc在给我们创建GlobalTransactionScanner这个bean的时候,就会调用它的
    afterPropertiesSet这个方法。
    @Override
    public void afterPropertiesSet() {
        if (disableGlobalTransaction) {
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("Global transaction is disabled.");
            }
            return;
        }
        initClient();
    }
  • initClient这个方法中就有去初始化TMClient和RMClient,这个方法里面我把一个日志打印和一些校验去掉了,留下主要内容。
    private void initClient() {
        //init TM
        TMClient.init(applicationId, txServiceGroup);
        //init RM
        RMClient.init(applicationId, txServiceGroup);
        registerSpringShutdownHook();
    }

今天只关注TM的初始化过程,RM的初始化过程基本与TM初始化过程类似。

  • TMClient的init方法,首先获取了TmRpcClient 实例,然后调用了tmRpcClient的init方法。
    public static void init(String applicationId, String transactionServiceGroup) {
        TmRpcClient tmRpcClient = TmRpcClient.getInstance(applicationId, transactionServiceGroup);
        tmRpcClient.init();
    }
  • TmRpcClient 的init方法,判断了有没有被初始化过,如果有就不再进行初始化,没有就调用了父类的init方法。
    @Override
    public void init() {
        // registry processor
        registerProcessor();
        if (initialized.compareAndSet(false, true)) {
            enableDegrade = CONFIG.getBoolean(ConfigurationKeys.SERVICE_PREFIX + ConfigurationKeys.ENABLE_DEGRADE_POSTFIX);
            super.init();
        }
    }
  • TmRpcClient 的父类AbstractRpcRemotingClient的init方法,第一步启动netty服务端;第二步启动了一个定时任务,这个定时任务会判断跟server端的连接是否还有效,如果已经断开,就会重新连上,并且重新把tm注册到server;第二步开启了一个线程池,并且又调用了父类的init方法。我们的关注点就是第二步的定时任务。RmRpcClient也继承了AbstractRpcRemotingClient。
    @Override
    public void init() {
        clientBootstrap.setChannelHandlers(new ClientHandler());
        clientBootstrap.start();
        timerExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                clientChannelManager.reconnect(getTransactionServiceGroup());
            }
        }, 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());
        }
        super.init();
    }
  • 这个定时任务是每10s执行一次,初始化60s后才开始执行。调用了clientChannelManager.reconnect方法,去掉校验看,先获取到seata server的地址,并且挨个调用acquireChannel方法
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;
        }
        for (String serverAddress : availList) {
            try {
                acquireChannel(serverAddress);
            } catch (Exception e) {
                LOGGER.error("{} can not connect to {} cause:{}",FrameworkErrorCode.NetConnect.getErrCode(), serverAddress, e.getMessage(), e);
            }
        }
    }
  • acquireChannel方法,首先查看channel是否已经存在并且是alive状态,如果是直接返回,如果否就创建一个把锁,调用doConnect方法。
 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);
        }
    }
  • doConnect,首先是获取到registerRMRequest 对象,然后从nettyClientKeyPool对象池中获取到channel,获取不到就会调用工厂去创建,在NettyClientChannelManager的构造函数中创建的nettyClientKeyPool对象,并把NettyPoolableFactory对象传给了它的构造函数。
 private Channel doConnect(String serverAddress) {
        Channel channelToServer = channels.get(serverAddress);
        if (channelToServer != null && channelToServer.isActive()) {
            return channelToServer;
        }
        Channel channelFromPool;
        try {
            NettyPoolKey currentPoolKey = poolKeyFunction.apply(serverAddress);
            NettyPoolKey previousPoolKey = poolKeyMap.putIfAbsent(serverAddress, currentPoolKey);
            if (previousPoolKey != null && previousPoolKey.getMessage() instanceof RegisterRMRequest) {
                RegisterRMRequest registerRMRequest = (RegisterRMRequest) currentPoolKey.getMessage();
                ((RegisterRMRequest) previousPoolKey.getMessage()).setResourceIds(registerRMRequest.getResourceIds());
            }
            channelFromPool = nettyClientKeyPool.borrowObject(poolKeyMap.get(serverAddress));
            channels.put(serverAddress, channelFromPool);
        } catch (Exception exx) {
            LOGGER.error("{} register RM failed.",FrameworkErrorCode.RegisterRM.getErrCode(), exx);
            throw new FrameworkException("can not register RM,err:" + exx.getMessage());
        }
        return channelFromPool;
    }
  • NettyPoolableFactory的makeObject方法,这里面真正去建立连接并把消息传送了出去
 @Override
    public Channel makeObject(NettyPoolKey key) {
        InetSocketAddress address = NetUtil.toInetSocketAddress(key.getAddress());
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("NettyPool create channel to " + key);
        }
        Channel tmpChannel = clientBootstrap.getNewChannel(address);
        long start = System.currentTimeMillis();
        Object response;
        Channel channelToServer = null;
        if (key.getMessage() == null) {
            throw new FrameworkException("register msg is null, role:" + key.getTransactionRole().name());
        }
        try {
            response = rpcRemotingClient.sendAsyncRequestWithResponse(tmpChannel, key.getMessage());
            if (!isRegisterSuccess(response, key.getTransactionRole())) {
                rpcRemotingClient.onRegisterMsgFail(key.getAddress(), tmpChannel, response, key.getMessage());
            } else {
                channelToServer = tmpChannel;
                rpcRemotingClient.onRegisterMsgSuccess(key.getAddress(), tmpChannel, response, key.getMessage());
            }
        } catch (Exception exx) {
            if (tmpChannel != null) {
                tmpChannel.close();
            }
            throw new FrameworkException(
                "register " + key.getTransactionRole().name() + " error, errMsg:" + exx.getMessage());
        }
        return channelToServer;
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的使用 Spring Cloud Seata AT 模式Java 代码实例: 首先,需要在 Spring Boot 项目的 pom.xml 文件中添加 Seata 的依赖: ```xml <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>1.4.1</version> </dependency> ``` 然后,在项目的 application.properties 文件中添加 Seata 的配置信息: ```properties # Seata配置信息 seata.tx-service-group=my_tx_group seata.enable-auto-data-source-proxy=true ``` 接着,在需要使用 Seata 的服务中,可以通过注解的方式进行事务管理: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override @GlobalTransactional(name = "create-user", rollbackFor = Exception.class) public void createUser(User user) { userDao.insert(user); } } ``` 在上述代码中,@GlobalTransactional 注解标记了事务的边界,表示这个方法是一个全局事务,并设置了事务的名称和回滚异常。 最后,需要在项目的启动类中添加 Seata 的配置: ```java @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @EnableTransactionManagement @EnableAutoDataSourceProxy public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } @Bean public GlobalTransactionScanner globalTransactionScanner() { return new GlobalTransactionScanner("user-service", "my_tx_group"); } } ``` 在上述代码中,@EnableTransactionManagement 注解开启了 Spring 的事务管理,@EnableAutoDataSourceProxy 注解开启了 Seata 的数据源代理,而 GlobalTransactionScanner 则是 Seata 的全局事务扫描器,用于处理全局事务的注册和注销。 以上就是一个简单的 Spring Cloud Seata AT 模式Java 代码实例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值