分布式系统核心问题

一致性

  一致性consistency,也叫agreement。是指对于分布式系统中的多个服务节点,给定一系列操作,在约定协议的保障下,试图使得它们对处理结果达成“某种程度”的认同。若各个服务节点严格遵守相同的处理协议,构成相同的处理状态机,给定相同的初始状态和输入序列,则可以保障在处理过程中的每个环境的结果都是相同的。

  在分布式计算机集群系统中,很容易在以下几个方面出问题:

    节点之间的网络通信是不可靠的,包括消息延迟,乱序和内容错误等

    节点的处理时间无法保障,结果可能出现错误,甚至节点自身可能发生宕机

    同步调用可以简化设计,但会严重降低分布式系统的可扩展性,甚至使其退化为单点系统

  解决方案都有一个一致的思路:将可能引发不一致的并行操作进行串行化。即把多件事情进行排序,而且这个顺序还是大家认可的。

  分布式系统达成一致的过程,应该满足:

    可终止性(termination):一致的结果在有限时间内能完成

    约同性(agreement):不同节点最终完成决策的结果是相同的

    合法性(validity):决策的结果必须是某个节点提出的提案

  可终止性是在有限的时间内完成,意味着可以保障提供服务。约同性是算法要么不给出结果,要么给出的结果必定是达成了共识的,即安全性。合法性是指得到的结果必须是节点执行操作的结果。

  要想实现绝对量的严格一致性(strict consistency)代价很大。除非系统不发生任何故障而且节点之间的通信无需任何时间,这个时候整个系统其实就等价于一台机器了。越强的一致性要求往往会造成越弱的处理性能以及越差的可拓展性。

  强一致性(strong consistency)主要包括以下两类:

    顺序一致性(sequential consistency):是一种比较强的约束,保证所有进程看到的全局执行顺序一致,并且每个进程看到自身的执行顺序跟实际发生顺序一致。顺序一致性实际上限制了各进程内指令的偏序关系,但不能在进程间按照物理时间进行全局排序。

    线性一致性(linearizability consistency):在顺序一致性前提下加强了进程间的操作排序,形成唯一的全局顺序(系统等价于是顺序执行,所有进程看到的操作顺序都一致,并且跟实际发生的顺序一致),是很强的原子性保证,但比较难实现,目前基本上要么依赖于全局的时钟,要么通过一些复杂的算法实现,性能往往不高。

  在一定的约束下实现所谓最终一致性(eventual consistency),即总会存在一个时刻让系统达到一致的状态;相对强一致性。这一类在某方面弱化的一致性统称为弱一致性(weak consistency)。

  

共识算法

  共识(consensus)很多时候会与一致性(consistency)术语放在一起。但实际上两者含义并不完全相同。一致性往往指分布式系统中多个副本对外呈现的数据的状态,而共识则描述了分布式系统中的多个节点之间,彼此对某个状态达成一致结果的过程。一致性描述的是结果状态,共识则是一种手段。彼此达成共识并不意味着保障了一致性。实际中,要保障系统满足不同程度的一致性,核心往往需要通过共识算法来达成。

  公式算法解决的是对某个提案(proposal)大家达成一致意见的过程。对于分布式系统来讲,各个节点通常都是相同的确定性状态机模型(状态机复制问题,state-machine replication),从相同初始状态开始接收相同顺序的指令,则可以保证相同的结果状态。因此系统中多个节点最关键的是对多个时间的顺序进行共识,即排序。

  把出现故障(crash或fail-stop,既不响应)但不会伪造信息的情况称为“非拜占庭错误(non-byzantine fault)”或“故障错误(Crash Fault)”;伪造信息恶意响应的情况为“拜占庭错误(Byzantine Fault)”,对应节点为拜占庭节点。根据解决的是非拜占庭的普通错误还是拜占庭错误情况,公式算法可以分为Crash Fault Tolerance(CFT)类算法和Byzantine Fault Tolerance(BFT)类算法。这对常见的非拜占庭错误,可以用Paxos,Rafe极其变种等。针对拜占庭错误,一般包括PBRF(Practical Byzantine Fault Tolerance)为代表的的确定性系列算法,PoW为代表的的概率算法等。对于确定性算法,一旦达成对某个结果的共识就不可逆转,即共识是最终结果;而对于概率类算法,共识结果则是临时的,随着时间的推移或某种强化,共识结果被推翻的概率越来越小,成为事实上的最终结果。拜占庭类容错算法往往性能较差,容忍不超过3/1的故障节点。

  即便在网络通信可靠情况下,可拓展的分布式系统的共识问题,其通用解法的理论下限是——没有下线(无解)。这个结论成为FLP不吭能远离。是分布式领域里的“测不准原理”。  

  FLP不可能原理:在网络可能,但允许节点失败的最小化异步模型系统中,不存在一个可以解决一致性问题的确定性共识算法(No completely asynchronous consensus protocol can tolerate even a single unannounced process death)。

  在分布式系统中,同步是指系统中的各个节点的时钟误差存在上限,并且消息传递必须在一定时间你完成,否则认为失败;同时各个节点完成处理消息的时间是一定的。对于同步系统,可以很容易地判断消息是否丢失。异步似乎指系统中各个节点可能存在较大的时钟差异,同时消息传输时间是任意长的,各节点对消息进行处理的时间也可能是任意长的。这就造成无法判断某个消息迟迟没有被响应是哪里出了问题。

  CAP原理:分布式计算系统不能同时确定以下三个特性:一致性(Consistency),可用性(Availability)和分区容忍性(Partition)。一致性是指任何操作应该都是原子的,发生在后面的事件能看到前面事件发生导致的结果(强一致性)。可用性:在有限时间内,任何非失败节点都能应答请求;分区容忍性:网络可能发生分区,即节点之间的通信不可保障。既然CAP三种特性不可能同时保障,则设计系统时必然要弱化对某个特性的支持,则可能出现下面三个应用场景:

    弱化一致性

      对结果一致性不敏感的应用,可以允许在新版本上线后过一段时间才最终更新成功,期间不保证一致性。

    弱化可用性

      对一致性很敏感的应用,当系统故障时会拒绝服务。Paxos,Raft等共识算法主要处理这种情况。

    弱化分区容忍性

      两阶段的提交法,某些关系型数据库及Zookeeper主要考虑了这种。实际中,网络可以通过双通道等机制增强可靠性,达到高稳定的网络通信。 

  ACID原则

    ACID是指Atomicity(原子性),Consistency(一致性),Isolation(隔离性),Durability(持久性)。原子性是指每次操作都是原子的,要么成功,要么失败;一致性是指数据库的状态是一致的,无中间状态;隔离性是指各种操作彼此之间互相不影响;持久性是指状态的改变是持久的,不会失败。

  与ACID对应的另一个原则是BASE(Basic Availability,Soft-state,Eventual Consistency)原则,牺牲掉对一致性的约束,来换取一定的可用性。

Paxos

  Paxos是第一个广泛应用的共识算法,其原理基于“两阶段提交”算法并进行反话和扩展,通过消息传递来逐步消除系统中的不确定状态。算法原理是基于“两阶段提交”算法进行泛化和扩展,通过消息传递来逐步消除系统中的不确定状态。算法的基本原理是将节点分为三种逻辑角色,在实现同一个节点可以担任多个角色:

  Proposer(提案者):提出一个提案,等到大家批准为结案。系统中提案都有一个自增的唯一提案号。往往由客户端担任该角色。

  Acceptor(接受者):负责对提案进行投票,接收提案。往往由服务端担任该角色

  Leaner(学习者):获取批准结果,并可以帮忙传播,不参与投票过程。可能为客户端或服务端。

  算法需要满足Safty和Liveness两方面的约束要求。Safety约束是保证决议结果是对的,无歧义的,不会出现错误情况;只有被Proposers提出的提案才可能最终被批准;再一次执行中,只批准一个最终决议。被多数接收的结果成为决议。Liveness约束:保证决议过程能在有效时间内完成。决议总会产生,并且学习者能获得被批准的决议。

  基本过程是多个提案者先争取到提案的权利(获得大多数接受者的支持);得到提案权利的提案者发送提案给所有人进行确认,得到大部分人确认的提案成为批准的结案。Paxos不保证系统随时处在一致的状态。但由于每次达成一致的过程中至少有超过一半的节点参与,这样最终整个系统都会获知共识结果。Paxos能保证在超过一半的节点正常工作时,系统总能以较大概率达成共识。

  单个提案者 + 多个接受者

    若系统中限定只有某个特定节点是提案者,那么共识结果很容易达成。提案者只要收到了来自多数接受者的投票,即可认为通过。因为系统中不存在其他的提案。但此时一旦提案者故障,则系统无法工作。

  多个提案者 + 单个接受者

    限定某个节点作为接受者。此时,接受者收到多个提案,选第一个提案作为决议,发送给其他提案者即可。缺陷也是易发生单点故障,包括接受者故障或首个提案者节点故障。

  多个提案者 + 多个接受者

    多个提案者和多个接受者则在同一时间就可能出现多个提案和多个和接受者。一种情况是同一时间片段内只有一个提案者,这是可以退化到单提案者的情形。需要设计一种机制来保障提案者的正确产生。另一种情况允许同一时间片段内可以出现多个提案者。那么同一个节点可能收到多份提案。此时每个提案需要带上不同的序号。节点需要根据提案序号来判断接受哪个。可以根据每个节点的提案数字区间彼此隔离开,互相不冲突。为了满足递增的需求可以配合用时间戳作为前缀字段。

  同时允许多提案意味着很可能单个提案人无法极其足够多的投票;另一方面,提案者即便收到了多数接受者的投票,也不敢说就一定通过,因为在此过程中投票者无法获知其他投票人的结果,也无法确认提案人是否收到了自己的投票。因此需要实现两个阶段的提交过程。

两阶段的提交

  提案者发出提案申请之后,会收到来自接受者的反馈。一种结果是提案被大多数接受者接受了,一种结果是没被接受。没被接受的话,可以过会再重试。即便收到了来自大多数接受者的答复,也不能认为就最终确认了。因为这些接受者并不知道自己刚答复的天是否可以构成大多数的一致意见。因此需要引入一个新的阶段,即提案者在第一阶段拿到所有的反馈后,需要再次判断这个提案是否得到大多数的支持,若支持则需要对其进行最终确认。Paxos读这两个阶段分别命名为准备阶段和提交阶段。准备阶段通过锁来解决对哪个天内容进行确认的问题,提交阶段解决大多数确认最终值的问题。

  准备阶段:

    提案者发送自己计划提交的提案的编号到多个接受者,试探是否可以锁定多数接受者的支持

    接受者时刻保留收到过提案的最大编号和接收的最大提案。若收到提案号比目前保留的最大提案号还大,则返回自己已接受的提案值给提案者,并更新当前最大提案号,并说明不再接受小于最大提案号的提案。

  提交阶段:

    提案者若收到大多数的回复,则可准备发出带有刚才提案号的接受消息。若收到的恢复中不带有新的提案,说明锁定成功。则使用自己的提案内容;若返回中有提案内容,则替换提案值为返回中编号最大的提案值。若没有收到足够多的回复,则需要再次发出请求。

    接受者收到“接受消息”后,若发现提案号不小于已接收的最大提案号,则接受该提案,并更新接手的最大提案

  一旦多数接受者接受了共同的提案值,则形成决议,成为最终确认。    

    

Raft

  Raft算法面向多个决策达成一致的问题,分解了Leader选举,日志复制和安全方面的考虑,并通过约束减少了不确定性的状态空间。Raft算法包括三种角色:Leader(领导者),Candidate(候选领导者)和Follower(跟随者),决策前通过选举一个全局的Leader来简化后续的决策过程。Leader决定日志的提交。日志只能由Leader向Follower单向复制。

  典型的过程包括以下两个主要阶段:

    Leader选举:开始所有节点都是Follower,在随机超时发生后未收到来自Leader或Candidate消息,则转变角色为Candidate,提出选举请求。最后选举阶段(Term)中得票超过一半者被选举为Leader;若未选出,随机超时后进入新的阶段重试。Leader负责从客户端接收log,并分发到其它节点。

    同步日志:Leader会找到系统中日志最新的记录,并强制所有的Follower来刷新这个记录,数据的同步是单向的。

  

拜占庭算法

  对于拜占庭问题,加入节点总是为N,叛军讲述数为F,则当N >= 3F+1时,问题才有解,由BFT算来进行保证。

PBRT

  PBRT算法采用密码学相关技术(RSA签名算法,消息验证编码和摘要)确保消息传递过程无法被篡改和破坏。

  算法的基本过程如下:

    首先通过轮换或随机算法选出某个节点为主节点,此后只要主节点不切换,则成为一个视图(View)

    在某个视图中,客户端将请求<REQUEST, view, timestamp, client, id_node, response>发送给主节点,主节点负责广播请求到所有其他副本节点

    所有节点处理完成请求,将处理结果<REPLY, view, timestamp, client, id_node, response>返回给客户端。客户端检查是否收到了至少f+1个来自不同节点的相同结果,最为最终结果。

  主节点广播过程包括是三个阶段处理:预准备(pre-prepare)阶段,准备(prepare)阶段和提交(commit)阶段。预准备和准备阶段确保同一个视图内请求发送的顺序正确;准备和提交阶段则确保在不同视图之间的确认请求是保序的:

    预准备阶段:主节点为客户端收到的请求分配提案编号,然后发出预准备消息<<PRE-PREPARE, view, n, digest>, message>给各副本节点,其中message是客户端的请求消息,digest是消息的摘要

    准备阶段:副本节点收到预准备消息后,检查消息合法,若检查通过则向其他节点发送准备消息<PREPARE, view, n, digest, id>,带上自己的id信息,同时接受来自其他节点的准备信息。收到准备消息的节点对消息同样进行合法性检验。验证通过则把这个准备消息写入消息日志。集齐至少2f+1个验证过的消息才进入准备状态。

    提交阶段:广播commit消息,告诉其他节点某个提案n在视图v中已经处于准备状态。若集齐至少2f+1个验证通过的commit消息,则说明提案通过。

  可靠性指标或可用性可以用服务承诺(Service Level Agreement,SLA),服务指标(Service Level Indicator,SLI),服务目标(Service Level Objective, SLO)等方面进行衡量。

  描述系统出现故障的可能性和故障出现后的恢复能力,有两个基础指标:MTBF和MTTR。一个高可用的系统应该是具有尽量长的MTBF和尽量短的MTTR。

    MTBF(Mean Time Between Failures):平均故障间隔时间,即系统可以无故障运行的预期时间

    MTTR(Mean Time to Repair):平均修复时间,即发生故障后,系统可以恢复到正常运行的预期时间

  

  

猜你喜欢

转载自www.cnblogs.com/forerver-elf/p/9007517.html