基本概念

节点

节点是指一个可以独立按照分布式协议完成一组逻辑的程序个体。

在具体的工程项目中,一个节点往往是一个操作系统上的进程。

通信

节点与节点之间是完全独立、相互隔离的,节点之间传递信息的唯一方式是通过不可靠的网络进行通信。

即一个节点可以向其他节点通过网络发送消息,但发送消息的节点无法确认消息是否被接收节点完整正确收到。

存储

节点可以通过将数据写入与节点在同一台机器的本地存储设备保存数据。

通常的存储设备有磁盘、SSD等。

存储、读取数据的节点称为有状态的节点,反之称为无状态的节点。

如果某个节点 A存储数据的方式是将数据通过网络发送到另一个节点 B ,由节点 B 负责将数据存储到节点 B 的本地存储设备,那么不能认为节点 A 是有状态的节点,而只有节点 B 是有状态的节点。

异常

分布式系统核心问题之一就是处理各种异常情况。本节着重讨论在本文范围内系统会遇到的各种异常。

机器宕机

机器宕机是最常见的异常之一。

在大型集群中每日 宕机发生的概率为 千分之一左右 。

在实践中,一台宕机的机器恢复的时间通常认为是 24 小时,一般需要人工介入重启机器。

宕机造成的后果通常为该机器上节点不能正常工作。

假设节点的工作流程是三个独立原子的步骤,宕机造成的后果是节点可能在处理完某个步骤后不再继续处理后续步骤。

由于本文不考虑拜占庭问题,认为不会出现执行完第一个步骤后跳过第二个步骤而执行第三个步骤的情况。

宕机是一个完全随机 的事件,在本文中,认为在任何时刻都可能发生宕机,从而造成某些协议流程无法完成。

当发生宕机时,节点无法进入正常工作的状态,称之为“不可用”unavailable 状态。

机器重启后,机器上的节点可以重新启动,但节点将失去所有的内存信息。

在某些分布式系统中,节点可以通过读取本地存储设备中的信息或通过读取其他节点数据的方式恢复内存信息,从而恢复到某一宕机前的状态,进而重新进入正常工作状态、即“可用” available 状态。而另一些分布式系统中的某些无状态节点则无需读取读取任何信息就可以立刻重新“可用”。

使得节点在宕机后从“不可用”到“可用”的过程称为宕机恢复

网络异常

网络异常是另一类常见的异常形式。

消息丢失

消息丢失是最常见的网络异常。

对于常见的 IP 网络来说,网络层不保证数据报文(IP fragement)的可靠传递,在发生网络拥塞、路由变动、设备异常等情况时,都可能发生发送的数据丢失。

由于网络数据丢失的异常存在,直接决定了分布式系统的协议必须能处理网络数据丢失的情况。

依据网络质量的不同,网络消息丢失的概率也不同,甚至可能出现在一段时间内某些节点之间的网络消息完全丢失的情况。

如果某些节点的直接的网络通信正常或丢包率在合理范围内,而某些节点之间始终无法正常通信,则称这种特殊的网络异常为“网络分化”(network partition)

消息乱序

消息乱序是指节点发送的网络消息有一定的概率不是按照发送时的顺序依次到达目的节点。

通常由于 IP 网络的存储转发机制、路由不确定性等问题,网络报文乱序也是一种常见的网络异常。

这就要求设计分布式协议时,考虑使用序列号等机制处理网络消息的乱序问题,使得无效的、过期的网络消息不影响系统的正确性。

数据错误

网络上传输的数据有可能发生比特错误,从而造成数据错误。

通常使用一定的校验码机制可以较为简单的检查出网络数据的错误,从而丢弃错误的数据。

不可靠的TCP

TCP协议为应用层提供了可靠的、面向连接的传输服务。

TCP 协议 是最优秀的传输层协议之一,其设计初衷就是在不可靠的网络之上建立可靠的传输服务。

TCP 协议通过为传输的每一个字节设置顺序递增的序列号,由接收方在收到数据后按序列号重组数据并发送确认信息,当发现数据包丢失时, TCP 协议重传丢失的数据包,从而 TCP 协议解决了网络数据包丢失的问题和数据包乱序问题。

TCP 协议为每个 TCP 数据段(以太网上通常最大为 1460 字节)使用 32 位的校验和从而检查数据错误问题。

TCP 协议通过设置接收和发送窗口的机制极大的提高了传输性能,解决了网络传输的时延与吞吐问题。

TCP 协议最为复杂而巧妙的是其几十年来不断改进的拥塞控制算法,使得 TCP 可以动态感知底层链路的带宽加以合理使用并与其他 TCP 链接分享带宽( TCP friendly )。

由上述分析,在设计分布系统的网络协议时即使使用TCP 协议,也依旧要考虑网络异常,不能简单的认为使用 TCP 协议后通信就是可靠的。

另一方面,如果完全放弃使用 TCP 协议,使用 UDP协议加自定义的传输控制机制,则会使 得系统设计复杂。

尤其是要设计、实现一个像 TCP 那样优秀的拥塞控制机制是非常困难的。

存储数据丢失

数据丢失指节点存储的数据不可被读取或读取出的数据错误。数据丢失是另一类常见的异常。

尤其是使用 机械硬盘做存储介质时,硬盘损坏的概率较大 。

对于有状态节点来说,数据丢失意味着状态丢失,通常只能从其他节点读取、恢复存储的状态。

分布式系统的三态

由于网络异常的存在,分布式系统中请求结果存在“三态”的概念。

在单机系统中,我们调用一个函数实现一个功能,则这个函数要么成功、要么失败,只要不发生宕机其执行的结果是确定的。

然而在分布式系统中,如果某个节点向另一个节点发起 RPC(Remote procedure call) 调用,即某个节点 A 向另一个节点 B 发送一个消息,节点 B 根据收到的消息内容完成某些操作,并将操作的结果通过另一个消息返回给节点 A ,那么这个 RPC 执行的结果有三种状态:“成功”、“失败”、“超时(未知)”,称之为分布式系统的三态。

如果请求RPC 的节点 A 收到了执行 RPC 的节点 B 返回的消息,并且消息中说明执行成功,则该 RPC 的结果为“成功”。

如果请求 RPC 的节点 A 收到了执行 RPC 的节点 B 返回的消息,并且消息中说明执行失败,则该 RPC 的结果为“失败”。

但是,如果请求 RPC 的节点 A 在给定的时间内没有收到执行 RPC 的节点 B 返回的消息,则认为该操作“超时”。

一旦发生超时,请求方是无法获知 RPC 的执行结果的。

一般解决超时,有 2 种方式:

(1)超时之后,通过反查的方式确认最终的状态。

(2)保证接口的幂等性,通过重试的方式。

副本

副本的概念

副本(replica/copy)指在分布式系统中为数据或服务提供的冗余。

对于数据副本指在不同的节点上持久化同一份数据,当出现某一个节点的存储的数据丢失时,可以从副本上读到数据。

数据副本是分布式系统解决数据丢失异常的唯一手段。

另一类副本是服务副本,指数个节点提供某种相同的服务,这种服务一般并不依赖于节点的本地存储,其所需数据一般来自其他节点。

副本一致性

分布式系统通过副本控制协议,使得从系统外部读取系统内部各个副本的数据在一定的约束条件下相同,称之为副本一致性(consistency)。

副本一致性是针对分布式系统而言的,不是针对某一个副本而言。

一致性的几个级别

依据一致性的强弱即约束条件的不同苛刻程度,副本一致性分为若干变种或者级别,本文挑选几种常见的一致性级别介绍:

  • 强一致性

(strong consistency):任何时刻任何用户或节点都可以读到最近一次成功更新的副本数据。

强一致性是程度最高的一致性要求,也是实践中最难以实现的一致性。

  • 单调一致性

(monotonic consistency):任何时刻,任何用户一旦读到某个数据在某次更新后的值这个用户不会再读到比这个值更旧的值。

单调一致性是弱于强一致性却非常实用的一种一致性级别。

因为通常来说,用户只关心从己方视角观察到的一致性,而不会关注其他用户的一致性情况。

  • 会话一致性

(session consistency )):任何用户在某一次会话内一旦读到某个数据在某次更新后的值这个用户在这次会话过程中不会再读到比这个值更旧的值。

会话一致性通过引入会话的概念,在单调一致性的基础上进一步放松约束,会话一致性只保证单个用户单次会话内数据的单调修改,对于不同用户间的一致性和同一用户不同会话间的一致性没有保障。

实践中有许多机制正好对应会话的概 念,例如 php 中的 session 概念。

可以将数据版本号等信息保存在 session 中,读取数据时验证副本的版本号,只读取版本号大于等于session 中版本号的副本,从而实现会话一致性。

  • 最终一致性

eventual consistency:最终一致性要求一旦更新成功,各个副本上的数据最终将达到完全一致的状态,但达到完全一致状态所需要的时间不能保障。

对于最终一致性系统而言,一个用户只要始终读取某一个副本的数据,则可以实现类似单调一致性的效果,但一旦用户更换读取的副本,则无法保障任何一致性。

  • 弱一致性

(week consistency:一旦某个更新成功,用户无法在一个确定时间内读到这次更新的值,且即使在某个副本上读到了新的值,也不能保证在其他副本上可以读到新的值。

弱一致性系统一般很难在实际中使用,使用弱一致性系统需要应用方做更多的工作从而使得系统可用。

衡量分布式系统的指标

评价分布式系统有一些常用的指标。

依据设计需求的不同,分布式系统对于这些指标也有不同的要求。

性能

无论是分布式系统还是单机系统,都会对性能(perfomance)有所要求。

对于不同的系统,不同的服务,关注的性能不尽相同、甚至相互矛盾。

常见的性能指标有:

系统的吞吐能力,指系统在某一时间可以处理的数据总量,通常可以用系统每秒处理的总的数据量来衡量;

系统的响应延迟,指系统完成某一功能需要使用的时间;

系统的并发能力,指系统可以同时完成某一功能的能力,通常也用 QPS(query per second) 来衡量。

上述三个性能指标往往会相互制约,追求高吞吐的系统,往往很难做到低延迟;系统平均响应时间较长时,也很难提高 QPS 。

可用性

系统的可用性 availability 指系统在面对各种异常时可以正确提供服务的能力。

系统的可用性可以用系统停服务的时间与正常服务的时间的比例来衡量,也可以用某功能的失败次数与成功次数的比例来衡量。

可用性是分布式的重要指标,衡量了系统的鲁棒性,是系统容错能力的体现。

可扩展性

系统的可扩展性 scalability 指分布式系统通过扩展集群机器规模提高系统性能(吞吐、延迟、并发)、存储容量、计算能力的特性。

可扩展性是分布式系统的特有性质。分布式系统的设计初衷就是利用集群多机的能力处理单机无法解决的问题。

然而,完成某一具体任务的所需要的机器数目即集群规模取决于系统的性能和任务的要求。

当任务的需求随着具体业务不断提高时,除了升级系统的性能,另一个做法就是通过增加机器的方式扩展系统的规模。

好的分布式系统总在追求“线性扩展性”,也就是使得系统的某一指标可以随着集群中的机器数量线性增长。

一致性

分布式系统为了提高可用性,总是不可避免的使用副本的机制,从而引发副本一致性的问题。

根据具体的业务需求的不同,分布式系统总是提供某种一致性模型,并基于此模型提供具体的服务。

正如 1.2.2 提到的,越是强的一致的性模型,对于用户使用来说使用起来越简单。

例如通常我们总是希望某次更新后可以立刻读到最新的修改,如果成功更新后的数据依旧有可能不一致读到旧数据,那么用户就需要在写入数据时加入序列号等信息,并在读取数据时首先自行实现过滤去重后再使用数据。

参考资料

分布式系统相关书籍pdf免费下载