分布式理论总结

分布式理论,架构设计

分布式理论

分布式系统架构回顾

  • 概念:分布式系统是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。

    所谓分布式系统,就是一个业务拆分成多个子业务,分布在不同的服务器节点,共同构成的系统称为分布式系统,同一个分布式系统中的服务器节点在空间部署上是可以随意分布的,这些服务器可能放在不同的机柜中,也可能在不同的机房中,甚至分布在不同的城市。

  • 特点

    • (1)分布性(2)对等性(3)并发性(4)缺乏全局时钟(5)故障总是会发生
  • 分布式架构发展大致过程

    • https://mp.weixin.qq.com/s/bFMObUDJI1zwChirDK6RKQ
  • 面临的问题

    • 通信异常

      网络本身的不可靠性,因此每次网络通信都会伴随着网络不可用的风险(光纤、路由、DNS等硬件设备或系统的不
      可用),都会导致最终分布式系统无法顺利进行一次网络通信,另外,即使分布式系统各节点之间的网络通信能够
      正常执行,其延时也会大于单机操作,存在巨大的延时差别,也会影响消息的收发过程,因此消息丢失和消息延迟
      变的非常普遍。

    • 网络分区

      网络之间出现了网络不连通,但各个子网络的内部网络是正常的,从而导致整个系统的网络环境被切分成了若干个
      孤立的区域,分布式系统就会出现局部小集群,在极端情况下,这些小集群会独立完成原本需要整个分布式系统才
      能完成的功能,包括数据的事务处理,这就对分布式一致性提出非常大的挑战

    • 节点故障

      节点故障是分布式系统下另一个比较常见的问题,指的是组成分布式系统的服务器节点出现的宕机或"僵死"现象,
      根据经验来说,每个节点都有可能出现故障,并且经常发生

    • 三态

      分布式系统每一次请求与响应存在特有的“三态”概念,即成功、失败和超时。
      分布式系统中,由于网络是不可靠的,虽然绝大部分情况下,网络通信能够接收到成功或失败的响应,但当网络出
      现异常的情况下,就会出现超时现象,通常有以下两种情况:

      1. 由于网络原因,该请求并没有被成功的发送到接收方,而是在发送过程就发生了丢失现象。
      2. 该请求成功的被接收方接收后,并进行了处理,但在响应反馈给发送方过程中,发生了消息丢失现象。

一致性

  • 概念:分布式数据一致性,指的是数据在多份副本中存储时,各副本中的数据是一致的。

  • 一致性分类 / 级别

    • 强一致性

      • 这种一致性级别是最符合用户直觉的,它要求系统写入什么,读出来的也会是什么,用户体验好,但实现起来往往对系统的性能影响大。但是强一致性很难实现
    • 弱一致性

      这种一致性级别约束了系统在写入成功后,不承诺立即可以读到写入的值,也不承诺多久之后数据能够达到一致,
      但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态。

      • 读写一致性

        • 用户读取自己写入结果的一致性,保证用户永远能够第一时间看到自己更新的内容。

          解决方案:
          方案1:一种方案是对于一些特定的内容我们每次都去主库读取。 (问题主库压力大)

          方案2:我们设置一个更新时间窗口,在刚刚更新的一段时间内,我们默认都从主库读取,过了这个窗口之后,我们会挑选最近有过更新的从库进行读取

          方案3:我们直接记录用户更新的时间戳,在请求的时候把这个时间戳带上,凡是最后更新时间小于这个时间戳的从库都不予以响应

      • 单调读一致性

        • 本次读到的数据不能比上次读到的旧。

          解决方案:就是根据用户ID计算一个hash值,再通过hash值映射到机器。同一个用户不管怎么刷新,都只会被映射到同
          一台机器上。这样就保证了不会读到其他从库的内容,带来用户体验不好的影响。

      • 因果一致性

        • 指的是:如果节点 A 在更新完某个数据后通知了节点 B,那么节点 B 之后对该数据的访问和修改都是基于 A 更新后 的值。于此同时,和节点 A 无因果关系的节点 C 的数据访问则没有这样的限制。
      • 最终一致性

        • 最终一致性是所有分布式一致性模型当中最弱的。可以认为是没有任何优化的“最”弱一致性,它的意思是说,我不考虑 所有的中间状态的影响,只保证当没有新的更新之后,经过一段时间之后,最终系统内所有副本的数据是正确的

CAP定理

  • 概念:一个分布式系统不可能同时满足一致性(C:Consistency)(强一致性),可用性(A: Availability)和分区容错性(P:Partition tolerance)这三个基本需求,最多只能同时满足其中的2个。

    C 一致性
    分布式系统当中的一致性指的是所有节点的数据一致,或者说是所有副本的数据一致

    A 可用性
    Reads and writes always succeed. 也就是说系统一直可用,而且服务一直保持正常

    P 分区容错性
    系统在遇到一些节点或者网络分区故障的时候,仍然能够提供满足一致性和可用性的服务

BASE理论

  • 阐述:BASE是对CAP中一致性和可用性权衡的结果,BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。

    BASE:全称:Basically Available(基本可用),Soft state(软状态),和 Eventually consistent(最终一致性)三个
    短语的缩写

  • Basically Available(基本可用)

    • 基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性——但请注意,这绝不等价于系统不可用

      响应时间上的损失:正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出
      现故障(比如系统部分机房发生断电或断网故障),查询结果的响应时间增加到了1~2秒。

      功能上的损失:正常情况下,在一个电子商务网站(比如淘宝)上购物,消费者几乎能够顺利地完成每一笔
      订单。但在一些节日大促购物高峰的时候(比如双十一、双十二),由于消费者的购物行为激增,为了保护
      系统的稳定性(或者保证一致性),部分消费者可能会被引导到一个降级页面

  • Soft state(软状态)

    • 软状态指的是:允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本之间进行数据同步的过程中存在延迟。
  • Eventually consistent(最终一致性)

    • 最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此最终
      一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

一致性协议2PC

  • 描述:2PC ( Two-Phase Commit缩写)即两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Prepare
    phase)、提交阶段(commit phase),2是指两个阶段,P是指准备阶段,C是指提交阶段

  • 过程:

    • 1 准备阶段:

      • 事务管理器给每个参与者发送Prepare消息,每个数据库参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有提交。 (Undo日志是记录修改前的数据,用于数据库回滚,Redo日志是记录修改后的数据,用于提交事务后写入数 据文件)
    • 2 提交阶段:

      • 如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据事务管理器的指令执行提交或者回滚操作,并释放事务处理过程中使用的锁资源。注意:必须在最后阶段释放锁资源。
  • 执行流程

    • 成功执行

      • 阶段一: 1. 事务询问 协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者的响应。 2. 执行事务 (写本地的Undo/Redo日志) 3. 各参与者向协调者反馈事务询问的响应 总结: 各个参与者进行投票是否让事务进行
      • 阶段二: 1. 发送提交请求: 协调者向所有参与者发出 commit 请求。 2. 事务提交: 参与者收到 commit 请求后,会正式执行事务提交操作,并在完成提交之后释放整个事务执行期间占用的事务资 源。 3. 反馈事务提交结果: 参与者在完成事务提交之后,向协调者发送 Ack 信息。 4. 完成事务: 协调者接收到所有参与者反馈的 Ack 信息后,完成事务。
    • 中断执行

      • 阶段一: 同成功执行过程
      • 阶段二: 1. 发送回滚请求: 协调者向所有参与者发出 Rollback 请求。 2. 事务回滚: 参与者接收到 Rollback 请求后,会利用其在阶段一中记录的 Undo 信息来执行事务回滚操作,并在完成回滚之后 释放在整个事务执行期间占用的资源。 3. 反馈事务回滚结果: 参与者在完成事务回滚之后,向协调者发送 Ack 信息。 4. 中断事务: 协调者接收到所有参与者反馈的 Ack 信息后,完成事务中断。 从上面的逻辑可以看出,二阶段提交就做了2个事情:投票,执行。
  • 优缺点

    • 优点: 原理简单,实现方便

    • 缺点

      • 1 同步阻塞, 2 单点问题, 3数据不一致, 4数据不一致,过于保守

一致性协议 3PC

  • 描述:3PC,全称 “three phase commit”,是 2PC 的改进版,将 2PC 的 “提交事务请求” 过程一分为二,共形成了由CanCommit、PreCommit和doCommit三个阶段组成的事务处理协议。

  • 流程

    • 第一个阶段: CanCommit
      ① 事务询问 协调者向所有的参与者发送一个包含事务内容的canCommit请求,询问是否可以执行事务提交操作,并开始等待各参与者的响应。
      ② 各参与者向协调者反馈事务询问的响应 参与者在接收到来自协调者的包含了事务内容的canCommit请求后,正常情况下,如果自身认为可以顺利执行事务,则反馈Yes响应,并进入预备状态,否则反馈No响应。

    • 阶段二:PreCommit
      协调者在得到所有参与者的响应之后,会根据结果有2种执行操作的情况:执行事务预提交,或者中断事务
      假如所有参与反馈的都是Yes,那么就会执行事务预提交。

      1. 执行事务预提交分为 3 个步骤
        ① 发送预提交请求:
        协调者向所有参与者节点发出preCommit请求,并进入prepared阶段。
        ② 事务预提交:
        参与者接收到preCommit请求后,会执行事务操作,并将Undo和Redo信息记录到事务日志中。
        ③ 各参与者向协调者反馈事务执行的结果:
        若参与者成功执行了事务操作,那么反馈Ack
        若任一参与者反馈了No响应,或者在等待超时后,协调者尚无法接收到所有参与者反馈,则中断事务
  2. 中断事务也分为2个步骤: 
  ① 发送中断请求: 
  协调者向所有参与者发出abort请求。 
  ② 中断事务: 
  无论是收到来自协调者的abort请求或者等待协调者请求过程中超时,参与者都会中断事务

- 阶段三:do Commit

该阶段做真正的事务提交或者完成事务回滚,所以就会出现两种情况

  1. 执行事务提交 
  ① 发送提交请求: 
  进入这一阶段,假设协调者处于正常工作状态,并且它接收到了来自所有参与者的Ack响应,那么他将从预提交状 
  态转化为提交状态,并向所有的参与者发送doCommit请求。 
  ② 事务提交: 
  参与者接收到doCommit请求后,会正式执行事务提交操作,并在完成提交之后释放整个事务执行过程中占用的事 
  务资源。 
  ③ 反馈事务提交结果: 
  参与者在完成事务提交后,向协调者发送Ack响应。 
  ④ 完成事务: 
  协调者接收到所有参与者反馈的Ack消息后,完成事务。 
  
  
  2. 中断事务 
  ① 发送中断请求:协调者向所有的参与者节点发送abort请求。 
  ② 事务回滚:参与者收到abort请求后,会根据记录的Undo信息来执行事务回滚,并在完成回滚之后释放整 
  个事务执行期间占用的资源。 
  ③ 反馈事务回滚结果:参与者在完成事务回滚后,向协调者发送Ack消息。④ 中断事务:协调者接收到所有参与者反馈的Ack消息后,中断事务。

    - 注意:一旦进入阶段三,可能会出现 2 种故障:
  1. 协调者出现问题
  2. 协调者和参与者之间的网络故障
    如果出现了任一一种情况,最终都会导致参与者无法收到 doCommit 请求或者 abort 请求,针对这种情况,参与者都会在等待超时之后,继续进行事务提交
  • 对比2PC

    • 1.首先对于协调者和参与者都设置了超时机制(在2PC中,只有协调者拥有超时机制,即如果在一定时间内没有收到参与者的消息则默认失败),主要是避免了参与者在长时间无法与协调者节点通讯(协调者挂掉了)的情况下,无法释放资源的问题,因为参与者自身拥有超时机制会在超时后,自动进行本地commit从而进行释放资源。而这种机制也侧面降低了整个事务的阻塞时间和范围。

2.通过CanCommit、PreCommit、DoCommit三个阶段的设计,相较于2PC而言,多设置了一个缓冲阶段保证了在最后提交阶段之前各参与节点的状态是一致的 。

        - 

3.PreCommit是一个缓冲,保证了在最后提交阶段之前各参与节点的状态是一致的。

  • 问题:3PC协议并没有完全解决数据不一致问题。

一致性算法Paxos

  • 描述:一种基于消息传递的分布式一致性算法

    ZooKeeper,以及MySQL 5.7推出的用来取代传统的主从复制的MySQL Group Replication等纷纷采用Paxos算法
    解决分布式一致性问题

    • 解决了分布式系统的一致性问题
  • 相关概念:

提案 (Proposal):Proposal信息包括提案编号 (Proposal ID) 和提议的值 (Value) 最终要达成一致的value就在提案里。
- 角色

    - Client: 客户端

        - 客户端向分布式系统发出请求,并等待响应。例如,对分布式文件服务器中文件的写请求。

    - Proposer:提案者(可以有多个)

        - 提案者提倡客户请求,试图说服Acceptor对此达成一致,并在发生冲突时充当协调者以推动协议向前发展

    - Acceptor:决策者,可以批准提案(可以有多个)

        - Acceptor可以接受(accept)提案;如果某个提案被选定(chosen),那么该提案里的value就被选定了

    - Learners:最终决策的学习者(可以有多个)

        - 学习者充当该协议的复制因素

- 假设有一组可以提出提案的进程集合,那么对于一个一致性算法需要保证以下几点:

    - 在这些被提出的提案中,只有一个会被选定
    - 如果没有提案被提出,就不应该有被选定的提案。
    - 当一个提案被选定后,那么所有进程都应该能学习(learn)到这个被选定的value
  • 推导过程

    • 几个约束:一个提案被选定需要被半数以上的Acceptor接受

      • P1:一个Acceptor必须接受它收到的第一个提案

      • P2:如果某个value为v的提案被选定了,那么每个编号更高的被选定提案的value必须也是v。

        • P2a:如果某个value为v的提案被选定了,那么每个编号更高的被Acceptor接受的提案的value必须也是v。
        • P2b:如果某个value为v的提案被选定了,那么之后任何Proposer提出的编号更高的提案的value必须也是v。
        • P2c:对于任意的Mn和Vn,如果提案[Mn,Vn]被提出,那么肯定存在一个由半数以上的Acceptor组成的集合S,满足以下 两个条件中的任意一个: * 要么S中每个Acceptor都没有接受过编号小于Mn的提案。 * 要么S中所有Acceptor批准的所有编号小于Mn的提案中,编号最大的那个提案的value值为Vn
  • Proposer生成提案,Acceptor接受提案规则

    这里有个比较重要的思想:Proposer生成提案之前,应该先去『学习』已经被选定或者可能被选定的value,然后以该value作为自己提出的提案的value。如果没有value被选定,Proposer才可以自己决定value的值。这样才能达成一致。这个学习的阶段是通过一个『Prepare请求』实现的。

    • Proposer生成提案

      • prepare请求

        • Proposer选择一个新的提案编号N,然后向某个Acceptor集合(半数以上)发送请求,要求该集合中的每个Acceptor做出如下响应(response) (a) Acceptor向Proposer承诺保证不再接受任何编号小于N的提案。 (b) 如果Acceptor已经接受过提案,那么就向Proposer反馈已经接受过的编号小于N的,但为最大编号的提案的值。
      • accept请求

        • 如果Proposer收到了半数以上的Acceptor的响应,那么它就可以生成编号为N,Value为V的提案[N,V]。这里
          的V是所有的响应中编号最大的提案的Value。如果所有的响应中都没有提案,那 么此时V就可以由Proposer
          自己选择。生成提案后,Proposer将该提案发送给半数以上的Acceptor集合,并期望这些Acceptor能接受该提案。我们称该请求为Accept请求。
    • Acceptor接受提案

      • Prepare请求:Acceptor可以在任何时候响应一个Prepare请求
        Accept请求:在不违背Accept现有承诺的前提下,可以任意响应Accept请求

        • P1A 一个Acceptor只要尚未响应过任何编号大于N的Prepare请求,那么他就可以接受这个编号为N的提案。
      • 算法优化:如果Acceptor收到一个编号为N的Prepare请求,在此之前它已经响应过编号大于N的Prepare请求。根据P1a,该 Acceptor不可能接受编号为N的提案。因此,该Acceptor可以忽略编号为N的Prepare请求。

        通过这个优化,每个Acceptor只需要记住它已经批准的提案的最大编号以及它已经做出Prepare请求响应的提案的最大编号,以便出现故障或节点重启的情况下,也能保证P2c的不变性,而对于Proposer来说,只要它可以保证不会产生具有相同编号的提案,那么就可以丢弃任意的提案以及它所有的运行时状态信息

  • Paxos算法描述

    • 阶段一:
      (a) Proposer选择一个提案编号N,然后向半数以上的Acceptor发送编号为N的Prepare请求。
      (b) 如果一个Acceptor收到一个编号为N的Prepare请求,且N大于该Acceptor已经响应过的所有Prepare请求的编号,那么它就会将它已经接受过的编号最大的提案(如果有的话)作为响应反馈给Proposer,同时该Acceptor承诺不再接受任何编号小于N的提案。

阶段二:
(a) 如果Proposer收到半数以上Acceptor对其发出的编号为N的Prepare请求的响应,那么它就会发送一个针对[N,V]提案的Accept请求给半数以上的Acceptor。注意:V就是收到的响应中编号最大的提案的value,如果响应中不包含任何提案,那么V就由Proposer自己决定。
(b) 如果Acceptor收到一个针对编号为N的提案的Accept请求,只要该Acceptor没有对编号大于N的Prepare请求做出过响应,它就接受该提案。

  • Learner学习被选定的value

    方案一:
    Learner获取一个已经被选定的提案的前提是,该提案已经被半数以上的Acceptor批准,因此,最简单的
    做法就是一旦Acceptor批准了一个提案,就将该提案发送给所有的Learner
    很显然,这种做法虽然可以让Learner尽快地获取被选定的提案,但是却需要让每个Acceptor与所有的Learner逐
    个进行一次通信,通信的次数至少为二者个数的乘积

方案二:
另一种可行的方案是,我们可以让所有的Acceptor将它们对提案的批准情况,统一发送给一个特定的Learner(称
为主Learner), 各个Learner之间可以通过消息通信来互相感知提案的选定情况,基于这样的前提,当主Learner
被通知一个提案已经被选定时,它会负责通知其他的learner
在这种方案中,Acceptor首先会将得到批准的提案发送给主Learner,再由其同步给其他Learner.因此较方案一而
言,方案二虽然需要多一个步骤才能将提案通知到所有的learner,但其通信次数却大大减少了,通常只是
Acceptor和Learner的个数总和,但同时,该方案引入了一个新的不稳定因素:主Learner随时可能出现故障

方案三:
在讲解方案二的时候,我们提到,方案二最大的问题在于主Learner存在单点问题,即主Learner随时可能出现故
障,因此,对方案二进行改进,可以将主Learner的范围扩大,即Acceptor可以将批准的提案发送给一个特定的
Learner集合,该集合中每个Learner都可以在一个提案被选定后通知其他的Learner。这个Learner集合中的
Learner个数越多,可靠性就越好,但同时网络通信的复杂度也就越高

  • 如何保证Paxos算法的活性:

    • 活性:最终一定会发生的事情:最终一定要选定value
    • 假设有两个Proposer依次提出编号递增的提案,最终会陷入死循环,没有value被选定(无法保证活性)
    • 解决:通过选区主Proposer,并规定只有主Proposer才能提出提案,这样一来只有主Proposer和过半的Acceptor能够正常进行网络通信,那么但凡主Proposer提出一个编号更高的天,该提案最终将会被批准,这样通过选择一个Proposer,整套Paxos算法就能够保持活性

一致性算法Raft

  • 描述: Raft是一种为了管理复制日志的一致性算法, Raft将一致性算法分解成了3各模块:1领导人选举,2日志复制,3安全性

  • 两个阶段

    • 领导人Leader选举

      • 描述:Raft 通过选举一个领导人,然后给予他全部的管理复制日志的责任来实现一致性。

        • 在Raft中,任何时候一个服务器都可以扮演下面的角色之一:
          领导者(leader):处理客户端交互,日志复制等动作,一般一次只有一个领导者
          候选者(candidate):候选者就是在选举过程中提名自己的实体,一旦选举成功,则成为领导者
          跟随者(follower):类似选民,完全被动的角色,这样的服务器等待被通知投票
          而影响他们身份变化的则是 选举
      • 过程:

        • 初始状态下集群中的所有节点都处于 follower 状态。
        • 某一时刻,其中的一个 follower 由于没有收到 leader 的 heartbeat 率先发生 election timeout 进而发起选举。
        • 只要集群中超过半数的节点接受投票,candidate 节点将成为即切换 leader 状态。
        • 成为 leader 节点之后,leader 将定时向 follower 节点同步日志并发送 heartbeat。
      • 异常及解决方案

        • leader不可用

          • 一般情况下,leader 节点定时发送 heartbeat 到 follower 节点。

            • 由于某些异常导致 leader 不再发送 heartbeat ,或 follower 无法收到 heartbeat 。

              • 当某一 follower 发生 election timeout 时,其状态变更为 candidate,并向其他 follower 发起投票。
          • 当超过半数的 follower 接受投票后,这一节点将成为新的 leader,leader 的步进数加 1 并开始向 follower 同步日志。

            • 当一段时间之后,如果之前的 leader 再次加入集群,则两个 leader 比较彼此的步进数,步进数低的 leader 将切换自己的状态为 follower。

              • ➢ 较早前 leader 中不一致的日志将被清除,并与现有 leader 中的日志保持一致
        • folloer不可用

          • 流程

            集群中的某个 follower 节点发生异常,不再同步日志以及接收 heartbeat

            经过一段时间之后,原来的 follower 节点重新加入集群。

            这一节点的日志将从当时的 leader 处同步。

        • 多个candidate或多个leader

          • 在集群中出现多个 candidate 或多个 leader 通常是由于数据传输不畅造成的。出现多个 leader 的情况相对少见,但多个 candidate 比较容易出现在集群节点启动初期尚未选出 leader 的“混沌”时期。

            初始状态下集群中的所有节点都处于 follower 状态。

            两个节点同时成为 candidate 发起选举

            两个 candidate 都只得到了少部分 follower 的接受投票。

            candidate 继续向其他的 follower 询问。

            由于一些 follower 已经投过票了,所以均返回拒绝接受。

            candidate 也可能向一个 candidate 询问投票。

            在步进数相同的情况下,candidate 将拒绝接受另一个 candidate 的请求。➢ 由于第一次未选出 leader,candidate 将随机选择一个等待间隔(150ms ~ 300ms)再次发起投票。

            如果得到集群中半数以上的 follower 的接受,这一 candidate 将成为 leader。

            稍后另一个 candidate 也将再次发起投票。

            由于集群中已经选出 leader,candidate 将收到拒绝接受的投票。

              在被多数节点拒绝之后,并已知集群中已存在 leader 后,这一 candidate 节点将终止投票请求、切换为follower,从 leader 节点同步日志。

        - 新节点接入集群

- 日志复制

  Leader选出后,就开始接收客户端的请求。Leader把请求作为日志条目(Log entries)加入到它的日志中, 
  然后并行的向其他服务器发起 AppendEntries RPC复制日志条目。当这条日志被复制到大多数服务器上,Leader 
  将这条日志应用到它的状态机并向客户端返回执行结果。

    - 步骤:

      可以看到,直到第四步骤,整个事务才会达成。中间任何一个步骤发生故障,都不会影响日志一致性。

        - 客户端的每一个请求都包含被复制状态机执行的指令。
        - leader把这个指令作为一条新的日志条目添加到日志中,然后并行发起 RPC 给其他的服务器,让他们复制这条信息。
        - 跟随者响应ACK,如果 follower 宕机或者运行缓慢或者丢包,leader会不断的重试,直到所有的 follower 最终都复制了所有的日志条目。
        - 通知所有的Follower提交日志,同时领导人提交这条日志到自己的状态机中,并返回给客户端

分布式架构网络通信

在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技
术,例如:RMI、Hessian、SOAP、ESB和JMS等,它们背后到底是基于什么原理实现的呢

基本原理: 要实现网络机器间的通讯,首先得来看看计算机系统网络通信的基本原理,在底层层面去看,网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络IO来实现,其中传输协议比较出名的有tcp、

udp等等,tcp、udp都是在基于Socket概念上为某类应用场景而扩展出的传输协议,网络IO,主要有bio、nio、
aio三种方式,所有的分布式应用通讯都基于这个原理而实现,只是为了应用的易用,各种语言通常都会提供一些
更为贴近应用易用的应用层协议

RPC

  • RPC描述

    • RPC全称为remote procedure call,即远程过程调用。
      借助RPC可以做到像本地调用一样调用远程服务,是一种进程间的通信方式
  • 架构

    • 客户端(Client),服务的调用方。
    • 客户端存根(Client Stub),存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。
    • 服务端(Server),真正的服务提供者。
    • 服务端存根(Server Stub),接收客户端发送过来的消息,将消息解包,并调用本地的方法。
  • 过程

    (1) 客户端(client)以本地调用方式(即以接口的方式)调用服务;
    (2) 客户端存根(client stub)接收到调用后,负责将方法、参数等组装成能够进行网络传输的消息体(将消息体对
    象序列化为二进制);
    (3) 客户端通过sockets将消息发送到服务端;
    (4) 服务端存根( server stub)收到消息后进行解码(将消息对象反序列化);
    (5) 服务端存根( server stub)根据解码结果调用本地的服务;
    (6) 本地服务执行并将结果返回给服务端存根( server stub);
    (7) 服务端存根( server stub)将返回结果打包成消息(将结果消息对象序列化);
    (8) 服务端(server)通过sockets将消息发送到客户端;
    (9) 客户端存根(client stub)接收到结果消息,并进行解码(将结果消息发序列化);
    (10) 客户端(client)得到最终结果。
    RPC的目标是要把2、3、4、7、8、9这些步骤都封装起来

RMI远程调用方法

  • 简介:Java RMI 指的是远程方法调用 (Remote Method Invocation),是java原生支持的远程调用 ,采用JRMP(Java Remote Messageing protocol)作为通信协议,可以认为是纯java版本的分布式远程调用解决方案, RMI主要用于不同虚拟机之间的通信,这些虚拟机可以在不同的主机上、也可以在同一个主机上,这里的通信可以理解为一个虚拟机上的对象调用另一个虚拟机上对象的方法。

  • 组成

    • 客户端

      • 1)存根/桩(Stub):远程对象在客户端上的代理;
      • 2)远程引用层(Remote Reference Layer):解析并执行远程引用协议;
      • 3)传输层(Transport):发送调用、传递远程方法参数、接收远程方法执行结果。
    • 服务端

      • 1)骨架(Skeleton):读取客户端传递的方法参数,调用服务器方的实际对象方法,并接收方法执行后的返回值;
      • 2)远程引用层(Remote Reference Layer):处理远程引用后向骨架发送远程方法调用;
      • 3)传输层(Transport):监听客户端的入站连接,接收并转发调用到远程引用层。
    • 注册表(Registry):以URL形式注册远程对象,并向客户端回复对远程对象的引用。

  • 远程调用过程

    1)客户端从远程服务器的注册表中查询并获取远程对象引用。

    2)桩对象与远程对象具有相同的接口和方法列表,当客户端调用远程对象时,实际上是由相应的桩对象代理完成的。

    3 )远程引用层在将桩的本地引用转换为服务器上对象的远程引用后,再将调用传递给传输层(Transport),由传输层通
    过TCP协议发送调用;

    4)在服务器端,传输层监听入站连接,它一旦接收到客户端远程调用后,就将这个引用转发给其上层的远程引用层;

    5)服务器端的远程引用层将客户端发送的远程应用转换为本地虚拟机的引用后,再将请求传递给骨架(Skeleton);

    6)骨架读取参数,又将请求传递给服务器,最后由服务器进行实际的方法调用。

BIO,NIO,AIO

  • 同步异步,阻塞非阻塞

    • IO的两个阶段:查看数据是否就绪,进行数据拷贝(内核将数据拷贝到用户线程)

    • 同步与异步

      • 同步和异步的概念描述的是用户线程与内核的交互方式: 同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;而异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。
        同步IO和异步IO的关键区别反映在数据拷贝阶段是由用户线程完成还是内核完成。所以说异步IO必须要有操作系统的底层支持。同步IO数据拷贝由用户线程完成,异步则为内核线程完成。
    • 阻塞与非阻塞

      • 阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式: 阻塞是指IO操作需要彻底完成后才返回到用户空间;而非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。阻塞与非阻塞主要是从CPU的消耗上来说的,阻塞就是CPU停下来等待一个慢的操作完成以后,CPU才接着完成其他工作。非阻塞就是在这个慢的操作执行时,CPU去做其他工作,等着这个慢的操作完成时,CP再接着完成后续的操作。阻塞IO和非阻塞IO是反映在IO操作的第一个阶段,在查看数据是否就绪时是如何处理的

Netty

  • NIO缺点及Netty改进

    • nio缺点:

      • NIO 的类库和 API 繁杂,使用麻烦。你需要熟练掌握 Selector、ServerSocketChannel、SocketChannel、ByteBuffer 等.
      • 可靠性不强,开发工作量和难度都非常大
      • NIO 的 Bug。例如 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%
    • netty优点

      • 对各种传输协议提供统一的 API

        • 高度可定制的线程模型——单线程、一个或多个线程池
      • 更好的吞吐量,更低的等待延迟

        • 更少的资源消耗

          • 最小化不必要的内存拷贝
  • 单线程模型

  • 线程池模型

  • Netty线程模型

    • netty模型

      • Netty 抽象出两组线程池, BossGroup 专门负责接收客 户端连接, WorkerGroup 专门负责网络读写操作。
        NioEventLoop 表示一个不断循环执行处理 任务的线程, 每个 NioEventLoop 都有一个 selector, 用于监听绑定在其上的 socket 网络通道。 NioEventLoop 内部采用串行化设计, 从消息的读取->解码->处理->编码->发送, 始终由 IO 线 程 NioEventLoop 负责。
  • Netty核心组件

    • ChannelHandler及其实现类

      • 我们经常需要自定义一个 Handler 类去继承 ChannelInboundHandlerAdapter, 然后通过 重写相应方法实现业务逻辑,
    • ChannelPipeline

      addFirst(ChannelHandler... handlers), 把一个业务处理类(handler) 添加到链中的第一 个位置

      addLast(ChannelHandler... handlers), 把一个业务处理类(handler) 添加到链中的最后 一个位置

      • 是一个 Handler 的集合, 它负责处理和拦截 inbound 或者 outbound 的事 件和操作, 相当于一个贯穿 Netty 的链。
    • ChannelHandlerContext

      • ChannelFuture close(), 关闭通道

      • ChannelOutboundInvoker flush(), 刷新

      • ChannelFuture writeAndFlush(Object msg) , 将 数 据 写 到 ChannelPipeline 中

      • ChannelHandler 的下一个 ChannelHandler 开始处理(出站)

        • 这 是 事 件 处 理 器 上 下 文 对 象 , Pipeline 链 中 的 实 际 处 理 节 点 。 每 个 处 理 节 点ChannelHandlerContext 中 包 含 一 个 具 体 的 事 件 处 理 器 ChannelHandler , 同 时
          ChannelHandlerContext 中也绑定了对应的 pipeline 和 Channel 的信息
    • ChannelFuture

      Channel channel(), 返回当前正在进行 IO 操作的通道

      ChannelFuture sync(), 等待异步操作执行完毕

      • 表示 Channel 中异步 I/O 操作的结果, 在 Netty 中所有的 I/O 操作都是异步的, I/O 的调 用会直接返回, 调用者
        并不能立刻获得结果, 但是可以通过 ChannelFuture 来获取 I/O 操作 的处理状态。
    • EventLoopGroup 和其实现类 NioEventLoopGroup

      • public NioEventLoopGroup(), 构造方法

      • public Future shutdownGracefully(), 断开连接, 关闭线程

        • EventLoopGroup 是一组 EventLoop 的抽象, Netty 为了更好的利用多核 CPU 资源, 一般 会有多个 EventLoop
          同时工作, 每个 EventLoop 维护着一个 Selector 实例。 EventLoopGroup 提供 next 接口, 可以从组里面按照一
          定规则获取其中一个 EventLoop 来处理任务。 在 Netty 服务器端编程中, 我们一般都需要提供两个EventLoopGroup
    • ServerBootstrap 和 Bootstrap

      group(EventLoopGroup parentGroup, EventLoopGroup childGroup),该方法用于 服务器端, 用来设置两个 EventLoop

      • public B group(EventLoopGroup group) , 该方法用于客户端, 用来设置一个 EventLoop

      • public B channel(Class channelClass), 该方法用来设置一个服务器端的通道实现

      • public B option(ChannelOption option, T value), 用来给 ServerChannel 添加配置

      • public ServerBootstrap childOption(ChannelOption childOption, T value), 用来给接收到的
        通道添加配置

      • public ServerBootstrap childHandler(ChannelHandler childHandler), 该方法用来设置业务处理类(自定
        义的 handler)

      • public ChannelFuture bind(int inetPort) , 该方法用于服务器端, 用来设置占用的端口号

      • public ChannelFuture connect(String inetHost, int inetPort) 该方法用于客户端, 用来连接服务器端

        • ServerBootstrap 是 Netty 中的服务器端启动助手,通过它可以完成服务器端的各种配置; Bootstrap 是 Netty 中
          的客户端启动助手, 通过它可以完成客户端的各种配置

分布式系统设计策略

分布式系统本质是通过低廉的硬件攒在一起以获得更好的吞吐量、性能以及可用性等。

设计策略

  • 如何检测当前节点还活着

    • 周期检测心跳机制

      Server端每间隔 t 秒向Node集群发起监测请求,设定超时时间,如果超过超时时间,则判断“死亡”。

    • 累计失效检测机制

      在周期检测心跳机制的基础上,统计一定周期内节点的返回情况(包括超时及正确返回),以此计算节点的“死
      亡”概率。另外,对于宣告“濒临死亡”的节点可以发起有限次数的重试,以作进一步判断。

  • 如何保障高可用

    • 主备 Master-Slave

      • 主备模式 主备模式就是Active-Standby模式,当主机宕机时,备机接管主机的一切工作,待主机恢复正常后,按
        使用者的设定以自动(热备)或手动(冷备)方式将服务切换到主机上运行。在数据库部分,习惯称之为MS模
        式。MS模式即Master/Slave模式,这在数据库高可用性方案中比较常用,如MySQL、Redis等就采用MS模式实现
        主从复制。保证高可用

        MySQL之间数据复制的基础是二进制日志文件(binary log fifile)。一台MySQL数据库一旦启用二进制日志后,作
        为master,它的数据库中所有操作都会以“事件”的方式记录在二进制日志中,其他数据库作为slave通过一个I/O线
        程与主服务器保持通信,并监控master的二进制日志文件的变化,如果发现master二进制日志文件发生变化,则
        会把变化复制到自己的中继日志中,然后slave的一个SQL线程会把相关的“事件”执行到自己的数据库中,以此实现
        从数据库和主数据库的一致性,也就实现了主从复制。

    • 互备 Active_active

      • 互备模式 互备模式指两台主机同时运行各自的服务工作且相互监测情况。在数据库高可用部分,常见的互备是
        MM模式。MM模式即Multi-Master模式,指一个系统存在多个master,每个master都具有read-write能力,会根
        据时间戳或业务逻辑合并版本

        我们使用过的、构建过的MySQL服务绝大多数都是Single-Master,整个拓扑中只有一个Master承担写请求。比
        如,基于Master-Slave架构的主从复制,但是也存在由于种种原因,我们可能需要MySQL服务具有Multi-Master的
        特性,希望整个拓扑中可以有不止一个Master承担写请求

    • 集群 Cluster

      • 集群模式是指有多个节点在运行,同时可以通过主控节点分担服务请求。如Zookeeper。集群模式需要解决主控节
        点本身的高可用问题,一般采用主备模式。
  • 容错处理

    • 比如缓存穿透:可以将这个不存在的key预先设定一个值。比如,key=“null”。在返回这个null值的时候,
      我们的应用就可以认为这是不存在的key,那我们的应用就可以决定是否继续等待访问,还是放弃掉这次操作。如
      果继续等待访问,过一个时间轮询点后,再次请求这个key,如果取到的值不再是null,则可以认为这时候key有值
      了,从而避免了透传到数据库,把大量的类似请求挡在了缓存之中。
  • 负载均衡

    • 其关键在于使用多台集群服务器共同分担计算任务,把网络请求及计算分配到集群可用的不同服务器节
      点上,从而达到高可用性及较好的用户操作体验

XMind - Trial Version

你可能感兴趣的:(分布式理论总结)