Seata1.5.0源码解析
写在前面
-
之前项目有使用Seata中间件实现分布式事务, 最近忙里偷闲做一次总结,一方面把一些踩坑记录下来,方便自己和读者快速排错,还有就是争取做到温故而知新, 话不多说, 搞起,搞起.
-
入门知识建议参考官网: Seata官网地址
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
基础知识
- 术语表
- TC (Transaction Coordinator) - 事务协调者: 维护全局和分支事务的状态,驱动全局事务提交或回滚, 就是我们启动的Seata服务端,一个单独的jvm进程
- TM (Transaction Manager) - 事务管理器: 定义全局事务的范围:开始全局事务、提交或回滚全局事务, 就是我们使用@GlobalTransactional的那个服务, 它同时也可以是RM
- RM (Resource Manager) - 资源管理器: 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚, 有异常情况需要处理的地方, 一般是被调用的服务
- 示意图, 这些知识官网上都有
- 源码下载: Seata地址,来源于全球最大同性交友网站github
- 建议先fork到自己的仓库
- 下载后的目录
- 模块作用:
- common模块: 通用的异常、工具类、线程工具、计数器、SPI、常量等
- serializer模块: 编解码相关,实现了kryo、protobuf等编解码器
- config模块: Seata中的配置中心和注册中心是分离的,实现了主流的配置中心,默认file模式,apollo,nacos,etcd3等
- core模块: 包括封装好的RPC、数据模型、通信协议、事件、认证、序列化、存储、锁等等
- discovery模块: seata-server充当的事务协调者的角色,用于管理全局事务, RM,TM通过网络通信(没错就是netty)进行事务的提交与回滚,类似与微服务这样, 所以需要一个注册中心,现在支持consul、etcd3、eureka、redis等等
- metrics模块: 统计相关的功能, 支持导出
- rm模块: resource manager相关功能的核心抽象, 上个图看一下
- rm-datasource模块: 对JDBC的扩展, 实现操作数据库相关的功能
- saga模块: 前面提到了seata是支持多种模式的, saga模式的实现
- tcc模块: 对tcc模式的支持
- tm模块: 分布式事务中事务管理者的实现, 稍后会详细介绍
- spring模块: 使用Spring注解类的功能实现
- server模块: TC的实现模块。包含不同模式的事务实现
- test模块: 单元测试
- compressor模块: 支持主流压缩模式, 像7z,zip,gzip等
- console模块: 可视化相关,整合了SpringSecurity,JWT相关
服务端启动
-
启动类: ServerApplication
配置了一下包扫描@SpringBootApplication(scanBasePackages = { "io.seata"}) public class ServerApplication { public static void main(String[] args) throws IOException { // run the spring-boot application SpringApplication.run(ServerApplication.class, args); } }
-
使用SpringBoot启动和关闭的钩子函数
@Component public class ServerRunner implements CommandLineRunner, DisposableBean { private static final Logger LOGGER = LoggerFactory.getLogger(ServerRunner.class); // 项目是否启动标识 private boolean started = Boolean.FALSE; // 释放资源 private static final List<Disposable> DISPOSABLE_LIST = new CopyOnWriteArrayList<>(); // 维护释放资源列表 public static void addDisposable(Disposable disposable) { DISPOSABLE_LIST.add(disposable); } // SpringBoot启动后的执行逻辑 @Override public void run(String... args) { try { long start = System.currentTimeMillis(); 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); } } public boolean started() { return started; } // SpringBoot关闭的执行逻辑 @Override public void destroy() throws Exception { if (LOGGER.isDebugEnabled()) { LOGGER.debug("destoryAll starting"); } for (Disposable disposable : DISPOSABLE_LIST) { disposable.destroy(); } if (LOGGER.isDebugEnabled()) { LOGGER.debug("destoryAll finish"); } } }
-
启动业务的类是Server
public class Server { /** * The entry point of application. * * @param args the input arguments */ public static void start(String[] args) { // create logger final Logger logger = LoggerFactory.getLogger(Server.class); //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. // 解析启动命令 ParameterParser parameterParser = new ParameterParser(args); //initialize the metrics // 使用SPI机制获取registry实例对象 MetricsManager.get().init(); // 从配置中读取存储模式,设置到系统变量中 System.setProperty(ConfigurationKeys.STORE_MODE, parameterParser.getStoreMode()); // 自定义线程池, 自定义线程工厂, 拒绝策略使用CallerRunsPolicy ThreadPoolExecutor workingThreads = new ThreadPoolExecutor(NettyServerConfig.getMinServerPoolSize(), NettyServerConfig.getMaxServerPoolSize(), NettyServerConfig.getKeepAliveTime(), TimeUnit.SECONDS, new LinkedBlockingQueue<>(NettyServerConfig.getMaxTaskQueueSize()), new NamedThreadFactory("ServerHandlerThread", NettyServerConfig.getMaxServerPoolSize()), new ThreadPoolExecutor.CallerRunsPolicy()); // 初始化PRC远程服务器 NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads); // UUIDGenerator初始化,雪花算法 UUIDGenerator.init(parameterParser.getServerNode()); //log store mode : file, db, redis // log的持久化 SessionHolder.init(parameterParser.getSessionStoreMode()); // 事务日志的持久化, 默认file LockerManagerFactory.init(parameterParser.getLockStoreMode()); // TC核心事务处理类 DefaultCoordinator coordinator = DefaultCoordinator.getInstance(nettyRemotingServer); coordinator.init(); nettyRemotingServer.setHandler(coordinator); // let ServerRunner do destroy instead ShutdownHook, see https://github.com/seata/seata/issues/4028 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,开始监听端口 nettyRemotingServer.init(); } }
-
参数解析
我们主要看ParameterParser的init方法, 还有ConfigurationFactoryprivate void init(String[] args) { try { // 基于JCommander解析启动时的参数 getCommandParameters(args); // 基于容器获取参数 getEnvParameters(); // 参数设置 if (StringUtils.isNotBlank(seataEnv)) { System.setProperty(ENV_PROPERTY_KEY, seataEnv); } if (StringUtils.isBlank(storeMode)) { storeMode = CONFIG.getConfig(ConfigurationKeys.STORE_MODE, SERVER_DEFAULT_STORE_MODE); } if (StringUtils.isBlank(sessionStoreMode)) { sessionStoreMode = CONFIG.getConfig