seata分布式事务server模块源码分析

seata-server源码分析

简介

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案【快速了解seata】

本文所有源码分析均基于Seata1.4.0版本,如有不对之处欢迎指出。1.4.0版本的seata-server端是一个java应用,主入口是一个main函数io.seata.server.Server#main()

1、seata-server入口main()方法分析

public static void main(String[] args) throws IOException {
    // get port first, use to logback.xml
    int port = PortHelper.getPort(args);
    System.setProperty(ConfigurationKeys.SERVER_PORT, Integer.toString(port));
    // create logger
    final Logger logger = LoggerFactory.getLogger(Server.class);
    if (ContainerHelper.isRunningInContainer()) {
        logger.info("The server is running in container.");
    }
    //initialize the parameter parser
    //Note that the parameter parser should always be the first line to execute.
    //Because, here we need to parse the parameters needed for startup.
    /**
     * 1、参数解析,解析启动以及配置文件的各种配置参数
     */
    ParameterParser parameterParser = new ParameterParser(args);
    //initialize the metrics
    /**
     * 2、监控初始化,metrics相关,这里是使用SPI机制获取Registry实例对象
     */
    MetricsManager.get().init();
    /**
     * 3、把从配置文件中读取到的storeMode写入SystemProperty中,方便其他类使用
     */
    System.setProperty(ConfigurationKeys.STORE_MODE, parameterParser.getStoreMode());
    /**
     * 4、创建与RM、TM通信的RPC服务,NettyRemotingServer实例,NettyRemotingServer是一个基于        * Netty实现的Rpc框架,此时并未初始化
     */
    NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(WORKING_THREADS);
    //server port
    // 设置RPC服务监听端口
    nettyRemotingServer.setListenPort(parameterParser.getPort());
    /**
     * 5、UUIDGenerator初始化,UUIDGenerator基于雪花算法实现,用于生成全局事务、分支事务的id,多个      * Server实例配置不同的ServerNode,保证id的唯一性
     */
    UUIDGenerator.init(parameterParser.getServerNode());
    //log store mode : file, db, redis
    /**
     * 6、设置资源存储模式,SessionHolder负责事务日志(状态)的持久化存储,当前支持file、db、redis      * 三种存储模式,集群部署模式要使用db或redis模式
     */
    SessionHolder.init(parameterParser.getStoreMode());
    /**
     * 7、创建默认事务协调器,初始化DefaultCoordinator实例,DefaultCoordinator是TC的默认的核心      * 事务逻辑处理类,底层包含了AT、TCC、SAGA等不同事务类型的逻辑处理。
     */
    DefaultCoordinator coordinator = new DefaultCoordinator(nettyRemotingServer);
    coordinator.init();
    /**
     * 8、把协调器作为一个回调,传给Netty RPC模块,NettyRemotingServer是基于Netty实现的简化版的          * Rpc服务端,NettyRemotingServer初始化时主要做了两件事:
     * ①...
     * ②...
     */
    nettyRemotingServer.setHandler(coordinator);
    // register ShutdownHook
    //  9、注册JVM关闭构造函数
    ShutdownHook.getInstance().addDisposable(coordinator);
    ShutdownHook.getInstance().addDisposable(nettyRemotingServer);

    //127.0.0.1 and 0.0.0.0 are not valid here.
    if (NetUtil.isValidIp(parameterParser.getHost(), false)) {
        XID.setIpAddress(parameterParser.getHost());
    } else {
        XID.setIpAddress(NetUtil.getLocalIp());
    }
    XID.setPort(nettyRemotingServer.getListenPort());

    try {
        // 初始化Netty,开始监听端口并阻塞在这里,等待程序关闭
        nettyRemotingServer.init();
    } catch (Throwable e) {
        logger.error("nettyServer init error:{}", e.getMessage(), e);
        System.exit(-1);
    }

    System.exit(0);
}

2、参数解析ParameterParse初始化方法init()分析

首先看看参数解析io.seata.server.ParameterParser#init(),其实参数解析很简单主要是通过JCommander解析main函数中的args数组,不过在需要注意的是,由于Seata Server已经支持容器部署, 所以在容器环境启动参数的创建跟正常启动的参数是不同的。容器部署的启动参数需要通过System.getenv获取

private void init(String[] args) {
    try {
        /**
         * 判断是否运行在容器中,如果运行在容器中则配置从环境变量中获取
         */
        if (ContainerHelper.isRunningInContainer()) {
            this.seataEnv = ContainerHelper.getEnv();
            this.host = ContainerHelper.getHost();
            this.port = ContainerHelper.getPort();
            this.serverNode = ContainerHelper.getServerNode();
            this.storeMode = ContainerHelper.getStoreMode();
        } else {
            /**
             * 基于JCommander获取启动应用程序时配置的参数,JCommander通过注解、反射的方式把参数赋              * 值到当前类的字段上。
             */
            JCommander jCommander = JCommander.newBuilder().addObject(this).build();
            jCommander.parse(args);
            if (help) {
                jCommander.setProgramName(PROGRAM_NAME);
                jCommander.usage();
                System.exit(0);
            }
        }
        /**
         * serverNode用于雪花算中实例的唯一标识,需要保证唯一。如果没有指定基于当前服务器的IP随机生成
         */
        if (this.serverNode == null) {
            this.serverNode = IdWorker.initWorkerId();
        }
        if (StringUtils.isNotBlank(seataEnv)) {
            System.setProperty(ENV_PROPERTY_KEY, seataEnv);
        }
        if (StringUtils.isBlank(storeMode)) {
           /**
            * 这里用到一个重要的Configuration类,ParameterParser只负责获取ip、port、                       * storeMode等核心参数,其他的参数都是从Configuration中获取的。这里如果没有启动参数没有               * 指定storeMode,就从Configuration类中获取。
            */
            storeMode = ConfigurationFactory.getInstance().getConfig(ConfigurationKeys.STORE_MODE,
                                                                     SERVER_DEFAULT_STORE_MODE);
        }
    } catch (ParameterException e) {
        printError(e);
    }
}
2.1、配置工厂类ConfigurationFactory的静态代码块分析

在ParameterParser的init方法中第一次调用了ConfigurationFactory.getInstance()方法其实就是获取一个Configuration单例对象,Configuration负责初始化所有的其他配置参数信息,核心方法在buildConfiguration中,不过在buidlConfiguration方法前,ConfigurationFactory类有一段static代码块会先执行。

    static {
        load();
    }

    /**
     * ConfigurationFactory中的static代码块是从registry.conf中读取配置信息。
     * registry.conf中主有两个配置信息,注册中心和配置源,配置源用来指定其他更详细的配置项是file.conf或      * 者是apollo等其他配置源。
     * 所以registry.conf配置文件时必须的,registry.conf配置文件中指定其他详细配置的配置源,
     * 当前配置源支持file、zk、apollo、nacos、etcd3等。所以file.conf不是必须的,
     * 只有当设置配置源为file类型时才会读取file.conf文件中的内容。
     */
    private static void load() {
        // 获取配置文件的名称,默认为registry.conf
        String seataConfigName = System.getProperty(SYSTEM_PROPERTY_SEATA_CONFIG_NAME);
        if (seataConfigName == null) {
            seataConfigName = System.getenv(ENV_SEATA_CONFIG_NAME);
        }
        if (seataConfigName == null) {
            seataConfigName = REGISTRY_CONF_DEFAULT;
        }
        String envValue = System.getProperty(ENV_PROPERTY_KEY);
        if (envValue == null) {
            envValue = System.getenv(ENV_SYSTEM_KEY);
        }
        /**
         * 读取registry.conf文件的配置,构建基础的Configuration对象
         */
        Configuration configuration = (envValue == null) ? new FileConfiguration(seataConfigName,
                false) : new FileConfiguration(seataConfigName + "-" + envValue, false);
        Configuration extConfiguration = null;
        try {
            /**
             * ExtConfigurationProvider当前只有一个SpringBootConfigurationProvider实现类
             * 用于支持客户端SDK SpringBoot的配置文件方式,对于Server端来说这段逻辑可以忽略。
             */
            extConfiguration = EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration);
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("load Configuration:{}", extConfiguration == null ? configuration.getClass().getSimpleName()
                        : extConfiguration.getClass().getSimpleName());
            }
        } catch (EnhancedServiceNotFoundException ignore) {

        } catch (Exception e) {
            LOGGER.error("failed to load extConfiguration:{}", e.getMessage(), e);
        }
        CURRENT_FILE_INSTANCE = extConfiguration == null ? configuration : extConfiguration;
    }    




    /**
     * Gets instance.
     *
     * @return the instance
     */
    public static Configuration getInstance() {
        if (instance == null) {
            synchronized (Configuration.class) {
                if (instance == null) {
                    instance = buildConfiguration();
                }
            }
        }
        return instance;
    }
2.2、配置工厂类ConfigurationFactory的buildConfiguration()方法分析

ConfigurationFactory中的buildConfiguration就是根据registry.conf中设置的配置源来加载更多的配置项。

    private static Configuration buildConfiguration() {
        ConfigType configType;
        String configTypeName;
        try {
            /**
             * 从registry.conf配置文件中读取config.type字段值,并解析为枚举ConfigType
             */
            configTypeName = CURRENT_FILE_INSTANCE.getConfig(
                    ConfigurationKeys.FILE_ROOT_CONFIG + ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR
                            + ConfigurationKeys.FILE_ROOT_TYPE);

            if (StringUtils.isBlank(configTypeName)) {
                throw new NotSupportYetException("config type can not be null");
            }
            configType = ConfigType.getType(configTypeName);
        } catch (Exception e) {
            throw e;
        }
        Configuration extConfiguration = null;
        Configuration configuration;
        if (ConfigType.File == configType) {
            /**
             * 如果配置文件为file类型,则从registry.conf中读取config.file.name配置项,
             * 即file类型配置文件的路径,示例中默认为file.conf
             */
            String pathDataId = String.join(ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR,
                    ConfigurationKeys.FILE_ROOT_CONFIG, FILE_TYPE, NAME_KEY);
            String name = CURRENT_FILE_INSTANCE.getConfig(pathDataId);
            /**
             * 根据file配置文件的路径构建FileConfuguration对象
             */
            configuration = new FileConfiguration(name);
            try {
                /**
                 * configuration的额外扩展,也是只对客户端SpringBoot的SDK才生效
                 */
                extConfiguration = EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration);
                if (LOGGER.isInfoEnabled()) {
                    LOGGER.info("load Configuration:{}", extConfiguration == null
                            ? configuration.getClass().getSimpleName() : extConfiguration.getClass().getSimpleName());
                }
            } catch (EnhancedServiceNotFoundException ignore) {

            } catch (Exception e) {
                LOGGER.error("failed to load extConfiguration:{}", e.getMessage(), e);
            }
        } else {
            /**
             * 如果配置文件的类型不是file,如:nacos、zk等, 则通过SPI的方式生成对应的ConfigurationProvider对象
             */
            configuration = EnhancedServiceLoader
                    .load(ConfigurationProvider.class, Objects.requireNonNull(configType).name()).provide();
        }
        try {
            /**
             * ConfigurationCache是对Configuration做了一次层代理内存缓存,提升获取配置的性能
             */
            Configuration configurationCache;
            if (null != extConfiguration) {
                configurationCache = ConfigurationCache.getInstance().proxy(extConfiguration);
            } else {
                configurationCache = ConfigurationCache.getInstance().proxy(configuration);
            }
            if (null != configurationCache) {
                extConfiguration = configurationCache;
            }
        } catch (EnhancedServiceNotFoundException ignore) {

        } catch (Exception e) {
            LOGGER.error("failed to load configurationCacheProvider:{}", e.getMessage(), e);
        }
        return null == extConfiguration ? configuration : extConfiguration;
    }

3、Metrics监控MetricsManager初始化方法init()分析

/**
 * Metrics manager for init
 *
 * @author zhengyangyong
 */
public class MetricsManager {
    private static class SingletonHolder {
        private static MetricsManager INSTANCE = new MetricsManager();
    }

    public static final MetricsManager get() {
        return MetricsManager.SingletonHolder.INSTANCE;
    }

    private Registry registry;

    public Registry getRegistry() {
        return registry;
    }

    public void init() {
        boolean enabled = ConfigurationFactory.getInstance().getBoolean(
            ConfigurationKeys.METRICS_PREFIX + ConfigurationKeys.METRICS_ENABLED, false);
        if (enabled) {
            registry = RegistryFactory.getInstance();
            if (registry != null) {
                List<Exporter> exporters = ExporterFactory.getInstanceList();
                //only at least one metrics exporter implement had imported in pom then need register MetricsSubscriber
                if (exporters.size() != 0) {
                    exporters.forEach(exporter -> exporter.setRegistry(registry));
                    EventBusManager.get().register(new MetricsSubscriber(registry));
                }
            }
        }
    }
}

4、创建与RM、TM通信的RPC服务并设置监听端口

NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(WORKING_THREADS);

nettyRemotingServer.setListenPort(parameterParser.getPort());

nettyRemotingServer.setHandler(coordinator);

在这里插入图片描述

4.1RPC服务初始化流程具体分析
4.1.1实例化RPC远程服务
   /**
     * Instantiates a new Rpc remoting server.
     *
     * @param messageExecutor   the message executor
     */
    public NettyRemotingServer(ThreadPoolExecutor messageExecutor) {
        super(messageExecutor, new NettyServerConfig());
    }

子类实例化先实例化父类根据类图找到父类AbstractNettyRemotingServer

	public AbstractNettyRemotingServer(ThreadPoolExecutor messageExecutor, NettyServerConfig nettyServerConfig) {
        super(messageExecutor);
        serverBootstrap = new NettyServerBootstrap(nettyServerConfig);
        serverBootstrap.setChannelHandlers(new ServerHandler());
    }

继续找父类…

	public AbstractNettyRemoting(ThreadPoolExecutor messageExecutor) {
        this.messageExecutor = messageExecutor;
    }

然后接着执行AbstractNettyRemotingServer中的serverBootstrap = new NettyServerBootstrap(nettyServerConfig);serverBootstrap.setChannelHandlers(new ServerHandler());通过netty相关配置来实例化一个RPC Netty Server端,并为此RPC服务设置Channel管道处理器.

然后就到了main方法中的nettyRemotingServer.setListenPort(parameterParser.getPort());给Netty RPC服务设置监听端口以及设置默认协调器nettyRemotingServer.setHandler(coordinator);

【注意:此时NettyRemotingServer#init()还未执行,在5\6\7步骤执行之后才开始真正的初始化…

5、UUIDGenerator初始化

UUIDGenertor初始化接收一个serverNode参数,UUIDGenertor当前是使用了雪花算法来生成唯一Id,该serverNode用来保证多个seata-server实例生成的唯一id不重复。

public class UUIDGenerator {

    /**
     * Generate uuid long.
     *
     * @return the long
     */
    public static long generateUUID() {
        return IdWorker.getInstance().nextId();
    }

    /**
     * Init.
     *
     * @param serverNode the server node id
     */
    public static void init(Long serverNode) {
        /**
         * UUIDGenerator初始化接收一个serverNode参数,UUIDGenerator当前是使用了雪花算法来生成唯一Id,
         * 该serverNode用来保证多个seata-server实例生成的唯一id不重复。
         * UUIDGenerator是对IdWorker做了封装,唯一id的核心实现逻辑在IdWoker类中,IdWorker是一个雪花算法实现的
         */
        IdWorker.init(serverNode);
    }

}
5.1、IdWorker雪花算法实现Id生成器

UUIDGenerator是对IdWorker做了封装,唯一id的核心实现逻辑在IdWoker类中,IdWorker是一个雪花算法实现的。此处的IdWorker又是一个单例

public static IdWorker getInstance() {
    if (idWorker == null) {
        synchronized (IdWorker.class) {
            if (idWorker == null) {
                init(initWorkerId());
            }
        }
    }
    return idWorker;
}
public static void init(Long serverNodeId) {
    if (idWorker == null) {
        synchronized (IdWorker.class) {
            if (idWorker == null) {
                idWorker = new IdWorker(serverNodeId);
            }
        }
    }
}
/**
     * Get the next ID (the method is thread-safe)
     *
     * @return SnowflakeId
     */
public synchronized long nextId() {
    long timestamp = timeGen();

    if (timestamp < lastTimestamp) {
        throw new RuntimeException(String.format(
            "clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
    }

    if (lastTimestamp == timestamp) {
        sequence = (sequence + 1) & sequenceMask;
        if (sequence == 0) {
            timestamp = tilNextMillis(lastTimestamp);
        }
    } else {
        sequence = 0L;
    }
    lastTimestamp = timestamp;
    //雪花算法64位唯一id组成:第一位0+41位时间戳+10位workerId+12位自增序列化(同一时间戳内自增)
    return ((timestamp - twepoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence;
}

6、持久化存储事务日志(状态)的初始化init()方法

SessionHolder负责Session的持久化,一个Session对象对应一个事务,事务分为两种:全局事务(GlobalSession)和分支事务(BranchSession)。 SessionHolder.init(parameterParser.getStoreMode());SessionHolder支持file和db以及redis三种持久化方式,其中db和redis支持集群模式,推荐使用db。

public static void init(String mode) {
    if (StringUtils.isBlank(mode)) {
        mode = CONFIG.getConfig(ConfigurationKeys.STORE_MODE);
    }
    StoreMode storeMode = StoreMode.get(mode);
    if (StoreMode.DB.equals(storeMode)) {
        /**
         * 这里又用到了SPI的方式加载SessionManager,其实下面获取的四个SessionManager实例都是同一个类
         * DataBaseSessionManager的不同实例,只是给DataBaseSessionManager的构造函数传参不同。
         */
        ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName());
        ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(),
        new Object[] {ASYNC_COMMITTING_SESSION_MANAGER_NAME});
        RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(),
        new Object[] {RETRY_COMMITTING_SESSION_MANAGER_NAME});
        RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(),
        new Object[] {RETRY_ROLLBACKING_SESSION_MANAGER_NAME});
    } else if (StoreMode.FILE.equals(storeMode)) {
        String sessionStorePath = CONFIG.getConfig(ConfigurationKeys.STORE_FILE_DIR,
                                                   DEFAULT_SESSION_STORE_FILE_DIR);
        if (StringUtils.isBlank(sessionStorePath)) {
            throw new StoreException("the {store.file.dir} is empty.");
        }
        ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.getName(),
        new Object[] {ROOT_SESSION_MANAGER_NAME, sessionStorePath});
        ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.getName(),
        new Class[] {String.class, String.class}, new Object[] {ASYNC_COMMITTING_SESSION_MANAGER_NAME, null});
        RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.getName(),
        new Class[] {String.class, String.class}, new Object[] {RETRY_COMMITTING_SESSION_MANAGER_NAME, null});
        RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.getName(),
        new Class[] {String.class, String.class}, new Object[] {RETRY_ROLLBACKING_SESSION_MANAGER_NAME, null});
    } else if (StoreMode.REDIS.equals(storeMode)) {
        ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.REDIS.getName());
        ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class,StoreMode.REDIS.getName(), new Object[] {ASYNC_COMMITTING_SESSION_MANAGER_NAME});
        RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class,StoreMode.REDIS.getName(), new Object[] {RETRY_COMMITTING_SESSION_MANAGER_NAME});
        RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class,StoreMode.REDIS.getName(), new Object[] {RETRY_ROLLBACKING_SESSION_MANAGER_NAME});
    } else {
        // unknown store
        throw new IllegalArgumentException("unknown store mode:" + mode);
    }
    reload(storeMode);
}
6.1、SPI方式加载SessionManager【以DB方式为例】
io.seata.server.storage.file.session.FileSessionManager
io.seata.server.storage.db.session.DataBaseSessionManager
io.seata.server.storage.redis.session.RedisSessionManager

当持久化存储方式为DB的时候,以SPIEnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName());的方式加载的SessionManager为DataBaseSessionManager的实现。其中决定使用何种持久化方式的配置在file.conf中指定,即store.mode=db。

6.1.1、DataBaseSessionManager的初始化init()方法
    @Override
    public void init() {
        transactionStoreManager = DataBaseTransactionStoreManager.getInstance();
    }
6.1.2、DataBaseTransactionStoreManager的单例实现getInstance()方法
    /**
     * Get the instance.
     */
    public static DataBaseTransactionStoreManager getInstance() {
        if (instance == null) {
            synchronized (DataBaseTransactionStoreManager.class) {
                if (instance == null) {
                    instance = new DataBaseTransactionStoreManager();
                }
            }
        }
        return instance;
    }
6.1.3、DataBaseTransactionStoreManager的构造方法

在DataBaseTransactionStoreManager的构造方法中以SPI的EnhancedServiceLoader.load(DataSourceProvider.class, datasourceType).provide();的方式集成了DB模式下持久化存储数据源DataSourceProvider的实现DruidDataSourceProvider。其指定的配置文件位置为file.conf,当store.mode=db时,db中的store.db.datasource=druid决定加载哪种持久化实现。

io.seata.server.store.DbcpDataSourceProvider
io.seata.server.store.DruidDataSourceProvider
io.seata.server.store.HikariDataSourceProvider
    /**
     * Instantiates a new Database transaction store manager.
     */
    private DataBaseTransactionStoreManager() {
        logQueryLimit = CONFIG.getInt(ConfigurationKeys.STORE_DB_LOG_QUERY_LIMIT, DEFAULT_LOG_QUERY_LIMIT);
        String datasourceType = CONFIG.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE);
        //init dataSource
        DataSource logStoreDataSource = EnhancedServiceLoader.load(DataSourceProvider.class, datasourceType).provide();
        logStore = new LogStoreDataBaseDAO(logStoreDataSource);
    }

当通过SPI机制动态加载好DataSource后,又构建了持久化存储层logStore = new LogStoreDataBaseDAO(logStoreDataSource);通过LogStoreDataBaseDAO来持久化存储全局事务(GlobalSession)和分支事务(BranchSession)的以及更新事务状态和删除事务状态等.

注意:1.4版本持久化的全局事务和分支事务数据,在事务成功提交或成功回滚后会删除,所以这两张表数据一直是空

6.2、SPI方式加载LockManager【以DB方式为例】
io.seata.server.storage.db.lock.DataBaseLockManager
io.seata.server.storage.file.lock.FileLockManager
io.seata.server.storage.redis.lock.RedisLockManager

当持久化存储方式为DB的时候,以SPIio.seata.server.lock.LockManager的方式加载的LockManager。来持久化存储seata中分布式事务逻辑锁的概念,即【lock_table】的数据。其中决定使用何种持久化方式的配置在file.conf中指定,即store.mode=db。

具体机制同SessionManager,此处不再一一讲解。

7、创建并初始化默认事务协调器DefaultCoordinator实例

DefaultCoordinator是事务协调器的核心,如:开启、提交、回滚全局事务,注册、提交、回滚分支事务都是由DefaultCoordinator负责协调处理的。DefaultCoordinato通过RpcServer与远程的TM、RM通信来实现分支事务的提交、回滚等。此处稍后再具体分析。

    public DefaultCoordinator(RemotingServer remotingServer) {
        this.remotingServer = remotingServer;
        // DefaultCore封装了AT、TCC、Saga等分布式事务模式的具体实现类
        this.core = new DefaultCore(remotingServer);
    }

    /**
     * // init方法初始化了5个定时器,主要用于分布式事务的重试机制,
     * // 因为分布式环境的不稳定性会造成事务处于中间状态,
     * // 所以要通过不断的重试机制来实现事务的最终一致性。
     * // 下面的定时器除了undoLogDelete之外,其他的定时任务默认都是1秒执行一次。
     * Init.
     */
    public void init() {
        // 处理处于回滚状态可重试的事务
        retryRollbacking.scheduleAtFixedRate(() -> {
            try {
                handleRetryRollbacking();
            } catch (Exception e) {
                LOGGER.info("Exception retry rollbacking ... ", e);
            }
        }, 0, ROLLBACKING_RETRY_PERIOD, TimeUnit.MILLISECONDS);
        // 处理二阶段可以重试提交的状态可重试的事务
        retryCommitting.scheduleAtFixedRate(() -> {
            try {
                handleRetryCommitting();
            } catch (Exception e) {
                LOGGER.info("Exception retry committing ... ", e);
            }
        }, 0, COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS);
        // 处理异步提交的事务
        asyncCommitting.scheduleAtFixedRate(() -> {
            try {
                handleAsyncCommitting();
            } catch (Exception e) {
                LOGGER.info("Exception async committing ... ", e);
            }
        }, 0, ASYNC_COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS);
        // 检查事务的第一阶段已经超时的事务,设置事务状态为TimeoutRollbacking,
        // 该事务会由其他定时任务执行回滚操作
        timeoutCheck.scheduleAtFixedRate(() -> {
            try {
                timeoutCheck();
            } catch (Exception e) {
                LOGGER.info("Exception timeout checking ... ", e);
            }
        }, 0, TIMEOUT_RETRY_PERIOD, TimeUnit.MILLISECONDS);
        // 根据unlog的保存天数调用RM删除unlog
        undoLogDelete.scheduleAtFixedRate(() -> {
            try {
                undoLogDelete();
            } catch (Exception e) {
                LOGGER.info("Exception undoLog deleting ... ", e);
            }
        }, UNDO_LOG_DELAY_DELETE_PERIOD, UNDO_LOG_DELETE_PERIOD, TimeUnit.MILLISECONDS);
    }

8、初始化RPC服务NettyRemotingServer实例

NettyRemotingServer是基于Netty实现的简化版的Rpc服务端,NettyRemotingServer初始化时主要做了两件事:

  1. registerProcessor:注册与Client通信的Processor。
  2. super.init():super.init()方法中负责初始化Netty,并把当前实例的IP端口注册到注册中心中
8.1、NettyRemotingServer的init()方法
    @Override
    public void init() {
        // registry processor
//      registerProcessor:注册与Client通信的Processor。
        registerProcessor();
        if (initialized.compareAndSet(false, true)) {
//      super.init():super.init()方法中负责初始化Netty,并把当前实例的IP端口注册到注册中心中
            super.init();
        }
    }


    private void registerProcessor() {
        // 1. registry on request message processor

        // 1. 注册核心的ServerOnRequestProcessor,即与事务处理相关的Processor,
        // 如:全局事务开始、提交,分支事务注册、反馈当前状态等。
        // ServerOnRequestProcessor的构造函数中传入getHandler()返回的示例,这个handler
        // 就是前面提到的DefaultCoordinator,DefaultCoordinator是分布式事务的核心处理类
        ServerOnRequestProcessor onRequestProcessor =
            new ServerOnRequestProcessor(this, getHandler());
        super.registerProcessor(MessageType.TYPE_BRANCH_REGISTER, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_BRANCH_STATUS_REPORT, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_GLOBAL_BEGIN, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_GLOBAL_COMMIT, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_GLOBAL_LOCK_QUERY, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_GLOBAL_REPORT, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_GLOBAL_ROLLBACK, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_GLOBAL_STATUS, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_SEATA_MERGE, onRequestProcessor, messageExecutor);
        // 2. registry on response message processor

        // 2.注册ResponseProcessor,ResponseProcessor用于处理当Server端主动发起请求时,
        // Client端回复的消息,即Response。如:Server向Client端发送分支事务提交或者回滚的请求时,
        // Client返回提交/回滚的结果
        ServerOnResponseProcessor onResponseProcessor =
            new ServerOnResponseProcessor(getHandler(), getFutures());
        super.registerProcessor(MessageType.TYPE_BRANCH_COMMIT_RESULT, onResponseProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_BRANCH_ROLLBACK_RESULT, onResponseProcessor, messageExecutor);
        // 3. registry rm message processor

        // 3. Client端发起RM注册请求时对应的Processor
        RegRmProcessor regRmProcessor = new RegRmProcessor(this);
        super.registerProcessor(MessageType.TYPE_REG_RM, regRmProcessor, messageExecutor);
        // 4. registry tm message processor

        // 4. Client端发起TM注册请求时对应的Processor
        RegTmProcessor regTmProcessor = new RegTmProcessor(this);
        super.registerProcessor(MessageType.TYPE_REG_CLT, regTmProcessor, null);
        // 5. registry heartbeat message processor

        // 5. Client端发送心跳请求时对应的Processor
        ServerHeartbeatProcessor heartbeatMessageProcessor = new ServerHeartbeatProcessor(this);
        super.registerProcessor(MessageType.TYPE_HEARTBEAT_MSG, heartbeatMessageProcessor, null);
    }
8.2、AbstractNettyRemotingServer的init()方法

NettyRemotingServerinit()方法中有调用基类AbstractNettyRemotingServerinit()方法,代码如下:

public AbstractNettyRemotingServer(ThreadPoolExecutor messageExecutor, NettyServerConfig nettyServerConfig) {
    super(messageExecutor);
    serverBootstrap = new NettyServerBootstrap(nettyServerConfig);
    serverBootstrap.setChannelHandlers(new ServerHandler());
}   

@Override
public void init() {
    // super.init()方法中启动了一个定时清理超时Rpc请求的定时任务,3S执行一次。
    super.init();
    // 配置Netty Server端,开始监听端口。
    serverBootstrap.start();
}
8.3、NettyServerBootstrap的start()方法
    @Override
    public void start() {
        // Netty server端的常规配置,其中添加了两个ChannelHandler:
        // ProtocolV1Decoder、ProtocolV1Encoder,
        // 分别对应Seata自定义RPC协议的解码器和编码器
        this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupWorker)
            .channel(NettyServerConfig.SERVER_CHANNEL_CLAZZ)
            .option(ChannelOption.SO_BACKLOG, nettyServerConfig.getSoBackLogSize())
            .option(ChannelOption.SO_REUSEADDR, true)
            .childOption(ChannelOption.SO_KEEPALIVE, true)
            .childOption(ChannelOption.TCP_NODELAY, true)
            .childOption(ChannelOption.SO_SNDBUF, nettyServerConfig.getServerSocketSendBufSize())
            .childOption(ChannelOption.SO_RCVBUF, nettyServerConfig.getServerSocketResvBufSize())
            .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK,
                new WriteBufferWaterMark(nettyServerConfig.getWriteBufferLowWaterMark(),
                    nettyServerConfig.getWriteBufferHighWaterMark()))
            .localAddress(new InetSocketAddress(listenPort))
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) {
                    ch.pipeline().addLast(new IdleStateHandler(nettyServerConfig.getChannelMaxReadIdleSeconds(), 0, 0))
                        .addLast(new ProtocolV1Decoder())
                        .addLast(new ProtocolV1Encoder());
                    if (channelHandlers != null) {
                        addChannelPipelineLast(ch, channelHandlers);
                    }

                }
            });

        try {
            // 开始监听配置的端口
            ChannelFuture future = this.serverBootstrap.bind(listenPort).sync();
            LOGGER.info("Server started, listen port: {}", listenPort);
            // Netty启动成功之后把当前实例注册到registry.conf配置文件配置的注册中心上
            RegistryFactory.getInstance().register(new InetSocketAddress(XID.getIpAddress(), XID.getPort()));
            initialized.set(true);
            future.channel().closeFuture().sync();
        } catch (Exception exx) {
            throw new RuntimeException(exx);
        }

    }

未完待续…

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老谭酸菜面

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值