Seata Server
Seata Server
首先确定一下Seata Server 分为几个重要的模块:
- Config (配置相关)
- Store(全局事务和分支信息持久化相关)
- Coordinator (TC事务协调核心逻辑)
- Lock (资源锁的实现)
- Netty (与TM 和RM 通信)
这里Seata Server 都是基于1.5.1 的版本进行分析,需要对springboot
,netty
又一定的了解
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);
}
}
}
ServerRunner
的run
方法中调用了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
,分别为FileSessionManager
,DataBaseSessionManager
,RedisSessionManager
初始化锁管理器
LockerManagerFactory#init
和会话管理器相似,锁管理器也是通过SPI 的方式进行初始化
初始化TC事务协调器
DefaultCoordinator#getInstance
DefaultCore
会加载全部的Core
,然后放到Map
中,根据不同的事务类型,执行不同的core
逻辑
在init
方法中创建了许多个定时任务线程池去处理事务回滚重试,提交重试,异步提交,超时检查 和undoLog 的删除,提高了seata 的吞吐量
启动netty server
NettyRemotingServer#init
registerProcessor
以MessageType
为key, 将RemotingProcessor
和ThreadPoolExecutor
封装为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);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」