天天看点

Seata解析-TM、RM如何与TC集群通讯一、RM与集群通讯二、TM与集群通讯三、集群各机器间如何交互四、事务分组

本文基于seata 1.3.0版本

在AT模式下,服务端集群部署很简单,如果集群是在一台机器上,复制一份程序,修改一下端口号,启动即可。

因为集群部署很简单,本文不再对部署做介绍,本文向介绍一下TM、RM如何与集群建立连接,以及集群中各机器如何通讯。

文章目录

  • 一、RM与集群通讯
  • 二、TM与集群通讯
  • 三、集群各机器间如何交互
  • 四、事务分组

一、RM与集群通讯

RM启动的时候要向TC注册,根据事务分组从注册中心找到该分组对应的服务集群,之后与集群中的每台机器建立连接,并向每台机器注册RM。

与集群建立连接是在NettyClientChannelManager的reconnect方法中完成的,代码如下:

//该方法的入参是事务分组
	void reconnect(String transactionServiceGroup) {
        List<String> availList = null;
        try {
            //从注册中心获得与事务分组对应的集群中每台机器地址
            //transactionServiceGroup是事务分组,通过配置spring.cloud.alibaba.seat.tx-service-group指定
            availList = getAvailServerList(transactionServiceGroup);
        } catch (Exception e) {
            LOGGER.error("Failed to get available servers: {}", e.getMessage(), e);
            return;
        }
        //如果集群中没有机器提供服务,那么打印出日志,seata启动定时任务线程每过一段时间会重新查看集群中是否有机器
        if (CollectionUtils.isEmpty(availList)) {
            String serviceGroup = RegistryFactory.getInstance()
                                                 .getServiceGroup(transactionServiceGroup);
            LOGGER.error("no available service '{}' found, please make sure registry config correct", serviceGroup);
            return;
        }
        //遍历每台机器
        for (String serverAddress : availList) {
            try {
                //建立与TC的连接,并注册RM
                acquireChannel(serverAddress);
            } catch (Exception e) {
                LOGGER.error("{} can not connect to {} cause:{}",FrameworkErrorCode.NetConnect.getErrCode(), serverAddress, e.getMessage(), e);
            }
        }
    }
           

注意RM是与集群中的每台机器都建立连接,那么之后RM与TC通讯时,比如注册分支事务,RM是与集群中的一条机器通讯还是与所有的机器通讯?

答案是一台机器。

RM与TC通讯时,首先也是根据事务分组从注册中心找到集群机器列表,之后使用负载均衡策略,从列表中选出一台机器,因为已经与每台TC建立了连接,所以这里不用再建立连接而是直接使用。seata提供了两种负载均衡策略:随机和轮询。可以参见的代码AbstractNettyRemotingClient的sendSyncRequest方法。

二、TM与集群通讯

TM与集群通讯和RM基本是一致。

TM启动时,也是根据事务分组从注册中心找到集群机器列表,然后与每台机器建立连接,并向其注册TM信息。

启动后的通讯也是根据负载均衡策略与其中一台机器通讯。而且对于同一个全局事务,可能TM访问的机器前后是不同的。TM和RM与集群通讯使用的代码都是一样的。

三、集群各机器间如何交互

RM、TM在启动后都是与集群中一台机器通讯,而且就同一个全局事务前后交互的机器可能不同,那么事务数据如何在集群中共享?另外为什么RM或者TM注册必须向每台机器注册,而之后的交互则不用?

先来看第一个问题。服务端收到TM或者RM的请求后,如果是事务注册请求,则会将注册的事务数据写入文件、redis或者数据库,具体写入哪个可以使用store.mode配置。之后事务的提交、回滚或者查询都是根据事务XID从文件、redis或者数据库查询出事务数据,然后更新的。所以如果服务端集群部署,事务数据存储必须使用redis或者数据库。

第二个问题,为什么必须向每台机器都注册。因为注册信息是记录在内存中的,不写入数据库,而且各个机器之间也没有通过网络交换注册信息,注册信息记录在ChannelManager的IDENTIFIED_CHANNELS属性中。

四、事务分组

下面先详细介绍一下RM如何查找TC机器列表,这也是上面代码中方法getAvailServerList的执行过程。

  1. RM启动时,读取配置spring.cloud.alibaba.seat.tx-service-group,得到本RM所属事务分组,下面假设事务分组为my_test_tx_group;
  2. 将my_test_tx_group与service.vgroupMapping组合成service.vgroupMapping.my_test_tx_group,使用该字符串查找配置信息,找到以该字符串为key的配置,读取对应的value值,假设该value值为“default”;
  3. 在“default”前添加固定内容形成一个字符串,然后访问注册中心,从注册中心查找该字符串对应的TC机器列表,比如注册中心使用的是zk,TC启动时会在zk上建立一个/registry/zk/default的节点(节点名通过register.conf的cluster配置),并将自己的ip注册到该节点,之后RM启动,并找到“default”值,在“default”前拼接“/registry/zk”,然后访问zk上该节点,便可以获得TC机器列表。

通过上述流程可以看到,事务分组是一个与集群相关的概念,可以将若干个RM、TM、TC形成一个逻辑分组,相当于一个资源单位,可以做资源隔离。

事务分组并不是直接映射到TC列表,而是中间经过service.vgroupMapping配置中转,这样做的好处是,service.vgroupMapping配置可以放到配置中心,一旦TC集群发生故障,通过修改配置中心值,在不停机的情况下切换到另一个TC集群。