[Interpretation of distributed transaction Seata source code 1] Server-side startup process

The core points of implementing distributed transactions:

  1. The persistence of the transaction, the various states of the transaction, the various states of the transaction participants need to be persisted, when the instance is down, the transaction can be rolled back or submitted based on the persistent data to achieve ultimate consistency
  2. Timed processing of unfinished transactions over time (continue to try to commit or roll back), that is, achieve the final consistency of the transaction through the retry mechanism
  3. Cross-service instance propagation of distributed transactions. When distributed transactions span multiple instances, transaction propagation needs to be realized. Generally, different rpc frameworks need to be adapted
  4. Transaction isolation level: For most distributed transactions for performance, the default isolation level is read uncommitted
  5. Idempotence: For distributed transactions such as XA or seata's AT, idempotence has been implemented by default, while distributed transactions implemented at the interface level of TCC and Saga require business developers to implement idempotence. Sex.

This article mainly introduces the source code of seata-server from the perspective of the startup process of seata-server. The startup flow chart is as follows:

Insert picture description here

1. Start class Server

The entry class of seata-server is in the Server class. The source code is as follows:

public static void main(String[] args) throws IOException {
    // 从环境变量或运行时参数中获取监听端口,默认端口8091
    int port = PortHelper.getPort(args);
    
    // 把监听端口设置到SystemProperty中,Logback的LoggerContextListener实现类
    // SystemPropertyLoggerContextListener会把Port写入到Logback的Context中,
    // 在logback.xml文件中会使用Port变量来构建日志文件名称。
    System.setProperty(ConfigurationKeys.SERVER_PORT, Integer.toString(port));

    // 创建Logger
    final Logger logger = LoggerFactory.getLogger(Server.class);
    if (ContainerHelper.isRunningInContainer()) {
        logger.info("The server is running in container.");
    }

    // 解析启动以及配置文件的各种配置参数
    ParameterParser parameterParser = new ParameterParser(args);

    // metrics相关,这里是使用SPI机制获取Registry实例对象
    MetricsManager.get().init();
    
	// 把从配置文件中读取到的storeMode写入SystemProperty中,方便其他类使用。
    System.setProperty(ConfigurationKeys.STORE_MODE, parameterParser.getStoreMode());
    
	// 创建NettyRemotingServer实例,NettyRemotingServer是一个基于Netty实现的Rpc框架,
	// 此时并没有初始化,NettyRemotingServer负责与客户端SDK中的TM、RM进行网络通信。
     nettyRemotingServer = new NettyRemotingServer(WORKING_THREADS);
    
    // 设置监听端口
    nettyRemotingServer.setListenPort(parameterParser.getPort());
    
	// UUIDGenerator初始化,UUIDGenerator基于雪花算法实现,
	// 用于生成全局事务、分支事务的id。
	// 多个Server实例配置不同的ServerNode,保证id的唯一性
    UUIDGenerator.init(parameterParser.getServerNode());
    
	// SessionHodler负责事务日志(状态)的持久化存储,
	// 当前支持file、db、redis三种存储模式,集群部署模式要使用db或redis模式
    SessionHolder.init(parameterParser.getStoreMode());
    
  	// 创建初始化DefaultCoordinator实例,DefaultCoordinator是TC的核心事务逻辑处理类,
  	// 底层包含了AT、TCC、SAGA等不同事务类型的逻辑处理。
    DefaultCoordinator coordinator = new DefaultCoordinator(nettyRemotingServer);
    coordinator.init();
    nettyRemotingServer.setHandler(coordinator);
    // register ShutdownHook
    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. Parse the configuration

The implementation code of parameter analysis is in the ParameterParser class, and the source code of the init method is as follows:

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用于雪花算中的实例的唯一标识,需要保证唯一。
       // 如果没有指定基于当前服务器的I随机生成一个
       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);
   }

}

The ConfigurationFactory.getInstance() is called for the first time in the init method of ParameterParser, which initializes a singleton Configuration object, and Configuration is responsible for initializing all other configuration parameter information. From the source code of the Seata Server side, we can see two configuration files file.conf and registry.conf. So what is the difference between these two configuration files? Are both files necessary? We continue to look at the code.

The ConfigurationFactory.getInstance method is actually to obtain a singleton object, the core is in the buildConfiguration method, but before the buidlConfiguration method, a static code block of the ConfigurationFactory class will be executed first.

// 获取Configuration的单例对象
public static Configuration getInstance() {
    if (instance == null) {
        synchronized (Configuration.class) {
            if (instance == null) {
                instance = buildConfiguration();
            }
        }
    }
    return instance;
}

// ConfigurationFactory的static代码块
static {
    // 获取配置文件的名称,默认为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_PREFIX;
    }
    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 + REGISTRY_CONF_SUFFIX,
        false) : new FileConfiguration(seataConfigName + "-" + envValue + REGISTRY_CONF_SUFFIX, 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;
}

The static code block in ConfigurationFactory reads configuration information from registry.conf. There are two configuration information in registry.conf , the registry and the configuration source . The configuration source is used to specify other more detailed configuration items such as file.conf or other configuration sources such as apollo. Therefore, the registry.conf configuration file is necessary, and other detailed configuration configuration sources are specified in the registry.conf configuration file. The current configuration source supports file, zk, apollo, nacos, etcd3, etc. Therefore, file.conf is not necessary. The content in file.conf will only be read when the configuration source is set to file type.

Next, buildConfiguration in ConfigurationFactory loads more configuration items according to the configuration source set in 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. Initialize UUIDGenerator

UUIDGenertor initializes and receives a serverNode parameter. UUIDGenertor currently uses the snowflake algorithm to generate a unique ID. The serverNode is used to ensure that the unique IDs generated by multiple seata-server instances are not repeated.

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) {
        IdWorker.init(serverNode);
    }
}

UUIDGenerator encapsulates IdWorker, the core implementation logic of unique id is in the IdWoker class, and IdWorker is implemented by a snowflake algorithm. Here IdWorker is another singleton

public class IdWorker
/**
     * Constructor
     *
     * @param workerId就是上面提到的ServerNode, 取值范围在0·1023,也就是在64位的UUID中占10位
     */
    public IdWorker(long workerId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(
                String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        this.workerId = workerId;
    }

    /**
     * Get the next ID (the method is thread-safe)
     *
     * @return SnowflakeId
     */
    public 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));
        }

        synchronized (this) {
            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;
    }

4. SessionHolder initialization

SessionHolder is responsible for the persistence of Session. A Session object corresponds to a transaction. There are two types of transactions: GlobalSession and BranchSession. SessionHolder supports two persistence methods: file and db, among which db supports cluster mode, and db is recommended. The four main fields in SessionHolder are as follows:

// ROOT_SESSION_MANAGER用于获取所有的Setssion,以及Session的创建、更新、删除等。
private static SessionManager ROOT_SESSION_MANAGER;
// 用于获取、更新所有的异步commit的Session
private static SessionManager ASYNC_COMMITTING_SESSION_MANAGER;
// 用于获取、更新所有需要重试commit的Session
private static SessionManager RETRY_COMMITTING_SESSION_MANAGER;
// 用于获取、更新所有需要重试rollback的Session
private static SessionManager RETRY_ROLLBACKING_SESSION_MANAGER;

SessionHolder's init method

public static void init(String mode) throws IOException {
    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)) {
        //file模式可以先不关心
        ...
    } else {
        throw new IllegalArgumentException("unknown store mode:" + mode);
    }
    // reload方法对于db模式可以忽略
    reload();
}

As you can see above, the four SessionManagers in SessionHolder are essentially instances of the class DataBaseSessionManager, but they pass different parameters to the constructor. Look at the definition of DataBaseSessionManager:

public DataBaseSessionManager(String name) {
	super();
	this.taskName = name;
}

// 根据实例的taskName来决定allSessions返回的事务列表,
// 如taskName等于ASYNC_COMMITTING_SESSION_MANAGER_NAME的
// 就返回所有状态为AsyncCommitting的事务。
public Collection<GlobalSession> allSessions() {
	// get by taskName
	if (SessionHolder.ASYNC_COMMITTING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) {
			return findGlobalSessions(new SessionCondition(GlobalStatus.AsyncCommitting));
	} else if (SessionHolder.RETRY_COMMITTING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) {
			return findGlobalSessions(new SessionCondition(new GlobalStatus[] {GlobalStatus.CommitRetrying}));
	} else if (SessionHolder.RETRY_ROLLBACKING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) {
			return findGlobalSessions(new SessionCondition(new GlobalStatus[] {GlobalStatus.RollbackRetrying,
					GlobalStatus.Rollbacking, GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying}));
	} else {
		// taskName为null,则对应ROOT_SESSION_MANAGER,即获取所有状态的事务
		return findGlobalSessions(new SessionCondition(new GlobalStatus[] {
				GlobalStatus.UnKnown, GlobalStatus.Begin,
				GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking,
				GlobalStatus.RollbackRetrying,
				GlobalStatus.TimeoutRollbacking, 
				GlobalStatus.TimeoutRollbackRetrying,
				GlobalStatus.AsyncCommitting}));
	}
}

5. Initialize DefaultCoordinator

DefaultCoordinator is the core of the transaction coordinator, such as: opening, committing, and rolling back global transactions, registration, committing, and rolling back branch transactions are all coordinated and processed by DefaultCoordinator. DefaultCoordinato communicates with remote TM and RM through RpcServer to realize the commit and rollback of branch transactions.

public DefaultCoordinator(ServerMessageSender messageSender) {
	// 接口messageSender的实现类就是上文提到的RpcServer
	this.messageSender = messageSender;
	
	// DefaultCore封装了AT、TCC、Saga等分布式事务模式的具体实现类
	this.core = new DefaultCore(messageSender);
}

// init方法初始化了5个定时器,主要用于分布式事务的重试机制,
// 因为分布式环境的不稳定性会造成事务处于中间状态,
// 所以要通过不断的重试机制来实现事务的最终一致性。
// 下面的定时器除了undoLogDelete之外,其他的定时任务默认都是1秒执行一次。
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);
}

6. Initialize NettyRemotingServer

NettyRemotingServer is a simplified version of Rpc server based on Netty implementation. When NettyRemotingServer is initialized, it mainly does two things:

  1. registerProcessor : Register the processor communicating with the Client.
  2. super.init() : The super.init() method is responsible for initializing Netty and registering the IP port of the current instance in the registry
public void init() {
    // registry processor
    registerProcessor();
    if (initialized.compareAndSet(false, true)) {
        super.init();
    }
}

private void registerProcessor() {
	// 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.注册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. Client端发起RM注册请求时对应的Processor
	RegRmProcessor regRmProcessor = new RegRmProcessor(this);
	super.registerProcessor(MessageType.TYPE_REG_RM, regRmProcessor, messageExecutor);
	
	// 4. Client端发起TM注册请求时对应的Processor
	RegTmProcessor regTmProcessor = new RegTmProcessor(this);
	super.registerProcessor(MessageType.TYPE_REG_CLT, regTmProcessor, null);
	
	// 5. Client端发送心跳请求时对应的Processor
	ServerHeartbeatProcessor heartbeatMessageProcessor = new ServerHeartbeatProcessor(this);
	super.registerProcessor(MessageType.TYPE_HEARTBEAT_MSG, heartbeatMessageProcessor, null);
}

In NettyRemotingServer there is an init method that calls the base class AbstractNettyRemotingServer, the code is as follows:

public void init() {
	// super.init()方法中启动了一个定时清理超时Rpc请求的定时任务,3S执行一次。
    super.init();
	// 配置Netty Server端,开始监听端口。
    serverBootstrap.start();
}

// serverBootstrap.start();
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);
    }
}

Original: https://seata.io/zh-cn/blog/seata-sourcecode-server-bootstrap.html

●The strongest Tomcat8 performance optimization in history

Why can Alibaba resist 10 billion in 90 seconds? --The evolution of server-side high-concurrency distributed architecture

B2B e-commerce platform--ChinaPay UnionPay electronic payment function

Learn Zookeeper distributed lock, let interviewers look at you with admiration

SpringCloud e-commerce spike microservice-Redisson distributed lock solution

Check out more good articles, enter the official account--please me--excellent in the past

A deep and soulful public account 0.0

 

Guess you like

Origin blog.csdn.net/a1036645146/article/details/108800345