当前位置: 首页 > news >正文

Apache Seata应用侧启动过程剖析——RM TM如何与TC建立连接

本文来自 Apache Seata官方文档,欢迎访问官网,查看更多深度文章。
本文来自 Apache Seata官方文档,欢迎访问官网,查看更多深度文章。
Apache Seata应用侧启动过程剖析——RM & TM如何与TC建立连接

前言

看过官网 README 的第一张图片的同学都应该清楚,Seata 协调分布式事务的原理便在于通过其协调器侧的 TC,来与应用侧的 TM、RM 进行各种通信与交互,来保证分布式事务中,多个事务参与者的数据一致性。那么 Seata 的协调器侧与应用侧之间,是如何建立连接并进行通信的呢?

没错,答案就是 Netty,Netty 作为一款高性能的 RPC 通信框架,保证了 TC 与 RM 之间的高效通信,关于 Netty 的详细介绍,本文不再展开,今天我们探究的重点,在于应用侧在启动过程中,如何通过一系列 Seata 关键模块之间的协作(如 RPC、Config/Registry Center 等),来建立与协调器侧之间的通信

从 GlobalTransactionScanner 说起

我们知道 Seata 提供了多个开发期注解,比如用于开启分布式事务的@GlobalTransactional、用于声明 TCC 两阶段服务的@TwoPhraseBusinessAction 等,它们都是基于 Spring AOP 机制,对使用了注解的 Bean 方法分配对应的拦截器进行增强,来完成对应的处理逻辑。而 GlobalTransactionScanner 这个 Spring Bean,就承载着为各个注解分配对应的拦截器的职责,从其 Scanner 的命名,我们也不难推断出,它是为了在 Spring 应用启动过程中,对与全局事务(GlobalTransactionScanner)相关的 Bean 进行扫描、处理的。

除此之外,应用侧 RPC 客户端(TMClient、RMClient)初始化、与 TC 建立连接的流程,也是在 GlobalTransactionScanner#afterPropertiesSet()中发起的:

    /*** package:io.seata.spring.annotation* class:GlobalTransactionScanner*/@Overridepublic void afterPropertiesSet() {if (disableGlobalTransaction) {if (LOGGER.isInfoEnabled()) {LOGGER.info("Global transaction is disabled.");}return;}//在Bean属性初始化之后,执行TM、RM的初始化initClient();}

RM & TM 的初始化与连接过程

这里,我们以 RMClient.init()为例说明,TMClient 的初始化过程亦同理。

类关系的设计

查看 RMClient#init()的源码,我们发现,RMClient 先构造了一个 RmNettyRemotingClient,然后执行其初始化init()方法。而 RmNettyRemotingClient 的构造器初始化方法,都会逐层调用父类的构造器与初始化方法

    /*** RMClient的初始化逻辑* package:io.seata.rm* class:RMClient*/public static void init(String applicationId, String transactionServiceGroup) {//① 首先从RmNettyRemotingClient类开始,依次调用父类的构造器RmNettyRemotingClient rmNettyRemotingClient = RmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup);rmNettyRemotingClient.setResourceManager(DefaultResourceManager.get());rmNettyRemotingClient.setTransactionMessageHandler(DefaultRMHandler.get());//② 然后从RmNettyRemotingClient类开始,依次调用父类的init()rmNettyRemotingClient.init();}

上述 RMClient 系列各类之间的关系以及调用构造器和 init()初始化方法的过程如下图示意:
RMClient.init简化版流程与主要类之间的关系

那么为何要将 RMClient 设计成这样较为复杂的继承关系呢?其实是为了将各层的职责、边界划分清楚,使得各层可以专注于特定逻辑处理,实现更好的扩展性,这部分的详细设计思路,可参考 Seata RPC 模块重构 PR 的操刀者乘辉兄的文章Seata-RPC 重构之路)

初始化的完整流程

各类的构造器与初始化方法中的主要逻辑,大家可以借助下面这个能表意的序列图来梳理下,此图大家也可先跳过不看,在下面我们分析过几个重点类后,再回头来看这些类是何时登场、如何交互的协作的。
RMClient的初始化流程

抓住核心——Channel 的创建

首先我们需要知道,应用侧与协调器侧的通信是借助 Netty 的 Channel(网络通道)来完成的,因此通信过程的关键在于 Channel 的创建,在 Seata 中,通过池化的方式(借助了 common-pool 中的对象池)方式来创建、管理 Channel。

这里我们有必要简要介绍下对象池的简单概念及其在 Seata 中的实现:
涉及到的 common-pool 中的主要类:

  • GenericKeydObjectPool<K, V>:KV 泛型对象池,提供对所有对象的存取管理,而对象的创建由其内部的工厂类来完成
  • KeyedPoolableObjectFactory<K, V>:KV 泛型对象工厂,负责池化对象的创建,被对象池持有

涉及到的 Seata 中对象池实现相关的主要类:

  • 首先,被池化管理的对象就是Channel,对应 common-pool 中的泛型 V
  • NettyPoolKey:Channel 对应的 Key,对应 common-pool 中的泛型 K,NettyPoolKey 主要包含两个信息:
    • address:创建 Channel 时,对应的 TC Server 地址
    • message:创建 Channel 时,向 TC Server 发送的 RPC 消息体
  • GenericKeydObjectPool<NettyPoolKey,Channel>:Channel 对象池
  • NettyPoolableFactory:创建 Channel 的工厂类

认识了上述对象池相关的主要类之后,我们再来看看 Seata 中涉及 Channel 管理以及与 RPC 相关的几个主要类:

  • NettyClientChannelManager:
    • 持有 Channel 对象池
    • 与 Channel 对象池交互,对应用侧 Channel 进行管理(获取、释放、销毁、缓存等)
  • RpcClientBootstrap:RPC 客户端核心引导类,持有 Netty 框架的 Bootstrap 对象,具备启停能力;具有根据连接地址来获取新 Channel 的能力,供 Channel 工厂类调用
  • AbstractNettyRemotingClient:
    • 初始化并持有 RpcClientBootstrap
    • 应用侧 Netty 客户端的顶层抽象,抽象了应用侧 RM/TM 取得各自 Channel 对应的 NettyPoolKey 的能力,供 NettyClientChannelManager 调用
    • 初始化 NettyPoolableFactory

了解上述概念后,我们可以把 Seata 中创建 Channel 的过程简化如下:
创建Channel对象过程

看到这里,大家可以回过头再看看上面的RMClient 的初始化序列图,应该会对图中各类的职责、关系,以及整个初始化过程的意图有一个比较清晰的理解了。

建立连接的时机与流程

那么,RMClient 是何时与 Server 建立连接的呢?

在 RMClient 初始化的过程中,大家会发现,很多 init()方法都设定了一些定时任务,而 Seata 应用侧与协调器的重连(连接)机制,就是通过定时任务来实现的:

    /*** package:io.seata.core.rpcn.netty* class:AbstractNettyRemotingClient*/public void init() {//设置定时器,定时重连TC ServertimerExecutor.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {clientChannelManager.reconnect(getTransactionServiceGroup());}}, SCHEDULE_DELAY_MILLS, SCHEDULE_INTERVAL_MILLS, TimeUnit.MILLISECONDS);if (NettyClientConfig.isEnableClientBatchSendRequest()) {mergeSendExecutorService = new ThreadPoolExecutor(MAX_MERGE_SEND_THREAD,MAX_MERGE_SEND_THREAD,KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(),new NamedThreadFactory(getThreadPrefix(), MAX_MERGE_SEND_THREAD));mergeSendExecutorService.submit(new MergedSendRunnable());}super.init();clientBootstrap.start();}

我们通过跟踪一次 reconnect 的执行,看看上面探究的几个类之间是如何协作,完成 RMClient 与 TC 的连接的(实际上首次连接可能发生在 registerResource 的过程中,但流程一致)
RMClient与TC Server连接过程

这个图中,大家可以重点关注这几个点:

  • NettyClientChannelManager 执行具体 AbstractNettyRemotingClient 中,获取 NettyPoolKey 的回调函数(getPoolKeyFunction()):应用侧的不同 Client(RMClient 与 TMClient),在创建 Channel 时使用的 Key 不同,使两者在重连 TC Server 时,发送的注册消息不同,这也是由两者在 Seata 中扮演的角色不同而决定的:
    • TMClient:扮演事务管理器角色,创建 Channel 时,仅向 TC 发送 TM 注册请求(RegisterTMRequest)即可
    • RMClient:扮演资源管理器角色,需要管理应用侧所有的事务资源,因此在创建 Channel 时,需要在发送 RM 注册请求(RegesterRMRequest)前,获取应用侧所有事务资源(Resource)信息,注册至 TC Server
  • 在 Channel 对象工厂 NettyPoolableFactory 的 makeObject(制造 Channel)方法中,使用 NettyPoolKey 中的两项信息,完成了两项任务:
    • 使用 NettyPoolKey 的 address 创建新的 Channel
    • 使用 NettyPoolKey 的 message 以及新的 Channel 向 TC Server 发送注册请求,这就是 Client 向 TC Server 的连接(首次执行)或重连(非首次,由定时任务驱动执行)请求

以上内容,就是关于 Seata 应用侧的初始化及其与 TC Server 协调器侧建立连接的全过程分析。

更深层次的细节,建议大家再根据本文梳理的脉络和提到的几个重点,细致地阅读下源码,相信定会有更深层次的理解和全新的收获!

后记:考虑到篇幅以及保持一篇源码分析文章较为合适的信息量,本文前言中所说的配置、注册等模块协作配合并没有在文章中展开和体现。

在下篇源码剖析中,我会以配置中心注册中心为重点,为大家分析,在 RMClient/TM Client 与 TC Server 建立连接之前,Seata 应用侧是如何通过服务发现找到 TC Server、如何从配置模块获取各种信息的。

相关文章:

Apache Seata应用侧启动过程剖析——RM TM如何与TC建立连接

本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 Apache Seata应用侧启动过程剖析——RM & TM如何与TC建立连接 前言 看过官网 README 的第…...

Origin 的使用

官网&#xff1a;OriginLab - Origin and OriginPro - Data Analysis and Graphing Software 安装&#xff1a;Origin2022最新最详细的安装教程 学生免费&#xff1a;Origin 官方正版免费续期教程 更改语言&#xff1a;解决OriginPro2024学生版本的更改中文change language灰…...

MySQL相关知识点

目录 1. 基本概念2. 数据类型3. 数据库操作4. 表操作5. 数据操作6. 索引7. 约束8. 事务9. 存储过程和触发器10. 优化和性能调优11. 安全性12. 备份和恢复 MySQL 是一个广泛使用的 关系数据库管理系统 (RDBMS)。了解 MySQL 的主要知识点可以帮助你更好地设计、管理和优化数据库…...

第4章 Vite模块化与插件系统(二)

4.3 常用插件介绍 4.3.1 官方插件 vitejs/plugin-vue 用于支持 Vue.js 开发&#xff1a; npm install vitejs/plugin-vue --save-devimport vue from vitejs/plugin-vueexport default defineConfig({plugins: [vue()] })vitejs/plugin-react 用于支持 React 开发&#xf…...

前端传到后端的data数组中有些属性值为空

将前端输入框中的值全部放入data中传入后端&#xff0c;但是在后端查看发现后端接收到的数据有些属性值为空。 第一种情况&#xff1a;只有第一个属性为空&#xff0c;其余属性接收正常 可能原因&#xff1a;后端用来接收的 比如前端发送数据&#xff1a; 实际上前端发送的数…...

怎么批量下载网页里的图片和视频 如何批量下载一个网站的所有图片 如何批量下载网页视频文件 idm软件怎么下载

当我们在网站内需要下载大量图片时&#xff0c;一张一张的下载非常麻烦。这里推荐大家使用IDM这款网页图片下载工具。下面&#xff0c;我将介绍怎么批量下载网页里的图片和视频&#xff0c;如何批量下载一个网站的所有图片的解决方法。 一、怎么批量下载网页里的图片和视频 …...

Python面试题:在 Python 中,如何处理文件操作?

在Python中&#xff0c;文件操作&#xff08;如读取和写入文件&#xff09;是一个常见的任务。Python标准库提供了内置的函数和上下文管理器来简化文件操作。以下是处理文件操作的一些基本方法和示例&#xff1a; 打开和关闭文件 使用open()函数打开文件。该函数返回一个文件…...

红日靶机1

靶场环境 使用kali攻击web服务器&#xff0c;然后根据web服务器攻击其他域内的机器 这里很明确kali是攻击机&#xff0c;外网机器&#xff0c;局域网中的win7是web服务器&#xff0c;有2个网卡&#xff0c;通内网和外网&#xff0c;2k3以及2008r2是内网机器&#xff0c;不出网&…...

Windows电脑PC使用adb有线跟无线安装apk包

在Android开发中&#xff0c;经常需要使用ADB&#xff08;Android Debug Bridge&#xff09;来安装APK包到Android设备上&#xff0c;无论是通过有线连接还是无线连接。以下将分别介绍如何通过有线和无线方式使用ADB安装APK包。 有线连接安装APK 启用开发者选项和USB调试&…...

如何把harmonos项目修改为openharmony项目

一开始分不清harmonyos和openharmony&#xff0c;在harmonyos直接下载的开发软件&#xff0c;后面发现不对劲&#xff0c;打脑阔 首先你要安装对应版本的开发软件&#xff0c;鸿蒙开发是由harmonyos和openharmony官网两个的&#xff0c;找到对应的地方下载对应版本的开发软件&…...

【QT】Qt智能指针QPointer、QSharedPointer、QWeakPointer、QScopedPointer

QPointer QPointer can only point to QObject instances. It will be automatically set to nullptr if the pointed to object is destroyed. It is a weak pointer specialized for QObject. QPointer只能指向QObject实例。如果指向的对象被销毁&#xff0c;它将自动设置为 …...

设计模式探索:建造者模式

1. 什么是建造者模式 建造者模式 (Builder Pattern)&#xff0c;也被称为生成器模式&#xff0c;是一种创建型设计模式。 定义&#xff1a;将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 建造者模式要解决的问题&#xff1a; 建造者模…...

[Go] 字符串遍历数据类型问题

字符串遍历问题 在使用for i,v:range str遍历字符串时 str[i]是unit8&#xff08;byte&#xff09;类型&#xff0c;返回的是单个字节 字符串在Go中是以字节序列的形式存储的&#xff0c;而 str[i] 直接访问了这个字节序列中的第 i 个字节。如果字符串中的字符是单字节的ASCII…...

HJ41 称砝码

HJ41 称砝码 提示&#xff1a;文章 文章目录 前言一、背景二、 2.1 2.2 总结 前言 前期疑问&#xff1a; 本文目标&#xff1a; 一、背景 这个题目之前是没有做出来的&#xff0c;我把之前没做出来的代码也记录一下 二、 2.1 之前的代码 #include <stdio.h>int m…...

如何使用Python脚本实现SSH登录

调试IDE&#xff1a;PyCharm Python库&#xff1a;Paramiko 首先安装Paramiko包到PyCharm&#xff0c;具体步骤为&#xff1a;在打开的PyCharm工具中&#xff0c;选择顶部菜单栏中“File”下的“Settings”&#xff0c;在设置对话框中&#xff0c;选择“Project”下的“Proje…...

2024年文化研究与数字媒体国际会议 (CRDM 2024)

2024年文化研究与数字媒体国际会议 (CRDM 2024) 2024 International Conference on Cultural Research and Digital Media 【重要信息】 大会地点&#xff1a;珠海 大会官网&#xff1a;http://www.iccrdm.com 投稿邮箱&#xff1a;iccrdmsub-conf.com 【注意&#xff1a;稿将…...

14-52 剑和诗人26 - RAG 和 VectorDB 简介

检索增强生成 (RAG) 和 VectorDB 是自然语言处理 (NLP) 中的两个重要概念&#xff0c;它们正在突破 AI 系统所能实现的界限。 在这篇博文中&#xff0c;我将深入探讨 RAG&#xff0c;探索其工作原理、应用、优势和局限性。 我们还将研究 VectorDB&#xff0c;这是一种专用于向…...

如果MySQL出现 “Too many connections“ 错误,该如何解决?

当你想要连接MySQL时出现"Too many connections" 报错的情况下&#xff0c;该如何解决才能如愿以偿呢&#xff1f;都是哥们儿&#xff0c;就教你两招吧&#xff01; 1.不想重启数据库的情况下 你可以尝试采取以下方法来解决&#xff1a; 增加连接数限制&#xff1a…...

论文阅读:Rethinking Interpretability in the Era of Large Language Models

Rethinking Interpretability in the Era of Large Language Models 《Rethinking Interpretability in the Era of Large Language Models》由Chandan Singh、Jeevana Priya Inala、Michel Galley、Rich Caruana和Jianfeng Gao撰写&#xff0c;探讨了在大型语言模型&#xff…...

C++/Qt 信号槽机制详解

文章目录 C++/Qt 信号槽机制详解一、信号和槽的基本概念1. 信号2. 槽3. 连接二、信号和槽的基本使用1. 信号和槽的声明和定义2. 连接信号和槽三、信号和槽的工作原理1. MOC(Meta-Object Compiler)2. 事件循环3. 连接类型四、信号和槽的高级应用1. 自定义信号和槽2. Lambda 表…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

Python 高效图像帧提取与视频编码:实战指南

Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...

全面解析数据库:从基础概念到前沿应用​

在数字化时代&#xff0c;数据已成为企业和社会发展的核心资产&#xff0c;而数据库作为存储、管理和处理数据的关键工具&#xff0c;在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理&#xff0c;到社交网络的用户数据存储&#xff0c;再到金融行业的交易记录处理&a…...