Skip to content

分布式系统模型

本文主要介绍分布式系统的概念、以及在构建分布式系统时,需要面对的问题及其解决思路。 参考的资料是Distributed Systems Notes By Dr. Martin Kleppmann

1. 分布式系统介绍

分布式系统是由一组通过网络进行通信和协作的独立节点组成的系统,但在用户看来,它就像是一个单一的系统

节点具有广泛的意义,可能是笔记本电脑、服务器、手机、工业控制设备、传感器等等,我们可以认为节点就是一个可以通信的计算设备。

独立是指这些节点在物理空间上是隔离的。

为什么需要分布式系统?

  • 固有的分布式属性:也就是说,只有分布式系统才能解决问题,例如,银行转账涉及两个银行系统之间的交互;
  • 高可用性:分布式系统能增强应用的可用性,如果某个节点由于故障下线了,那么另外的节点也能提供服务,不会导致整个应用下限;
  • 高性能:分布式系统可以带来更好的性能,例如,假设某个网站只部署在一个节点上,那么全世界的访问该网站的请求都会到达该节点,就会造成在物理位置上远离该节点的请求非常慢,我们可以借助分布式,在全世界范围内,部署多个节点,这样访问网站的请求会路由到最近的节点,解决访问慢的问题;再例如,某个计算任务需要处理大规模数据,如果在单节点上计算,耗时非常长,借助分布式,我们可以让每个节点只计算部分任务,从而提高性能;

但是,分布式系统也有其缺点:

  • 网络通信会失败:当分布式系统中的某节点A向另一个节点B发送消息时,由于网络问题,该消息可能会出现以下问题:

    • 由于网络中断,节点B没有收到消息;
    • 节点B收到消息并返回响应,但是由于网络中断,节点A没有收到消息;
    • 节点B收到消息并返回响应,但是由于网络延迟,节点A在一定时间内没有收到消息;

    以上问题,从节点A的角度来看,无法判断消息是否到达了节点B。

  • 单节点会出问题:某个节点可能会出问题,例如意外下线、运行速度变慢、或者异常响应(由于BUG或硬件错误)

    当某个节点出现问题后,分布式系统需要一种机制,能检测出该节点出问题了。

以上问题随时都会在分布式系统中发生,没有任何预兆。

当问题发生时,分布式系统也能对外提供服务,这就要求系统具有容错性,也就是说高可用性。

2. 分布式系统问题与模型

2.1 网络问题

首先,大多数分布式算法都假定,网络在两个节点之间提供双向通信,也就是点对点或单播通信(一个发送者,一个接收者)。

因此,只要在两个节点之间建立起TCP连接,就可以提供通信机制了,这种连接称为Arbitrary Link

Arbitrary Link是脆弱的,无法防御主动攻击者(active adversary),也就是说,在连接中用于转发消息的中间设备所有者(路由器、交换机),可能是恶意的,它能“偷听”消息(被动攻击),还能篡改、删除、伪造、重放或拦截数据包。

在TCP的基础上,我们可以通过TLS(Transport Layer Security)来防御主动攻击者,因此,通过TLS技术,我们可以在两个节点之间建立Fair-loss Link

但是,Fair-loss Link也有缺点,消息可能丢失。虽然TCP在网络包层级上实现了重发和去重,但是TCP 是“尽力而为(best effort)的可靠传输”,也就是说TCP 通常会设置超时时间,如果长时间收不到响应,TCP 就会放弃重传,并断开连接,这个超时时间通常大约是一分钟量级。

因此,在应用层协议上,需要设计重传机制,在发送端,对于某条消息(序号为n),如果在一定时间内(网络延迟)没有收到针对该消息的响应(n'),那么发送端需要重新发送消息;在接收端,如果收到了多条序号相同的消息,需要有去重的能力;如果发送端发送了两条消息(序号为n和n+1,有先后关系),在接收端先接收到第n+1消息,后接收到第n条消息,接收端需要有重排序能力。

通过在应用层协议上增加重传和去重机制,我们可以把Fair-loss Link转换为Reliable Link

network behaviour

在分布式系统中,网络连接可以分为三个模型,并且可以通过协议设计把弱模型提升成强模型。

物理层来说,网络连接永远是 Fair-loss 的,从 Fair-loss 到 Reliable 的前提条件是:网络不会无限中断。通过在应用层实现不断的重试和去重,我们就可以把 Fair-loss 连接转变为 Reliable 连接。

要理解网络模型,不要完全映射到物理网络模型(TCP、UDP),不要认为TCP就是可靠连接,也不要认为UDP就是不可靠连接,他们都是不可靠(Fair-loss)连接,只要在满足网络不会无限中断的前提下,应用层不断重试,都可以将Fair-loss转变为Reliable连接。

2.2 节点问题

在分布式系统中,节点也可能出问题,例如崩溃下线或者出现异常行为(由于BUG或黑客入侵)。

针对崩溃问题,有两个模型:

  • Crash-Stop:在Crash-Stop模型中,节点一旦崩溃,就永远停止运行,例如无法修复的硬件损伤,但是,假如是由于软件错误导致节点下线呢,此时我们可以通过重启来恢复节点上线,在Crash-Stop模型中,可以将重新上线的节点视为全新的节点;
  • Crash-Recovery:在Crash-Recovery模型中,节点会崩溃,但之后会恢复,恢复之后内存状态会丢失,但持久化的数据(例如日志、磁盘数据)在恢复后可以读取,因此,在Crash-Recovery模型中,节点恢复后需要做状态恢复;

针对节点出现异常行为,有一个模型来描述节点问题:

  • Byzantine:在Byzantine模型中,节点可以实行任意行为,也就是说节点可能正常运行,也可能崩溃、发错数据、伪造消息、撒谎、恶意攻击;

节点模型不是说系统如何实现,而是说:在设计分布式系统时,假设系统节点会怎么出错,针对不同的模型,会有不同的算法实现。

在网络模型中,可以通过协议设计,将弱模型转换为强模型,但是在节点模型中,对节点的信任程度不一样(Byzantine模型中,对节点的信任程度最低),因此不同模型的算法设计完全不一样,无法简单地转换。

2.3 延迟问题

在网络问题中,我们主要关注连接问题,但是还有延迟问题,也就是说,一条信息从一个节点发送到另一个节点是需要时间的,一个节点处理消息也是需要时间的,而这个时间是不确定的。

关于这个时间/耗时的假设,分为三个模型:

  • synchronous(同步):在同步模型中,消息在网络中的传输时间永远不会超过某个已知的最大值,节点总是以特定速度执行算法;
  • asynchronous(异步):在异步模型中,关于消息的传输时间和节点的执行速度,没有任何假设,也就是说,消息可能延迟很久才到达,节点之间的执行速度也非常不一样;
  • partially synchronous(部分同步):在部分同步模型中,假设大多数时候,系统是处于同步模型中的并且运行良好,但是偶尔会切换到异步模型中,但最终会恢复到同步模型;

同步模型很让算法实现简单,但是通常情况下,这只是理想假设。因为网络延迟通常是不可避免的,例如消息丢失、网络拥挤、路由重配置等,都会导致消息在网络中的传输时间不可能永远在某个阈值内,同样地,节点的执行速度也不可控,假设在某节点中,正在执行任务的应用程序会暂停,可能因为操作系统调度高优先级任务、STW(stop-the-world)垃圾回收、内存页错误等等。如果我们假设分布式系统是同步模型的,一旦出现不满足假设的情况,可能会造成服务下线的灾难性后果。

因此,在实际的分布式系统设计中,一般不会使用同步模型,而会选择异步模型或部分同步模型。

3. 错误、容错性与错误检测机制

错误(fault,例如节点下线、网络中断)会造成服务不可用,为了保证分布式系统的高可用性,需要在设计系统时考虑容错,也就是说,在系统的一部分组件遭遇错误时,系统也能向外提供服务,也就是容错性(fault tolerance)

容错性算法并不是万能的,假如系统中的所有节点都崩溃了,那么容错性算法也没有办法使系统向外提供服务。因此,容错性算法都会假设,在低于多少节点崩溃时,系统仍然能向外提供服务,例如,某个容错性算法假设少于一半的节点崩溃时,系统仍能正常提供服务。

在具有容错性的系统中,我们要避免单点错误(single points of failure),也就是说,某个节点或某个网络连接的崩溃,会导致整个系统不可用。

设计容错性系统的第一步是错误检测,主要通过错误检测器(failure detector,fault detector更准确,但failure detector是传统术语)来实现。错误检测器通常检测节点崩溃下线,而不检测拜占庭错误(也就是节点没有下线,但是行为异常)。

在大多数情况下,错误检测器的实现机制是通过定期向其他节点发送消息,如果该消息在指定时间段内没有返回,那么就标记该节点异常了。在理想情况下,只有节点真正发生异常时,响应消息才会超时。但是,响应超时还有可能是网络原因,例如消息丢失或消息延迟。

完美错误检测器(perfect failure detector),是指绝对不能产生误报的消息检测器,只会在同步、Crash-Stop分布式系统中存在,在 Crash-Recovery 模型中,一个节点崩溃后可能会在未来的某个时间点带着之前的状态(或部分状态)重新上线。如果检测器在节点崩溃期间将其标记为“已失效”,那么当该节点恢复时,这个标记就变成了误报,从而破坏了“强准确性”。

由于完美错误检测器需要满足的条件非常苛刻,所以在实际中,会使用最终完美错误检测器(eventually perfect failure detector),该错误检测器适用于部分同步系统(异步系统没有基于超时机制的错误检测器)。最终完美检测器有以下特点:

  • 允许误报 (False Positives):如果网络突然拥塞,导致节点 A 的心跳延迟到达,检测器可以暂时把 A 标记为“失效”。

  • 允许纠错:当 A 的心跳后来又到达时,检测器可以撤销之前的判断,把 A 从失效名单中移除。

  • 收敛过程:它承诺,只要网络恢复正常(进入所谓的“同步段”),它给出的结论就会趋于稳定且正确。

4. SLI、SLO、SLA

在分布式系统和互联网服务管理中,SLASLO(以及通常关联的 SLI)是衡量服务质量和可靠性的核心框架。

术语全称定义侧重点
SLIService Level Indicator (服务等级指标)具体的量化测量指标(如:响应时间、错误率)。数据:我们现在观测到了什么?
SLOService Level Objective (服务等级目标)给 SLI 设定的目标值或范围(如:99.9% 的请求成功率)。标准:我们希望服务表现如何?
SLAService Level Agreement (服务等级协议)企业与客户之间的法律/商业合同,包含 SLO 及其失败后的后果法律/财务:如果没达标,我们要赔多少钱?

例如,假设运营一个网页搜索服务:

  1. SLI (指标):查询响应时间(Latency)。
  2. SLO (目标):99% 的查询响应时间必须小于 500ms。
  3. SLA (协议):如果在一个月内,响应时间达标率低于 95%,我们将退还当月 10% 的服务费用。

错误预算 (Error Budget):这是从 SLO 衍生出的重要概念。

  • 如果 SLO 是 99.9%,意味着有 0.1% 的“容错空间”;
  • 这 0.1% 就是错误预算,可以用它来进行高风险的发布或实验;
  • 一旦预算耗尽,团队通常必须停止所有新功能开发,专注于提高稳定性;

不同的服务对于SLO的设定不一样,例如可以基于请求次数,也可以基于服务时间。

目前互联网服务(如 API、网页搜索)最常用的计算方式是请求次数:

  • 定义:成功请求数占总请求数的比例。

  • 计算公式:

    可用性=成功的有效请求数总有效请求数×100%
  • 适用场景:流量波动较大的服务。

  • 优点:更能反映用户的真实感受。即使服务在深夜宕机 1 小时(此时没用户),只要全天 99.9% 的请求是成功的, SLO 依然达标。

传统基础设施(如虚拟机、数据库、网络链路)常用的计算方式是服务时间,也常被称为“正常运行时间 (Uptime)”。

  • 定义:服务处于健康状态的时间占总时间的比例。

  • 计算公式:

    可用性=总时长停机时长总时长×100%
  • 99.9% 的含义:对于 99.9%(通常称为“3 个 9”)的可用性,意味着在一年(365天)中,允许的累计总停机时间约为 8.77 小时

  • 适用场景:始终在线、后台运行的任务或存储服务。

越高的SLO意味着更高的资源投入,所以,SLO如何定,需要斟酌经济成本。