跳转至

分布式系统的核心问题

一致性问题 Consensus Problem

在分布式系统中,一些节点可能故障 (Crash fault)或者恶意工作 (Byzantine Fault),此时会导致不能很好地做出一致的决定。

一种准确的表述是$n$个进程中的$m$个可能会出错,如何在有出错进程的情况下使得所有的非出错进程在某些值上达成一致的问题。

可以将问题分成以下几类:不出错时的一致性,最多$m$个故障进程时的一致性,最多$m$个恶意进程的一致性。

以下均假设理想情况,即有可靠的传输介质,同步的系统,完全连接,接收者永远知道信息的发送者。

不出错时,这是最简单的情况,每个进程会向所有其他节点广播自己的值,所有的节点选择接受值中最小的一个,这样所有的节点都接收到同一个值集合,其中最小的一个也会一致。

最多$m$个故障错误,至少需要m + 1轮才能在这种环境下达成共识。每个节点在第一轮中广播自己的值,并从后续轮开始广播节点在上一轮中收到的任何新值。 节点可能在与其他节点共享值时发生故障。因此,如果最多m个节点可能会发生故障,则最坏情况下每次尝试达成共识时都会有一个节点发生故障。 如果每个节点经过$m + 1$轮,则我们可以确保在这些$m + 1$轮中肯定会有至少一轮没有任何节点发生故障,并且每个人都会收到每个值。 因此,在$m + 1$轮之后,每个节点将选择最小值。

最多m个拜占庭故障:简单地说,拜占庭故障是指一些节点开始恶意或异常行为。这里的问题是,任何这样的节点都可能向两个不同的节点发送两个不同的值,如果发生这种情况,则我们之前的所有方法都不起作用。因为以前我们确定如果一个节点发送一个值,那么它将向每个节点发送相同的值。

假设$S$是所有节点的集合,发起协议的节点是指挥官,其建议的值是命令,其他节点中,指挥官向其发送命令的节点是他的中尉。称$OM$代表口头消息Oral Message,这个术语只是表明一个节点可以向两个不同的节点告知两个不同的值。

存在一种递归算法,称为Lamport-Shostak-Pease算法,用于解决拜占庭将军问题。 基本情况 $OM(0,S)$:指挥官 $i$ 向 $S-{i}$ 中的每个中尉$j$发送建议值$v$,每个中尉$j$从$i$接受值$v$

递归情况 $OM(m,S)$:指挥官$i$向 $S-{i}$ 中的每个中尉$j$发送值$v$。$v_j$是中尉$j$从指挥官$i$接收的值(如果没有接收到值,则为0)。 中尉$j$现在使用值$v_j$启动 $OM(m-1,S - {i})$作为指挥官。在每次递归执行结束时,每个忠诚的中尉 $j\in S - {i}$ 都同意一组对$(k,v_k)$,其中每个$k\in S-{i}$。 在所有中尉完成上一步之后,每个中尉$j$收集其收到的对(包含其指挥官的原始值以及其自己的中尉通过递归调用$OM(m,S- {i})$返回的值)。 然后,每个中尉$j$都同意值$v =majority({(k,v_k)| k \in S- {i}})$,该值在这些对中占多数。

到目前为止讨论的所有解决方案都是在系统是同步的,完全连接的并且节点知道彼此的身份的情况下可能的。 在拜占庭故障的情况下,如果$n <(3m + 1)$,则没有解决方案。 在异步模型中,即使有单个崩溃故障,也无法实现共识。 即使在同步模型中,单个链路故障也无法实现共识。

PAXOS算法

Paxos算法运行在允许宕机故障的异步系统中,不要求可靠的消息传递,可容忍消息丢失、延迟、乱序以及重复。它利用大多数(Majority)机制保证了$2F+1$的容错能力,即$2F+1$个节点的系统最多允许$F$个节点同时出现故障。

虽然客户端是并发的,没有先后顺序,但到了服务器的集群里必须保证所有机器的日志顺序是一样的,这就是所谓的“分布式一致性”。存储状态机的输入事件流(日志流),比存储状态机本身更容易。

一个或多个提议进程 (Proposer) 可以发起提案 (Proposal),Paxos算法使所有提案中的某一个提案,在所有进程中达成一致。系统中的多数派同时认可该提案,即达成了一致。最多只针对一个确定的提案达成一致。

Paxos将系统中的角色分为提议者 (Proposer),决策者 (Acceptor),和最终决策学习者 (Learner):

Proposer: 提出提案 (Proposal)。Proposal信息包括提案编号 (Proposal ID) 和提议的值 (Value)。 Acceptor:参与决策,回应Proposers的提案。收到Proposal后可以接受提案,若Proposal获得多数Acceptors的接受,则称该Proposal被批准。 Learner:不参与决策,从Proposers/Acceptors学习最新达成一致的提案(Value)。

第一阶段:Prepare阶段。Proposer向Acceptors发出Prepare请求,Acceptors针对收到的Prepare请求进行Promise承诺。 第二阶段:Accept阶段。Proposer收到多数Acceptors承诺的Promise后,向Acceptors发出Propose请求,Acceptors针对收到的Propose请求进行Accept处理。 第三阶段:Learn阶段。Proposer在收到多数Acceptors的Accept之后,标志着本次Accept成功,决议形成,将形成的决议发送给所有Learners。

每个承诺指:不再接受小于等于当前proposal id 的prepare,不再接受小于当前proposal id的propose.

改进:multipaxos

负载均衡 Load Balance

负载均衡就是将大量的数据处理操作分摊到多个操作单元进行执行,用来解决互联网分布式系统的大流量、高并发和高可用的问题。即在多个资源(一般是服务器)中分配负载,达到最优化资源使用,避免过载。

高可用(High Availability)通常是指提供一定性能上的服务运行时间高于平均正常时间段。衡量系统是否满足高可用,就是当一台或者多台服务器宕机的时候,系统整体和服务依然正常可用。

负载均衡核心关键在于是否分配均匀。

常见的互联网分布式系统架构分为客户端层(比如用户浏览器、APP 端)、反向代理层(技术选型 Nignx 或者 F5 等)、Web 层(前后端分离场景下, Web 端可以用 NodeJS 、 RN 、Vue)、业务服务层(用 Java 、Go,一般互联网公司,技术方案选型就是 SC 或者 Spring Boot + Dubbo 服务化)、数据存储层(DB 选型 MySQL ,Cache 选型 Redis ,搜索选型 ES 等)。一个请求从第 1 层到第 4 层,层层访问都需要负载均衡。即每个上游调用下游多个业务方的时候,需要均匀调用。这样整体系统来看,就比较负载均衡。

客户端层到反向代理层的负载均衡通过 DNS 轮询,反向代理层到Web 层 的负载均衡通过 Nginx 的负载均衡模块,Web 层到业务服务层的负载均衡通过服务治理框架的负载均衡模块,业务服务层到数据存储层的负载均衡通过数据的水平分布。

轮询策略

随机策略

哈希和一致性哈希策略