Seata Server

Seata Server

首先确定一下Seata Server 分为几个重要的模块:

  • Config (配置相关)
  • Store(全局事务和分支信息持久化相关)
  • Coordinator (TC事务协调核心逻辑)
  • Lock (资源锁的实现)
  • Netty (与TM 和RM 通信)

这里Seata Server 都是基于1.5.1 的版本进行分析,需要对springbootnetty又一定的了解

Seata Server 启动

Seata Server 是一个普通的springboot 的项目

这里来看一下Seata Server 的目录结构

根据上述的目录结构,标注了两个重要的类,这是seata server的启动入口

ServerRunner
@Component
public class ServerRunner implements CommandLineRunner, DisposableBean {
   @Override
    public void run(String... args) {
        try {
            long start = System.currentTimeMillis();
            // 调用start 方法启动seata server
            Server.start(args);
            started = true;
            long cost = System.currentTimeMillis() - start;
            LOGGER.info("seata server started in {} millSeconds", cost);
        } catch (Throwable e) {
            started = Boolean.FALSE;
            LOGGER.error("seata server start error: {} ", e.getMessage(), e);
            System.exit(-1);
        }
    } 
}

ServerRunnerrun方法中调用了Server#start 方法进行了一些初始化启动了seata server

Server
public class Server {

    public static void start(String[] args) {
        // 日志
        final Logger logger = LoggerFactory.getLogger(Server.class);
        // 解析配置文件,具体解析的方式,根据在application.yml 中配置seata.config.type 进行
        // 判断解析
        ParameterParser parameterParser = new ParameterParser(args);

        // 初始化监控,比如metric指标采集
        MetricsManager.get().init();
        // 将Store 资源持久化方式放到系统的环境变量中
        System.setProperty(ConfigurationKeys.STORE_MODE, parameterParser.getStoreMode());
		// 线程池, 这里是netty server 里面的io线程池
        ThreadPoolExecutor workingThreads = new ThreadPoolExecutor(
                NettyServerConfig.getMinServerPoolSize(),
                NettyServerConfig.getMaxServerPoolSize(), 
                NettyServerConfig.getKeepAliveTime(), TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(NettyServerConfig.getMaxTaskQueueSize()),
                new NamedThreadFactory("ServerHandlerThread", 
                                        NettyServerConfig.getMaxServerPoolSize()), 
                new ThreadPoolExecutor.CallerRunsPolicy());
        // 创建netty server 对象
        NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads);
        // 初始化UUID 生成器, 后面全局事务id,分支事务id 等都会用到这个id 生成器
        UUIDGenerator.init(parameterParser.getServerNode());
        // 初始化事务日志,锁日志的储存方式,这里支持file, db, redis
        SessionHolder.init(parameterParser.getSessionStoreMode());
        LockerManagerFactory.init(parameterParser.getLockStoreMode());
        // 初始化TC事务协调, 这里把netty 也传入进去了,然后利用反射初始化了一些核心类,比如
        //ATCore,TccCore, SagaCore, XACore, 后面根据不同的事务类型走不同的逻辑
        DefaultCoordinator coordinator = DefaultCoordinator.getInstance(nettyRemotingServer);
        // 这里是利用多个定时任务线程池用于提高性能,比如重试回滚,回滚日志删除等
        coordinator.init();
        // 将TC 事务协调处理器设置到netty,在netty 中处理数据的阶段回调用事务处理器中的
        // ATCore,TccCore, SagaCore, XACore 核心类进行处理
        nettyRemotingServer.setHandler(coordinator);

        //注册ServerRunner销毁(Spring容器销毁)的回调钩子函数
        ServerRunner.addDisposable(coordinator);

        //127.0.0.1 and 0.0.0.0 are not valid here.
        if (NetUtil.isValidIp(parameterParser.getHost(), false)) {
            XID.setIpAddress(parameterParser.getHost());
        } else {
            String preferredNetworks = ConfigurationFactory.getInstance()
                                                           .getConfig(REGISTRY_PREFERED_NETWORKS);
            if (StringUtils.isNotBlank(preferredNetworks)) {
                XID.setIpAddress(NetUtil.getLocalIp(preferredNetworks.split(REGEX_SPLIT_CHAR)));
            } else {
                XID.setIpAddress(NetUtil.getLocalIp());
            }
        }
        // 启动netty server
        nettyRemotingServer.init();
    }
}

总结: seata server 启动总共干了下面几件事

  • 解析配置文件和配置参数

  • 初始化监控,做metric指标采集

  • 初始化io 线程池,创建netty server 对象

  • 初始化 ID 生成器, 用于生成全局事务ID和分支事务ID

  • 初始化事务日志,锁日志的储存方式,这里支持file, db, redis

  • 初始化TC事务协调, 这里把netty 也传入进去了,然后利用反射初始化了一些核心类,比如ATCore,TccCore, SagaCore, XACore, 后面根据不同的事务类型走不同的逻辑

  • 注册ServerRunner销毁(Spring容器销毁)的回调钩子函数

  • 启动netty server

解析配置文件和配置参数

初始化监控

metric seata server默认是不开启的,如果需要开启需要将metric的开关打开 metrics.enabled=true

创建netty server 对象

创建了netty server 对象的时候,同时设置了一个handler, 这个hander 在 netty 启动的时候会添加到pipline 中,用于数据的具体处理

初始化UUID 生成器

UUIDGenerator#init

IdWorker

IdWorker#initTimestampAndSequence

这里主要是将时间戳和序列混合在一个long 类型中

IdWorker#initWorkerId

初始化会话事务日志

SessionHolder#init

SPI 机制会根据接口全限定类名去加载META-INF文件夹下面的指定文件中的类,这里主要有3种SessionManager,分别为FileSessionManagerDataBaseSessionManagerRedisSessionManager

初始化锁管理器
LockerManagerFactory#init

和会话管理器相似,锁管理器也是通过SPI 的方式进行初始化


初始化TC事务协调器

DefaultCoordinator#getInstance

DefaultCore

会加载全部的Core,然后放到Map中,根据不同的事务类型,执行不同的core逻辑

init方法中创建了许多个定时任务线程池去处理事务回滚重试,提交重试,异步提交,超时检查 和undoLog 的删除,提高了seata 的吞吐量

启动netty server

NettyRemotingServer#init

registerProcessor

MessageType为key, 将RemotingProcessorThreadPoolExecutor封装为Pari,以Pari为value 放入到Map中去

AbstractNettyRemotingServer#init

AbstractNettyRemoting#init

ServerBootstrap 启动

这里启动就是标准的netty 服务的启动, 因为channelHandler 被设置到了netty 的调用链路中,来了解一下这个ServerHandler

ServerHandler

netty 接受到的普通请求都会经过channelRead方法,在方法对Message进行处理, 每种Message都对应一种处理器,这里的处理器在netty serverBootstrap 启动之前,通过registerProcessor 注册,并且保存在了Map 中

protected void processMessage(ChannelHandlerContext ctx, RpcMessage rpcMessage) throws Exception {
    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug(String.format("%s msgId:%s, body:%s", this, rpcMessage.getId(), rpcMessage.getBody()));
    }
    Object body = rpcMessage.getBody();
    if (body instanceof MessageTypeAware) {
        MessageTypeAware messageTypeAware = (MessageTypeAware) body;
        // 根据消息的类型从Map 中获取处理消息组件, 这里大的组件是在启动Netty Server 
        // 服务中通过registerProcessor方法注册到Map 中的
        final Pair<RemotingProcessor, ExecutorService> pair = this.processorTable
                                                 .get((int) messageTypeAware.getTypeCode());
        if (pair != null) {
            if (pair.getSecond() != null) {
                try {
                    // 对Message 数据进行处理
                    pair.getSecond().execute(() -> {
                        try {
                            pair.getFirst().process(ctx, rpcMessage);
                        } catch (Throwable th) {
                            LOGGER.error(FrameworkErrorCode.NetDispatch.getErrCode(), 
                                         th.getMessage(), th);
                        } finally {
                            MDC.clear();
                        }
                    });
                } catch (RejectedExecutionException e) {
                    LOGGER.error(FrameworkErrorCode.ThreadPoolFull.getErrCode(),
                        "thread pool is full, current max pool size is " 
                        + messageExecutor.getActiveCount());
                    if (allowDumpStack) {
                        String name = ManagementFactory.getRuntimeMXBean().getName();
                        String pid = name.split("@")[0];
                        long idx = System.currentTimeMillis();
                        try {
                            String jstackFile = idx + ".log";
                            LOGGER.info("jstack command will dump to " + jstackFile);
                            Runtime.getRuntime().exec(
                                       String.format("jstack %s > %s", pid, jstackFile));
                         } catch (IOException exx) {
                            LOGGER.error(exx.getMessage());
                        }
                        allowDumpStack = false;
                    }
                }
            } else {
                try {
                    // 如果处理组件中不存在线程池,那么直接在当前线程上执行
                    pair.getFirst().process(ctx, rpcMessage);
                } catch (Throwable th) {
                    LOGGER.error(FrameworkErrorCode.NetDispatch.getErrCode(), 
                    th.getMessage(), th);
               }
            }
        } else {
            LOGGER.error("This message type [{}] has no processor.", 
            messageTypeAware.getTypeCode());
        }
    } else {
        LOGGER.error("This rpcMessage body[{}] is not MessageTypeAware type.", body);
    }
}
posted @   苜蓿椒盐  阅读(69)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示