跳至主要內容

分布式

HeChuangJun约 8017 字大约 27 分钟

1. 什么是CAP定理?

  • CAP定理是指分布式系统中, CAP三者不可兼得
    • 一致性(Consistency):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
    • 可用性(Availability):保证每个请求不管成功或者失败都有响应。
    • 分区容忍性(Partition tolerance):分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性或可用性的服务。

2. 为什么CAP不可兼得呢?

  • 分布式系统,分区是必然存在的,所谓分区指的是分布式系统可能出现的字区域网络不通,成为孤立区域的的情况。
  • 保证一致性。则可用性保证不了,因为要等一致
  • 保证可用性。则一致性保证不了,达到一致要时间

3. CAP对应的模型和应用,Zookeeper,Eureka,Nacos,consoul分别属于什么架构?√

  • CA(单机):放弃分区容错性,加强一致性和可用性,集群数据库、xFS文件系统
  • AP:放弃一致性,分区容错性和可用性,一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。系统不保证改变提交以后立即改变集群的状态,但是随着时间的推移最终状态是一致的。->Web缓存、DNS、GOSSIP.MySQL主从异步复制\Redis \Eureka
  • CP:放弃可用性,追求一致性和分区容错性,P(分区)会导致同步时间无限延长,网络问题会直接让整个系统不可用.保证系统改变提交以后立即改变集群的状态。->分布式数据库、分布式锁,paxo,raft,zab.MySQL主从半同步复制\Zookeeper
  • 业界主要采用了 XA 协议的强一致规范以及柔性事务的最终一致规范。

4. BASE理论

  • BASE理论是对CAP中AP的一个扩展,业务系统牺牲一致性来换取系统的可用性和分区容错性。BASE是下面三个短语的缩写
    • Basically Available 基本可用:通过支持局部故障而不是系统全局故障来实现的。如将用户分区在 5 个数据库服务器上,一个用户数据库的故障只影响这台特定主机那 20% 的用户,其他用户不受影响。
    • Soft State 软状态,允许系统中存在中间状态,这个状态不影响系统可用性,这里指的是 CAP 中的不一致。
    • Eventually Consistent 最终一致是指经过一段时间后,所有节点数据都将会达到一致。这个时间取决于网络延时、系统负载、数据复制方案设计等等因素。

5. 为什么需要一致性算法?

  • 数据不能存在单个节点(主机)上,否则可能出现单点故障;多个节点(主机)需要保证具有相同的数据

6. 常用的一致性算法分类?

  • Paxos算法:能够在存在故障节点的情况下保证系统的一致性。
  • Raft算法:比Paxos算法更易于理解和实现的一致性算法,它将分布式一致性问题分解成多个易于处理的子问题。
  • ZAB协议:Zookeeper中使用的一种一致性协议,它通过选举一个leader来协调多个follower之间的数据更新。
  • 2PC协议:分布式系统中常用的一致性协议,它通过预提交和提交两个阶段来保证系统的一致性。
  • 3PC协议:3在2PC协议的基础上增加了超时机制和准备阶段的“可以提交”状态,以提高系统的性能和可靠性。
  • Gossip协议:一种基于随机化的分布式一致性协议,它通过节点之间的随机通讯来传播数据和状态信息,从而达到一致性的目的。

7. Paxos算法

  • Paxos算法是基于消息传递且具有高效容错特性的一致性算法
  • 在Paxos中有这么几个角色,一个节点可以同时充当不同角色。
    • Proposer(提议者):提议者提出提案,用于投票表决。提案=编号+value,可以表示为[M,V],每个提案都有唯一编号,而且编号的大小是趋势递增的
    • Accecptor(接受者):对提案进行投票,并接受达成共识的提案。
    • Learner(学习者):被告知投票的结果,接受达成共识的提案。
    • Proposal(提议)
  • 算法流程:
    • 1.准备阶段(Prepare Phase)
      • 提议者Proposer提议一个编号为N(N必须大于之前本提议者所有Proposal提案的编号)的Proposal提案,然后向接受者Accecptor的某个超过半数的子集成员发送包含Proposal提案的prepare请求,由接受者Accecptor决定哪个请求占大多数
      • 如果一个接受者Accecptor收到包含编号为N的Proposal的prepare请求,并且编号N大于它已经接收的所有prepare请求的编号,然后接受者会返回promise承诺,忽略任何编号小于N的Proposal提案。同时包括已经accept过的最大编号的Proposal提案作为响应反馈给提议者
      • 接受者在收到提案后,会给与提议者两个承诺与一个应答:
        • 两个承诺:承诺忽略提案号小于或等于N的Prepare请求;承诺忽略提案号小于N的Accept请求
        • 一个应答:回复已经accept的提案中提案号最大的那个提案所设定的值NmaxValue和提案号Nmax,如果这个值从来没有被任何提案设定过,则返回空值。如果不满足已经做出的承诺,即收到的提案号并不是决策节点收到过的最大的,那允许直接对此 Prepare 请求不予理会
    • 2.Accept(接受)阶段
      • 如果提议者Proposer收到来自半数以上的接受者对于它发出的编号为N的prepare请求的响应,然后给Proposal提案设置值value,value就是从Accecptor接收者中收到的响应中编号最大的提案的值,如果响应中不包含任何提案,那么它可以随意选定一个值。此时,提议者Proposer会向接受者Accecptor的某个超过半数的子集成员发出已经设置值的Accept Request Message
      • 如果接受者Accecptor收到这个编号为N的Proposal提案的Accept Request Message,如果这个编号N大于接受者Accecptor之前所有返回promise承诺,则接受者Accecptor就会Accept,同时向提议者Proposer和所有学习者Learner发送Accepted Message,其他情况则接受者Accecptor忽略Accept Request Message
      • 注意:接受者Accecptor可以Accept多个Proposal提案,在某些失败情况下,Proposal提案可能有不同的值,但是Paxos算法保证值最终达到一致;当多个提议者Proposer发送冲突的Prepare请求,或者提议者Proposer没有接收到超过半数Promise承诺或者Accepted Message,以上这些情况都会使新一轮提议者发起编号更大的proposal; 当接收者Accecptor接收acceptAccept Requeset Message时,也会选出在提议者Proposer的leader,因此,Paxos算法也适合在集群中选出leader
  • Paxos算法有什么缺点吗?怎么优化?
    • 上述为Basic Paxos 算法,在单提议者的前提下是没有问题的,但是假如有多个提议者互不相让,那么就可能导致整个提议的过程进入了死循环。
    • Lamport 提出了 Multi Paxos 的算法思想。在多个提议者的情况下,选出一个Leader(领导者),由领导者作为唯一的提议者,这样就可以解决提议者冲突的问题。
      ![paxos.png)

7.1. Raft算法

  • Raft算法的角色
    • Leader(领导者)
    • Follower(跟随者)
    • Candidate(候选人)
  • 领导者由跟随者投票选出。刚开始没有 领导者,所有集群中的 参与者 都是 跟随者。所有跟随者 都能参与竞选,这时所有跟随者的角色就变成了 候选人,民主投票选出领袖后就开始了这届领袖的任期Term,然后选举结束,所有除 领导者 的 候选人 又变回 跟随者 服从领导者领导。
  • Leader选举过程
    • Raft 使用心跳(heartbeat)触发Leader选举。当Server启动时,初始化为Follower。Leader向所有Followers周期性发送heartbeat。如果Follower在选举超时时间内没有收到Leader的heartbeat,就会等待一段随机的时间后发起一次Leader选举。Follower将其当前term加一然后转换为Candidate。它首先给自己投票并且给集群中的其他服务器发送 RequestVote RPC 。结果有以下三种情况:
    • 赢得了多数(超过1/2)的选票,成功选举为Leader;
    • 收到了Leader的消息,表示有其它服务器已经抢先当选了Leader;
    • 没有Server赢得多数的选票,Leader选举失败,等待选举时间超时(Election Timeout)后发起下一次选举。
    • 选出 Leader 后,Leader 通过 定期 向所有 Follower 发送 心跳信息 维持其统治。若 Follower 一段时间未收到 Leader 的 心跳,则认为 Leader 可能已经挂了,然后再次发起 选举 过程。

8. 分布式id需要满足什么条件?常见生成方法有哪些?√

  • 全局唯一

  • 高性能:高可用低延时,ID生成响应要块,否则反倒会成为业务瓶颈

  • 高可用:100%的可用性是骗人的,但是也要无限接近于100%的可用性

  • 好接入:要秉着拿来即用的设计原则,在系统设计和实现上要尽可能的简单

  • 趋势递增:最好趋势递增,这个要求就得看具体业务场景了,一般不严格要求

  • Redis是单线程的并且reids中的incr命令是原子自增的。redis是第三方的组件,如果本身系统中就没有使用redis,这时使用redis就会增加系统的负担,因为一旦使用就是集群,而且至少得三个哨兵集群。
    redis如果使用RBD作为持久化,那在一个快照时间内宕机了,此时还未进行吃就会,恢复后会出现ID重复.使用AOF进行持久化,恢复较慢。(至少丢失1s得数据)

  • UUID

  • 雪花算法:twitter开源得分布式id生成方案。其核心思想就是:使用一个64bit得long型数字作为全局唯一id。

  • 第一部分1个bit:0,二进制的最高位为符号位。0表示整数,1表示负数。

  • 第二部分是41bit的时间戳。单位是毫秒。41bit可以表示的数字多达241-1,也就是可以标识241-1毫秒,约等于69年(从1970年开始)。

  • 第三部分5个bit:表示机房id,最多表示2^5个机房

  • 第四部分为5个bit:表示的是机器id。每个房间里可以有2^5个机器。

  • 第五部分12个bit:表示序号,就是某个机房某台机器上一毫秒内同时生成的id的序号。12bit可以代表的最大正整数是2^12-1,一共4096个数,也就是说一毫秒内可以生成4096个唯一id。如果在同一毫秒内,则将序列号递增1,使用掩码(最低12位为1,高位都为0)进行位与运行后如果值为0,则自增后的序列号超过了4095

  • 高性能高可用,生成时不依赖于数据库,完全在内存中生成。容量大:每秒能生成百万的自增id。id自增:时间时自增的,所以生成的id也是自增的。

  • 缺点:依赖与系统时间的一致性,如果系统时间被回调,或者改变,可能造成重复的id。算法中可通过记录最后一个生成 id 时的时间戳来解决,每次生成 id 之前比较当前服务器时钟是否被回拨,避免生成重复 id

  • redis集群的incr方法

9. 如何设计分布式锁?分布式锁怎么实现?√

  • 具备原子性、可重入性;具备锁失效机制,防止死锁;具备非阻塞锁特性,即没获取到锁返回获取锁失败,而不是一直等待
  • 高性能、高可用的获取与释放锁
  • 分布式锁的实现方式:
    • 基于数据库:
      • 用数据库的排他锁实现select * from xxx for update
      • 创建一张锁表,数据库对字段作唯一性约束。加锁的时候,在锁表中增加一条记录即可;释放锁的时候删除记录就行。如果有并发请求同时提交到数据库,数据库会保证只有一个请求能够得到锁。属于数据库 IO 操作,效率不高,而且频繁操作会增大数据库的开销,因此这种方式在高并发、高性能的场景中用的不多。
    • 基于redis:利用redis的set key value NX EX 30000;也可以用redis的第三方库比如Redisson
    • 基于zookeeper:利用zookeeper的临时顺序节点实现;每个线程都是先创建临时顺序节点,然后获取当前目录下最小的节点(序号),判断最小节点是不是当前节点,如果是那么获取锁成功,如果不是那么获取锁失败。获取锁失败的线程获取当前节点上一个临时顺序节点,并对对此节点进行监听,当该节点删除的时候(上一个线程执行结束删除或者是掉线zk删除临时节点)这个线程会获取到通知,代表获取到了锁。公平锁使用临时节点实现,非公平锁使用临时顺序节点实现.也可以用zookeeper的第三方库比如Curator

10. 分布式事务

  • 分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。布式事务需要保证这些分支事务要么全部成功,要么全部失败
distributedtransaction.png
distributedtransaction.png
  • XA方案/2PC二阶段提交

    • XA分布式事务的规范,由X/Open组织提出,定义了(全局)事务管理器(TM)和(局部)资源管理器(RM)之间的接口。主流的数据库基本都支持XA事务,包括mysql、oracle、sqlserver
    • XA事务由一个或多个资源管理器(RM)(数据库)、一个事务管理器(TM)和一个应用程序(ApplicationProgram)组成。
    • XA一共分为两阶段:
      • 第一阶段(prepare)所有的参与者RM准备执行事务并锁住需要的资源。参与者ready时,向TM报告已准备就绪。
      • 第二阶段 (commit/rollback):当事务管理者(TM)确认所有参与者(RM)都ready后,向所有参与者发送commit命令。有任何一个参与者(RM)ready失败,向所有参与者发送rollback命令,若在commit过程中出现宕机等异常时,则在节点服务重启后,可根据 XA recover 再次进行 commit 补偿,以保证数据的一致性。
  • 问题

    • 单点问题:单机事务管理器宕机导致资源管理器阻塞,数据库无法使用.
    • 性能问题:准备就绪后资源一直处于阻塞状态,直到提交完成,释放资源.并发度低
    • 数据一致性问题,因为网络问题部分资源管理器执行了提交,但其他资源管理器没有执行。数据不一致
  • java语言可参考seata、Sharding Sphere、Spring JTA + Atomikos

  • 应用场景:适合单块应用里,跨多个库的分布式事务,而且因为严重依赖于数据库层面来搞定复杂的事务,效率很低,绝对不适合高并发的场景。微服务规定和规范,是要求每个服务只能操作自己对应的一个数据库。如果你要操作别人的服务的库,你必须是通过调用别的服务的接口来实现,绝对不允许交叉访问别人的数据库
    xatransaction.png

  • 3PC(三阶段提交)

    • 为解决两阶段提交协议的单点故障和同步阻塞问题。
    • 三阶段提交有这么三个阶段:CanCommit,PreCommit,DoCommit三个阶段
      • CanCommit:准备阶段。协调者向参与者发送commit请求,参与者如果可以提交就返回Yes响应,否则返回No响应。
      • PreCommit:预提交阶段。协调者根据参与者在准备阶段的响应判断是否执行事务还是中断事务,参与者执行完操作之后返回ACK响应,同时开始等待最终指令。
      • DoCommit:提交阶段。协调者根据参与者在准备阶段的响应判断是否执行事务还是中断事务:
        • 如果所有参与者都返回正确的ACK响应,则提交事务
        • 如果参与者有一个或多个参与者收到错误的ACK响应或者超时,则中断事务
        • 如果参与者无法及时接收到来自协调者的提交或者中断事务请求时,在等待超时之后,会继续进行事务提交
  • 解决的只是两阶段提交中单体故障和同步阻塞的问题,因为加入了超时机制,这里的超时的机制作用于 预提交阶段 和 提交阶段。如果等待 预提交请求 超时,参与者直接回到准备阶段之前。如果等到提交请求超时,那参与者就会提交事务了。

  • 无论是2PC还是3PC都不能保证分布式系统中的数据100%一致。

  • TCC(Try-Confirm-Cancel) 方案(补偿机制)

    • TCC模型针对每个操作,都需要有一个其对应的确认和取消操作,当操作成功时调用确认操作,当操作失败时调用取消操作,把锁的粒度完全交给业务处理,它需要每个子事务业务都实现Try-Confirm/Cancel接口。TCC模式本质也是2PC ,只是TCC在应用层控制。
    • TCC分为3个阶段
      • Try阶段:尝试执行,完成所有业务检查(一致性), 预留必须业务资源(准隔离性)对各个服务的资源做检测以及对资源进行锁定或者预留。
      • Confirm阶段:确认执行真正执行业务,不作任何业务检查,只使用Try阶段预留的业务资源,Confirm操作要求具备幂等设计,Confirm失败后需要进行重试。
      • Cancel阶段:取消执行,释放Try阶段预留的业务资源。Cancel阶段满足幂等设计。如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作。(把那些执行成功的回滚)
    • 通常会在Try里面冻结金额,但不扣款,Confirm里面扣款,Cancel里面解冻金额,一个成功完成的TCC事务时序图如下:
  • 优缺点

    • 规避了数据库的2PC性能低下问题。并发度较高,无长期资源锁定。
    • 一致性较好,不会发生SAGA已扣款最后又转账失败的情况
    • 应用侵入性强,开发量较大,try、confirm、cancel三个阶段都需要业务逻辑实现。需要根据网络、系统故障等不同失败原因实现不同的回滚策略,实现难度大
    • 适用于订单类业务,对中间状态有约束的业务
    • 在需要前置资源锁定的场景,不得不使用 XA 或 TCC 的方式。如下单场景,在订单创建之前,需要扣除优惠券、钱包余额、积分等不得不进行前置多资源锁定,无非是使用 XA 的强锁,还是 TCC 的弱锁。当然,如果能不用 TCC 的情况下,尽量不要用 TCC 。因为,编写回滚逻辑的代码,可能会比较恶心。
    • ByteTCC,TCC-transaction,Himly。java语言可参考seata
      tcctransaction.png
  • SAGA方案

    • Saga是将长事务拆分为多个本地短事务,由Saga事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。
    • Saga组成如下:
      • 每个Saga由一系列sub-transaction Ti 组成
      • 每个Ti都有对应的补偿动作Ci ,补偿动作用于撤销Ti造成的结果。每个T都是一个本地事务。
      • 和TCC相比,Saga没有try动作 ,它的Ti就是直接提交到库
    • Saga的执行顺序有两种:
      • 子事务序列 T1, T2, …, Tn得以完成 (最佳情况)。
      • 或者序列 T1, T2, …, Tj, Cj, …, C2, C1, 0 < j < n, 得以完成。
    • Saga 定义了两种恢复策略:
      • 向后恢复:补偿所有已完成的事务,如果任一子事务失败。(第二种执行顺序)
      • 向前恢复:重试失败的事务,假设每个子事务最终都会成功。向前恢复没有必要提供补偿事务,如果你的业务中,子事务(最终)总会成功,或补偿事务难以定义或不可能,向前恢复更符合你的需求。理论上补偿事务永不失败,然而,在分布式世界中,服务器可能会宕机、网络可能会失败,甚至数据中心也可能会停电,这时需要提供故障恢复后回退的机制,比如人工干预。
    • 如何解决没有 Prepare阶段可能带来的问题?由于 Saga 模型中没有 Prepare 阶段,因此事务间不能保证隔离性,当多个 Saga 事务操作同一资源时,就会产生更新丢失、脏数据读取等问题,这时需要在业务层控制并发。例如:在应用层面加锁。应用层面预先冻结资源。
    • 并发度高,不用像XA事务那样长期锁定资源
    • 需要定义正常操作以及补偿操作,开发量比XA大
    • 一致性较弱,对于转账,可能发生A用户已扣款,最后转账又失败的情况
    • 适用于长事务,对中间结果不敏感的业务场景适用
    • go语言可参考DTM,java语言可参考seata、Apache Service Comb 的 Saga 事务引擎、Sharding Sphere 的 Saga 支持
      sagatransaction.png
  • 本地消息表ebay

    • 在消息发送方的同一个业务数据库中添加一个记录着消息状态相关信息的消息表,保证业务表与消息表在同一个事务,使用定时任务轮询查询状态为未同步的消息表,发送到MQ,如果发送失败,就重试发送
    • 消息消费方处理消息队列中的消息,完成自己的业务逻辑。同时保证不会处理重复消息。如果本地事务处理失败,那么就会重试执行。如果是业务上面的失败,给消息生产方发送一个业务补偿消息,通知进行回滚等操作。使用定时任务轮询查询状态为未同步的消息表,发送到MQ,如果发送失败,就重试发送
    • 优点&缺点:
      • 很好地解决了分布式事务问题,实现了最终一致性。长事务仅需要分拆成多个任务,使用简单
      • 缺点是消息表会耦合到业务系统中。最终一致性的间隔主要有定时任务的间隔时间决定。生产者需要额外的创建消息表。每个本地消息表都需要进行轮询。消费者的逻辑如果无法通过重试成功,那么还需要更多的机制,来回滚操作
    • 适用于可异步执行的业务,且后续操作无需回滚的业务
  • 可靠消息最终一致性方案

    • 阿里开源的RocketMQ4.3之后的版本正式支持事务消息
    • 消息发送方先发送一个prepared消息到mq,如果这个prepared消息发送失败那么就直接取消操作别执行了;如果这个消息发送成功过了,那么接着执行本地事务,如果成功就告诉mq发送确认消息,如果失败就告诉mq回滚消息
    • 如果发送了确认消息,那么此时消息接收方会接收到确认消息,然后执行本地的事务;
    • mq会自动定时轮询所有 prepared 消息回调你的接口,接口里面可以查下数据库看之前本地事务已经提交/回滚/异常,回复确认/回滚/重试。避免本地事务执行成功了,而确认消息却发送失败了。
    • 如果系统B的事务失败了这自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如B系统本地回滚后,想办法通知系统A也回滚;或者是发送报警由人工来手工回滚和补偿。
    • 长事务仅需要分拆成多个任务,并提供一个反查接口,使用简单、消费者的逻辑如果无法通过重试成功,那么还需要更多的机制,来回滚操作
    • 适用于可异步执行的业务,且后续操作无需回滚的业务
    • 不需要再建消息表,对性能的损耗和业务的入侵更小
    • 可参考rocketmq,DTM也提供了简单实现,《RabbitMQ 之消息确认机制(事务+Confirm)》
  • 最大努力通知方案

    • 适用于一些对最终一致性实时性要求没那么高的业务,比如支付通知,短信通知。
    • 发送方提供接口,让接受通知方能够通过接口查询业务处理结果
    • 发送方消息队列ACK机制,消息队列按照间隔1min、5min、10min、30min、1h、2h、5h、10h的方式,逐步拉大通知间隔 ,直到达到通知要求的时间窗口上限。之后不再通知
    • 最大努力通知适用于业务通知类型,例如微信交易的结果,就是通过最大努力通知方式通知各个商户,既有回调通知,也有交易查询接口
    • 以支付通知为例,业务系统调用支付平台进行支付,支付平台进行支付,进行操作支付之后支付平台会去同步通知业务系统支付操作是否成功,如果不成功,会一直异步重试,但是会有一个最大通知次数,如果超过这个次数后还是通知失败,就不再通知,业务系统自行调用支付平台提供一个查询接口,供业务系统进行查询支付操作是否成功。
  • 本地消息表和事务消息都属于可靠消息与最大努力通知区别?

    • 可靠消息一致性,发起通知方需要保证将消息发出去,并且将消息发到接收通知方,消息的可靠性关键由发起通知方来保证。
    • 最大努力通知,发起通知方尽最大的努力将业务处理结果通知为接收通知方,但是可能消息接收不到,此时需要接收通知方主动调用发起通知方的接口查询业务处理结果,通知的可靠性关键在接收通知方。

11. seata实现

  • Seata是从业务无侵入的两阶段提交(全局事务)着手,在传统的两阶段上进行改进,把一个分布式事务理解成一个包含了若干分支事务的全局事务。而全局事务的职责是协调它管理的分支事务达成一致性,要么一起成功提交,要么一起失败回滚

  • Seata中存在这么几种重要角色:

    • TC(Transaction Coordinator):事务协调者。管理全局的分支事务的状态,用于全局性事务的提交和回滚。
    • TM(Transaction Manager):事务管理者。用于开启、提交或回滚事务。
    • RM(Resource Manager):资源管理器。用于分支事务上的资源管理,向TC注册分支事务,上报分支事务的状态,接收TC的命令来提交或者回滚分支事务。
  • Seata整体执行流程

    • 服务A中的TM向TC申请开启一个全局事务,TC就会创建一个全局事务并返回一个唯一的XID
    • 服务A中的RM向TC注册分支事务,然后将这个分支事务纳入 XID 对应的全局事务管辖中
    • 服务A开始执行分支事务
    • 服务A开始远程调用B服务,此时 XID 会根据调用链传播
    • 服务B中的 RM 也向 TC 注册分支事务,然后将这个分支事务纳入 XID 对应的全局事务管辖中
    • 服务B开始执行分支事务
    • 全局事务调用处理结束后,TM 会根据有误异常情况,向 TC 发起全局事务的提交或回滚
    • TC 协调其管辖之下的所有分支事务,决定是提交还是回滚
      seata.png
  • 你们公司是如何处理分布式事务的?

    • 我们某某特别严格的场景,用的是 TCC 来保证强一致性。
    • 其他场基于阿里的 RocketMQ 来实现了分布式事务。如果是一般的分布式事务场景,订单插入之后要调用库存服务更新库存,库存数据没有资金那么的敏感,可以用可靠消息最终一致性方案。

12. 分布式限流算法

12.1. 计数器

  • 比如我们要限制1s能够通过的请求数,实现的思路就是从第一个请求进来开始计时,在接下来的1s内,每个请求进来请求数就+1,超过最大请求数的请求会被拒绝,等到1s结束后计数清零,重新开始计数。弊端:比如前10ms已经通过了最大的请求数,那么后面的990ms的请求只能拒绝,这种现象叫做“突刺现象”

12.2. 漏桶算法

  • 桶底出水的速度恒定,进水的速度可能快慢不一,但是当进水量大于出水量的时候,水会被装在桶里,不会直接被丢弃;但是桶也是有容量限制的,当桶装满水后溢出的部分还是会被丢弃的。可以准备一个队列来保存暂时处理不了的请求,然后通过一个线程池定期从队列中获取请求来执行

12.3. 令牌桶算法

  • 令牌桶就是生产访问令牌的一个地方,生产的速度恒定,用户访问的时候当桶中有令牌时就可以访问,否则将触发限流。Guava RateLimiter是一个谷歌提供的限流,其基于令牌桶算法,比较适用于单实例的系统