Seata解析-seata服务端启动初探

x33g5p2x  于2021-12-21 转载在 其他  
字(3.6k)|赞(0)|评价(0)|浏览(942)

本文基于seata 1.3.0版本

文章《Seata解析-seata部署启动初体验》介绍了seata的功能。seata提供四种方式解决了分布式事务问题,seata使用客户端-服务器模式,TM和RM作为客户端,TC作为事务协调者部署在服务端。
seata的服务端独立部署,功能相对简单,本文先来简单分析一下服务端,先尝尝鲜。

一、服务端的启动脚本

seata服务端启动需要使用启动脚本,启动脚本位于bin目录下:

第一个文件是Windows系统使用,第二个文件是Linux系统使用。两个文件都是设置一些启动参数,然后执行java的启动类。我们来看一下seata-server.sh文件的最后一部分:

exec "$JAVACMD" $JAVA_OPTS -server -XX:MaxDirectMemorySize=1024M \
  -classpath "$CLASSPATH" \
  -Dapp.name="seata-server" \
  -Dapp.pid="$$" \
  -Dapp.repo="$REPO" \
  -Dapp.home="$BASEDIR" \
  -Dbasedir="$BASEDIR" \
  io.seata.server.Server \
  "$@"

seata-server.sh文件前面的内容是设置参数BASEDIR、REPO和CLASSPATH的值,有兴趣的可以看一下。从上面的代码可以看出,seata-server.sh设置了直接内存大小。而且最重要的指定了服务端的启动类io.seata.server.Server。下一节来分析一下这个启动类。

二、服务端启动类Server

Server类的启动入口为其main方法。
首先介绍一下main方法的流程:

  1. 解析启动参数,启动参数指的是seata-server.sh命令后面跟的参数;
  2. 初始化统计模块;
  3. 创建NettyRemotingServer对象,并设置端口号;
  4. 初始化UUID生成器;
  5. 创建会话管理器,在seata中,一个会话就代表了一个事务;
  6. 创建协调器,并初始化,将NettyRemotingServer对象与协调器关联起来,当NettyRemotingServer收到请求后会转发给协调器处理;
  7. 注册JVM关闭钩子;
  8. 设置生成XID的参数;
  9. 初始化NettyRemotingServer对象,该对象初始化完毕之后便可以接收客户端的请求,main方法的处理也到此为止。

从流程上可以看到,服务端最重要的组件是: NettyRemotingServer和协调器、会话管理器,后面会详细介绍它们的作用。
下面看一下代码:

public static void main(String[] args) throws IOException {
        // 从启动参数里面获取端口号,这个端口号用于logback打印日志文件使用,默认值8091
        // 具体参见类SystemPropertyLoggerContextListener
        int port = PortHelper.getPort(args);
        System.setProperty(ConfigurationKeys.SERVER_PORT, Integer.toString(port));

        // create logger
        final Logger logger = LoggerFactory.getLogger(Server.class);
        //判断当前是否是在docker容器中运行
        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.
        //解析参数,参数解析指的是在seata-server.sh命令后面跟的启动参数
        ParameterParser parameterParser = new ParameterParser(args);

        //初始化统计模块,以后用单独的文章分析
        MetricsManager.get().init();
        //设置事务日志的存储模式,可以是db或者file、redis,在seata中,事务叫做会话(session)
        //存储模式既可以通过启动参数设置也可以通过file.conf文件设置
        System.setProperty(ConfigurationKeys.STORE_MODE, parameterParser.getStoreMode());
        //创建NettyRemotingServer
        NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(WORKING_THREADS);
        //server port
        //设置服务端的监听端口,默认是8091
        nettyRemotingServer.setListenPort(parameterParser.getPort());
        //ServerNode可以理解为是机器码,用一个数字代表一个服务节点,该值既可以标示一台服务节点,也用作生成UUID
        //如果不设置该值的话,默认使用雪花算法生成
        UUIDGenerator.init(parameterParser.getServerNode());
        //log store mode : file, db, redis
        //创建会话管理器,也就是事务管理器,根据存储模式的不同,会话管理器也不同
        //会话管理器:存储了全局会话和分支会话
        //会话管理器分为:根会话管理器、异步提交会话管理器、重试提交会话管理器、重试会话管理器
        SessionHolder.init(parameterParser.getStoreMode());
        //创建协调器,协调器用于处理nettyRemotingServer转发的请求,之后再调用核心模块DefaultCore,可以开启事务,回滚事务,提交事务等
        DefaultCoordinator coordinator = new DefaultCoordinator(nettyRemotingServer);
        //初始化,创建四个定时器,
        //分别用于检测回滚重试、提交重试、异步提交、超时检测等事务会话
        coordinator.init();
        //设置nettyRemotingServer的transactionMessageHandler属性,
        //当nettyRemotingServer收到请求后,可以将请求转发给协调器
        nettyRemotingServer.setHandler(coordinator);
        // register ShutdownHook
        // 注册JVM的关闭钩子
        ShutdownHook.getInstance().addDisposable(coordinator);
        ShutdownHook.getInstance().addDisposable(nettyRemotingServer);

        //127.0.0.1 and 0.0.0.0 are not valid here.
        // 校验IP地址,启动参数设置127.0.0.1和0.0.0.0 会导致启动失败
        if (NetUtil.isValidIp(parameterParser.getHost(), false)) {
            XID.setIpAddress(parameterParser.getHost());
        } else {
            XID.setIpAddress(NetUtil.getLocalIp());
        }
        XID.setPort(nettyRemotingServer.getListenPort());

        try {
            // 注册处理器,并启动Netty的服务端,执行完init方法后,服务端可以接收外部请求
            // 处理器的作用是:每个处理器只处理指定类型的请求报文或者响应报文,处理器将报文再转发给协调器,最终由协调器处理
            nettyRemotingServer.init();
        } catch (Throwable e) {
            logger.error("nettyServer init error:{}", e.getMessage(), e);
            System.exit(-1);
        }
        //如果服务端的socket不关闭,那么这行代码永远不会执行。
        //System.exit可以确保JVM关闭
        System.exit(0);
    }

相关文章