源码系列(八)-Seata1.5.0源码解析

源码系列(八)-Seata源码解析前言之前项目有使用Seata中间件实现分布式事务, 最近忙里偷闲做一次总结,一方面把一些踩坑记录下来,方便自己和读者快速排错,还有就是争取做到温故而知新, 话不多说, 搞起,搞起.入门知识建议参考官网: Seata官网地址Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。源码解析-AT模式术语表T
摘要由CSDN通过智能技术生成

写在前面

  1. 之前项目有使用Seata中间件实现分布式事务, 最近忙里偷闲做一次总结,一方面把一些踩坑记录下来,方便自己和读者快速排错,还有就是争取做到温故而知新, 话不多说, 搞起,搞起.

  2. 入门知识建议参考官网: Seata官网地址

    在这里插入图片描述

    Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

基础知识

  1. 术语表
    1. TC (Transaction Coordinator) - 事务协调者: 维护全局和分支事务的状态,驱动全局事务提交或回滚, 就是我们启动的Seata服务端,一个单独的jvm进程
    2. TM (Transaction Manager) - 事务管理器: 定义全局事务的范围:开始全局事务、提交或回滚全局事务, 就是我们使用@GlobalTransactional的那个服务, 它同时也可以是RM
    3. RM (Resource Manager) - 资源管理器: 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚, 有异常情况需要处理的地方, 一般是被调用的服务
  2. 示意图, 这些知识官网上都有
    在这里插入图片描述
  3. 源码下载: Seata地址,来源于全球最大同性交友网站github
    1. 建议先fork到自己的仓库
    2. 下载后的目录
      在这里插入图片描述
    3. 模块作用:
      1. common模块: 通用的异常、工具类、线程工具、计数器、SPI、常量等
      2. serializer模块: 编解码相关,实现了kryo、protobuf等编解码器
      3. config模块: Seata中的配置中心和注册中心是分离的,实现了主流的配置中心,默认file模式,apollo,nacos,etcd3等
      4. core模块: 包括封装好的RPC、数据模型、通信协议、事件、认证、序列化、存储、锁等等
      5. discovery模块: seata-server充当的事务协调者的角色,用于管理全局事务, RM,TM通过网络通信(没错就是netty)进行事务的提交与回滚,类似与微服务这样, 所以需要一个注册中心,现在支持consul、etcd3、eureka、redis等等
      6. metrics模块: 统计相关的功能, 支持导出
      7. rm模块: resource manager相关功能的核心抽象, 上个图看一下
        在这里插入图片描述
      8. rm-datasource模块: 对JDBC的扩展, 实现操作数据库相关的功能
      9. saga模块: 前面提到了seata是支持多种模式的, saga模式的实现
      10. tcc模块: 对tcc模式的支持
      11. tm模块: 分布式事务中事务管理者的实现, 稍后会详细介绍
      12. spring模块: 使用Spring注解类的功能实现
      13. server模块: TC的实现模块。包含不同模式的事务实现
      14. test模块: 单元测试
      15. compressor模块: 支持主流压缩模式, 像7z,zip,gzip等
      16. console模块: 可视化相关,整合了SpringSecurity,JWT相关

服务端启动

  1. 启动类: 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);
        }
    }
    
  2. 使用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");
            }
        }
    }
    
  3. 启动业务的类是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();
        }
    }
    
  4. 参数解析
    我们主要看ParameterParser的init方法, 还有ConfigurationFactory

    private 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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值