《源纹天书》第一百四十一章至第一百四十五章:etcd升级、2PC的阻塞困境、3PC的超时突破、TCC的补偿之道、Saga的最终选择!

第一百四十一章 etcd升级------共识入魂

归元圣域,调度中心。

CodeStats盘膝坐在调度中心的控制台前,面前悬浮着《共识天书》的玉简,金色的源纹在虚空中缓缓流转。令灵儿和程一念一左一右,为他护法。九座浮空岛的服务注册数据像无数光点一样在他们周围漂浮,每一颗光点都代表着一次心跳、一次注册、一次发现请求。

"etcd的元数据仓库,是调度中心的大脑。"CodeStats睁开眼,目光中金色的源纹闪烁,"但现在的etcd只有一个节点,单点故障------如果这个节点崩了,整个调度中心就失去了所有服务的状态信息。不知道指令岛在哪,不知道容器岛是否健康,不知道任何服务的位置------整个系统就瘫痪了。"

"在凡界,这叫'单点故障'。分布式系统里,任何单点都是隐患。"

令灵儿问:"那怎么解决?"

"把etcd从单节点升级为集群------三个节点,用Raft共识协议保证一致性。"CodeStats在虚空中画出了架构图,"三个节点中,一个Leader负责处理所有写请求,两个Follower负责复制日志。当Leader故障时,Follower自动选举出新的Leader,整个集群继续工作。只要多数派(至少两个节点)存活,etcd就能正常服务。"

程一念举手:"我来扮演Follower。我的九个栈可以模拟多个节点,每个栈独立维护自己的日志,独立执行Raft协议。"

令灵儿说:"我的指令通道可以充当Raft的网络层------Leader的AppendEntries请求通过指令通道发送给Follower,Follower的确认响应也通过指令通道返回。"

CodeStats点头:"好。我来做Leader,负责接收调度中心的写请求,生成日志条目,推动共识。"

三人盘膝而坐,神识相连。

CodeStats的丹田中,一个新的"etcd集群"开始构建。三个节点分别由三人的神识模拟------CodeStats的CPU虚影充当Leader节点,程一念的两个栈充当Follower节点,令灵儿的指令通道充当集群的网络通信层。

第一个写请求到来:"注册指令岛,地址:指令岛.归元圣域.源世界,版本:2.0.0。"

CodeStats作为Leader,生成一个日志条目,追加到自己的日志中。然后,他向两个Follower发送AppendEntries请求------令灵儿的指令通道将请求封装成APPEND_ENTRIES指令,通过神识网络发送给程一念的两个栈。

程一念的两个栈分别收到请求,将日志条目追加到自己的日志中,返回确认。

CodeStats收到两个确认(超过半数),将该条目标记为"已提交",然后向所有节点发送提交通知。两个Follower收到通知后,将条目应用到自己的状态机中。

"注册指令岛"这个信息,在三个节点上达成了一致。

"Raft的日志复制,完成了。"CodeStats说,"现在,无论哪个节点故障,其他节点都有完整的数据。只要多数派存活,数据就不会丢失。"

他继续操作。第二个写请求、第三个写请求......源源不断的注册和发现请求被etcd集群处理。三个节点之间通过Raft协议保持一致,元数据仓库从"单点"变成了"集群"。

令灵儿感受着指令通道中流动的共识协议数据包:"有了Raft的任期号和日志索引,我的指令通道不仅能传递数据,还能传递'一致性'。每条指令都带上了任期号和日志索引,接收方可以据此判断消息是否过期、日志是否匹配。"

程一念也感受着自己的两个栈:"我的两个栈维护了完整的Raft日志。每个栈都有自己的日志文件,记录了所有已提交的条目。如果Leader故障,我的栈可以参与选举------因为它们都有最新的日志。"

CodeStats看着三人共同构建的etcd集群,心中涌起一股满足感:"在凡界,etcd是Kubernetes的元数据存储。有了它,整个集群的状态才是可信的、一致的、永不丢失的。现在,源世界的调度中心,也有了自己的'etcd'。"

他在神识中的架构图上,把"etcd集群"标记为"已完成"。

"接下来,"他说,"是分布式事务。"

令灵儿问:"分布式事务......和共识协议有什么区别?"

CodeStats解释道:"共识协议解决的是'多个节点对同一个值达成一致'------比如'指令岛的地址是多少'。分布式事务解决的是'多个节点共同完成一个操作,要么全部成功,要么全部失败'------比如在容器岛创建一个Bean,同时在内存岛分配内存。如果容器岛成功了,内存岛失败了,整个操作应该回滚,不能让系统处于半完成状态。"

他在虚空中画了一张对比图:

text

复制代码
共识协议:多个节点 → 同一个值(一致性)
分布式事务:多个节点 → 同一个操作(原子性)

程一念问:"那分布式事务怎么实现?"

CodeStats想了想:"在凡界,分布式事务有几种经典方案------两阶段提交(2PC)、三阶段提交(3PC)、TCC(Try-Confirm-Cancel)、Saga。每一种都有各自的权衡。我们先从最简单的开始------两阶段提交。"

第一百四十二章 2PC的阻塞困境------事务协调者的考验

CodeStats在调度中心的控制台上创建了一个新的"事务管理器"------一个独立于etcd的模块,专门负责协调跨服务的分布式事务。

"在凡界,2PC(两阶段提交)是最经典的分布式事务协议。"CodeStats对令灵儿和程一念解释,"它有一个'协调者'(Coordinator)和多个'参与者'(Participants)。整个流程分为两个阶段------"

"第一阶段:准备(Prepare)。协调者向所有参与者发送'准备'请求,询问它们是否可以提交事务。每个参与者执行本地事务,但不提交,然后返回'可以'或'不可以'。"

"第二阶段:提交/回滚(Commit/Rollback)。如果所有参与者都返回'可以',协调者发送'提交'请求,所有参与者提交本地事务。如果有任何一个参与者返回'不可以',协调者发送'回滚'请求,所有参与者回滚本地事务。"

令灵儿问:"如果某个参与者在第一阶段之后、第二阶段之前崩溃了呢?"

CodeStats脸色变得凝重:"这就是2PC最大的问题------阻塞。如果协调者在发送'提交'请求之前崩溃了,所有参与者都会一直等待,永远阻塞在那里。它们已经执行了本地事务,但不知道是提交还是回滚。资源被锁定,事务无法释放。"

"在凡界,这叫'2PC的阻塞困境'------因为协调者是单点,协调者故障会导致整个事务挂起,系统陷入死锁状态。"

程一念皱眉:"那怎么办?"

CodeStats说:"在实际生产环境中,2PC很少单独使用。大多数系统用'三阶段提交'(3PC)来减少阻塞时间,或者用TCC和Saga来避免锁。但理解2PC,是理解所有分布式事务协议的基础。"

他在调度中心搭建了一个模拟场景------三个参与者:容器岛(创建Bean)、内存岛(分配内存)、线程岛(注册线程)。一个事务需要三者的操作全部成功才算成功。

"现在,我们来模拟2PC的完整流程。"

第一步:准备阶段。

CodeStats作为协调者,向三个参与者发送PREPARE请求。令灵儿的指令通道负责传输请求,程一念的栈帧负责接收和处理。每个参与者执行本地操作,但不提交,然后返回"READY"。

三个参与者全部返回READY。

第二步:提交阶段。

CodeStats发送COMMIT请求,三个参与者同时提交。事务成功。

"2PC的流程就是这样。"CodeStats说,"但问题来了------如果我在发送COMMIT之前崩溃了,三个参与者就会永远等待。它们已经执行了本地操作,但不知道下一步该做什么。"

他模拟了这个场景------协调者在发送COMMIT之前突然"宕机"。三个参与者收到PREPARE请求后,执行了本地操作,然后等待COMMIT或ROLLBACK。但协调者再也没有发送任何消息。三个参与者一直等待,资源被锁定,系统进入阻塞状态。

令灵儿问:"那在实际系统中,怎么解决这个问题?"

CodeStats说:"在实际系统中,2PC通常配合'超时机制'和'日志记录'使用------参与者在等待一段时间后,如果收不到协调者的指令,会自动回滚。但这只是一个'缓解',不是'根治'。真正解决阻塞问题的,是3PC和TCC。"

"接下来,我们来学习3PC------三阶段提交。"

第一百四十三章 3PC的超时突破------减少阻塞的智慧

CodeStats在事务管理器中增加了新的模块------3PC(三阶段提交)协议。

"3PC是2PC的改进版本。"CodeStats说,"它把2PC的两个阶段扩展成了三个阶段------"

"第一阶段:CanCommit(询问)。协调者询问所有参与者是否可以提交。参与者只需要回答'可以'或'不可以',不执行任何本地操作。"

"第二阶段:PreCommit(预提交)。如果所有参与者都回答'可以',协调者发送'预提交'请求。参与者执行本地操作,但不提交,然后返回确认。这个阶段和2PC的Prepare阶段类似。"

"第三阶段:DoCommit(最终提交)。如果所有参与者都确认了预提交,协调者发送'最终提交'请求,所有参与者提交本地事务。"

程一念问:"多了一个阶段,有什么好处?"

CodeStats解释道:"好处是------减少了阻塞时间。在2PC中,参与者在Prepare阶段之后就进入了阻塞状态,一直等到协调者发送Commit或Rollback。但在3PC中,参与者在CanCommit阶段不做任何操作,不会进入阻塞状态。只有在PreCommit阶段之后,参与者才会进入阻塞状态------但这个时候,协调者已经有很大概率会发送最终的决策了。"

"而且,3PC引入了'超时机制'------如果参与者在PreCommit阶段之后等待超时,会自动提交事务(而不是一直等待)。因为协调者既然发送了PreCommit,说明所有参与者都同意提交了,最终提交大概率会成功。"

令灵儿若有所思:"所以3PC通过引入'预提交'阶段和超时机制,减少了阻塞的可能性?"

"对。"CodeStats说,"但3PC也有问题------如果网络分区,部分参与者收到PreCommit,部分没有收到,那么收到的那部分会提交,没收到的那部分会回滚,导致数据不一致。"

程一念问:"那怎么解决?"

CodeStats摇头:"没有完美的解决方案。分布式事务的本质,就是在一致性、可用性、性能之间做权衡。2PC保证强一致性,但牺牲了可用性(阻塞)。3PC提高了可用性,但牺牲了一定的一致性(分区时可能不一致)。"

"在实际系统中,真正的选择不是'哪种协议更好',而是'在什么场景下用哪种协议'。对于强一致性要求的场景(比如金融交易),用2PC配合超时和日志恢复。对于高可用要求的场景(比如电商购物车),用最终一致性(TCC或Saga)。"

令灵儿问:"那我们调度中心的事务,属于哪种场景?"

CodeStats想了想:"调度中心的元数据更新------服务注册、心跳续约、版本升级------这些操作需要强一致性,因为如果元数据不一致,整个调度网络就会崩溃。所以,我们的etcd事务用Raft(强一致性)。但跨服务的业务操作------比如部署一个新版本的服务实例------可以用最终一致性,用Saga模式。"

"接下来,我们来学习TCC和Saga。"

第一百四十四章 TCC的补偿之道------预留与确认

CodeStats在事务管理器中创建了一个新的模块------TCC(Try-Confirm-Cancel)事务模式。

"TCC是'业务层面'的分布式事务方案。"CodeStats说,"它不是靠数据库的锁来保证原子性,而是靠业务逻辑的'预留'和'补偿'。"

"TCC的三个阶段------"

"Try(尝试):执行业务的'预留'操作。比如,要扣减库存,不是在Try阶段直接扣减,而是先'锁定'库存------把库存从'可用'状态变为'锁定'状态。其他事务不能使用被锁定的库存。"

"Confirm(确认):如果所有Try都成功,执行Confirm操作------把'锁定'状态变为'已扣减'状态,真正完成扣减。"

"Cancel(取消):如果任何一个Try失败,执行Cancel操作------把'锁定'状态恢复为'可用'状态,释放预留的资源。"

程一念问:"这和我们之前在内存殿学的'两阶段提交'有什么不同?"

CodeStats解释道:"2PC是靠数据库的锁来保证原子性------在Prepare阶段,数据库会锁定资源,其他事务无法访问。TCC是靠业务逻辑来保证原子性------在Try阶段,业务代码负责'预留'资源,而不是靠数据库锁。这样,其他事务可以访问未被预留的资源,并发度更高。"

"举个例子------订机票。2PC的做法是:在Prepare阶段,把座位锁住,其他人都不能买。TCC的做法是:在Try阶段,把座位标记为'预留',但不锁住------其他人可以看到这个座位被预留了,但不会尝试购买。然后Confirm阶段真正扣减,Cancel阶段释放预留。"

令灵儿恍然大悟:"所以TCC是'业务层'的分布式事务,而不是'数据库层'的?"

"对。"CodeStats说,"TCC的好处是------资源锁定时间短,并发度高。坏处是------需要业务代码支持'预留'和'补偿',实现复杂度高。"

他在调度中心搭建了一个TCC模拟场景------部署一个新版本的服务实例,需要三个步骤:1)在容器岛创建Bean定义;2)在线程岛分配线程资源;3)在源纹岛更新元数据。

他用TCC模式实现这个事务:

Try阶段:在容器岛'预留'Bean定义的空间(但不创建),在线程岛'预留'线程资源(但不分配),在源纹岛'预留'元数据的位置(但不写入)。

Confirm阶段:所有Try都成功后,执行Confirm------真正创建Bean定义、分配线程资源、写入元数据。

Cancel阶段:如果任何一个Try失败,执行Cancel------释放所有预留的资源。

"在凡界,TCC是分布式事务中最常用的方案之一。"CodeStats说,"很多电商系统、支付系统都用TCC来处理跨服务的订单操作。它的核心思想是------'先预留,后确认,失败就补偿'。"

程一念说:"我可以用我的九个栈来模拟TCC的多个参与者。每个栈维护自己的状态机------TRYING、CONFIRMED、CANCELLED。栈帧压入和弹出可以对应事务的开始和结束。"

令灵儿也说:"我的指令通道可以传递TCC的Try-Confirm-Cancel三种指令,让参与者在不同状态间转换。"

CodeStats点头:"好。我们来实践一次完整的TCC事务------部署一个新的服务实例。"

三个参与者(容器岛、线程岛、源纹岛)分别执行Try操作,全部成功。然后执行Confirm操作,全部成功。事务完成。

然后,他模拟了一个失败场景------线程岛的Try操作失败(线程资源不足)。容器岛和源纹岛已经执行了Try(预留了资源),但因为线程岛失败,整个事务需要回滚。协调者发送Cancel指令给容器岛和源纹岛,释放预留的资源。

"TCC的回滚,是通过Cancel操作完成的。"CodeStats说,"Cancel操作执行的是'补偿逻辑'------把Try阶段预留的资源释放掉,恢复到Try之前的状态。"

令灵儿说:"这就像'借东西'和'归还'------Try是'说好要借',Confirm是'真正借走',Cancel是'不借了,把东西放回去'。"

"对!"CodeStats笑道,"就是这个道理。TCC的精髓,就是'预留-确认-补偿'的闭环。"

第一百四十五章 Saga的最终选择------补偿事务的终极智慧

CodeStats在事务管理器中创建了最后一个模块------Saga模式。

"Saga是另一种分布式事务方案。"CodeStats说,"它不像TCC那样有明确的Try-Confirm-Cancel三个阶段,而是把一个大事务拆分成一系列'本地事务'(Local Transactions)。每个本地事务执行成功后,触发下一个本地事务。如果某个本地事务失败,就执行'补偿事务'(Compensating Transaction)来回滚之前已经完成的操作。"

程一念问:"这和TCC有什么不同?"

CodeStats解释道:"TCC是'预占'模式------先预留资源,再确认提交。Saga是'渐进'模式------一步一步执行,每一步都提交,如果某一步失败,就逆序执行补偿操作。"

"举个例子------在电商系统中,一个订单创建需要:1)扣减库存;2)创建订单;3)扣减余额;4)发送通知。用Saga实现的话------"

"第一步:扣减库存。执行本地事务(真正扣减),提交。如果失败,整个事务失败。"

"第二步:创建订单。执行本地事务,提交。如果失败,执行第一步的补偿操作(恢复库存)。"

"第三步:扣减余额。执行本地事务,提交。如果失败,执行第二步的补偿操作(取消订单),然后执行第一步的补偿操作(恢复库存)。"

"第四步:发送通知。执行本地事务,提交。如果失败,执行第三步的补偿操作(恢复余额),然后执行第二步、第一步的补偿操作。"

"每一步都真正提交了,不是'预留'。如果某一步失败,就逆序补偿之前已经提交的操作。"

令灵儿问:"那Saga的优势是什么?"

CodeStats说:"Saga的优势是------不需要全局锁,不需要预留资源,并发度更高。每个本地事务都是独立的,提交后就释放资源。缺点是------补偿逻辑复杂,而且如果补偿操作本身失败了,需要人工介入。"

"在凡界,Saga是微服务架构中最流行的分布式事务方案。因为微服务之间通常不能用分布式数据库锁,只能用业务层面的补偿事务来保证最终一致性。"

程一念说:"我的九个栈可以模拟Saga的多个本地事务。每个栈执行一个本地事务,如果失败,就逆序执行补偿操作------就像栈帧的逆序弹出一样。"

令灵儿也说:"我的指令通道可以传递Saga的事务ID和步骤序列,让每个参与者知道自己处于哪个步骤。"

CodeStats点头:"好。我们来实践一次完整的Saga事务------部署一个新版本的服务实例。包含三个本地事务:1)在容器岛创建Bean定义;2)在线程岛分配线程资源;3)在源纹岛更新元数据。如果第3步失败,逆序执行补偿:2的补偿(释放线程资源)、1的补偿(删除Bean定义)。"

三人开始模拟。第一步成功,第二步成功,第三步失败。系统自动执行补偿------先释放线程资源,再删除Bean定义。整个事务虽然没有成功,但系统状态回到了事务开始之前的状态------最终一致性得到了保证。

"TCC和Saga,各有优劣。"CodeStats总结道,"TCC适合'资源预留'类的事务------资源有限,需要提前锁定。Saga适合'长事务'------涉及多个服务,每个服务独立提交,用补偿来保证最终一致性。"

"在调度中心的场景中,元数据更新用etcd的强一致性事务(Raft),业务部署用Saga(最终一致性)。两者互补,覆盖了分布式系统中90%的事务场景。"

令灵儿问:"那剩下的10%呢?"

CodeStats说:"剩下的10%,是那些既需要强一致性、又需要高可用性的场景。在凡界,这种场景通常用'分布式数据库'(如TiDB)来解决------它用Raft保证一致性,用Multi-Raft保证可用性。在源世界,这就相当于------把etcd的共识协议和调度中心的事务协议融合在一起,构建一个'分布式事务+共识'的统一平台。"

"这,就是源世界2.0的最终目标。"

他看向远处,归元圣域的天空中,九座浮空岛在阳光下缓缓旋转。etcd集群已经升级完毕,TCC和Saga的事务框架已经搭建完成,调度中心从"能工作"变成了"能可靠地工作"。

"调度中心的分布式事务基础设施,全部完成了。"CodeStats说,"服务注册与发现、负载均衡、滚动升级、分布式调用链、共识协议、分布式事务------六块拼图全部就位。在凡界,这就是一个完整的'服务治理平台'------相当于Kubernetes+Istio+etcd+分布式事务的合体。"

程一念吹了个口哨:"从MOV指令到分布式事务......我们真的走完了整个技术栈。"

令灵儿看着他,眼中带着光:"接下来呢?"

CodeStats笑了笑,站起身来:"接下来,我们要把这套分布式治理体系,部署到源世界的每一个角落------让指令族、过程族、对象宗、容器道......所有种族都能享受到'分布式事务'带来的可靠性和一致性。"

"下一站,不是秘境,不是浮空岛。"他看向远处的天际线,"下一站------是'全源世界推广'。我们要把源世界2.0的分布式治理体系,变成所有种族都能用的'公共基础设施'。"

远处,金色的云层中,归元圣域的九座浮空岛在阳光下缓缓旋转,等待着它们的创造者,把新的规则注入它们的核心。

CodeStats握紧拳头:"源世界的'服务网格'(Service Mesh),正式启动。"


📢 写在最后:点赞、收藏与下一期预告

如果这个故事让你对etcd的Raft共识、2PC的阻塞困境、3PC的超时优化、TCC的预留-确认-补偿、Saga的最终一致性这些分布式事务和共识概念有了更直观的理解------

点赞 👍:让更多像我们一样,对技术本质充满好奇的道友看到这篇文章。

收藏 ⭐:方便你追更,跟随CodeStats一起,从码基期修炼到源初境。

评论 💬:告诉我你最喜欢哪个技术梗------是Raft日志复制的"APPEND_ENTRIES",还是TCC的"预留-确认-补偿"?

下一期预告:

CodeStats完成分布式事务基础设施后,源世界2.0的调度中心进入了"全量部署"阶段------九座浮空岛逐一接入调度中心,实现统一的服务注册、统一的路由、统一的事务管理。但虚空族的残余势力并未完全消散------一个隐藏在分布式事务日志中的"补偿陷阱",正在悄无声息地蔓延。令灵儿的指令通道将面临"分布式追踪"的终极考验,而程一念的栈帧将首次作为"事务日志存储"被全面调用!

敬请期待《源纹天书》第一百四十六章至第一百五十章:全量部署、服务网格的扩张、分布式追踪的终极考验、补偿陷阱的发现、事务日志的深渊!

相关推荐
JSON_L1 小时前
MongoDB文档关系详解
数据库·mongodb
snow@li1 小时前
Java:后端项目会有一个类似前端node_modules的目录吗 / jar包在本地仓库 ~/.m2/repository 中按版本共存
java·开发语言·前端
ganbingfenxiang1 小时前
太原干冰定制
大数据·python
济*沧*海1 小时前
创建springboot项目时会出现注释乱码情况
java·spring boot·后端
geovindu1 小时前
java: Builder Pattern
java·开发语言·后端·设计模式·建造者模式·生成器模式
卓怡学长1 小时前
w268基于springboot + vue 物流系统
java·数据库·vue.js·spring boot·spring·intellij-idea
跨境数据猎手1 小时前
反向海淘SaaS系统架构拆解与业务技术
大数据·爬虫·系统架构
.Cnn2 小时前
Redis基础day02
java·数据库·redis
能有时光2 小时前
[MAF的Agent管道详解-07]利用AIAgent中间件构建Agent管道
java·数据库