一文带你学明白分布式高可用算法链路——公平丢包链路!

链路

从物理层看,进程间可以通过多种连接方式传递消息。

物理层网络的连接方式如图 4-1 所示,可以是全网状连接,即任何两个进程之间有一条直连链路;

也可以通过广播型介质进行消息传递,例如以太网;

也可以通过环状网进行消息传递,例如光传输网络;

也可以是将多个小型网络桥接起来形成的大网,例如互联网。

但在分布式算法中,任何两个进程之间的通信都被抽象为点对点的通信,好像它们之间有一条真实的链路一样。

图4-1 物理层网络的连接方式

在真实的分布式系统中,链路失败是很常见的现象。

如前面所述,链路失败的类型很多,例如窃听、篡改、丢失、乱序、重发等。

其中,窃听、篡改问题可以通过加密和认证抽象解决,丢失、乱序、重发问题可以通过更高级的抽象解决,例如,可以通过重发的方式来解决丢失的问题。

本章将介绍五种链路抽象。

第一种叫作公平丢包链路,它允许消息丢失,但不是100%丢失,总有一定的概率能够让对端进程收到消息。

第二种叫作顽固链路,它在公平丢包链路的基础上通过重传机制确保正确的对端进程能收到每个消息。

第三种叫作可靠链路,它不仅能确保正确的对端进程能收到每个消息,还能确保每个消息最多被收到一次。

第四种叫作先进先出链路,它在可靠链路的基础上还保证了对端进程将按照消息被发送的顺序接收消息。

第五种叫作日志可靠链路,它是一个能够容忍进程崩溃后又恢复的可靠链路,使得进程在恢复后仍然可以收到所有被发送的消息。

每个链路抽象都有两个接口事件,一个是输入事件<Send|q,m>,它表示向进程q发送消息m;另一个是输出事件<Receive|p,m>,表示接收进程p发送的消息m。

在真实环境下,有两种原因会导致消息丢失。一是缓冲区溢出。例如,进程调用实例的输入事件<Send>的速度太快,那么链路实例不一定能受理得过来;又例如,链路实例宣告输出事件<Receive>的速度太快,那么进程也不一定能感知得过来。二是链路本身就存在丢包的情况,因此一端进程发送的消息不一定会被对端进程接收。

公平丢包链路

定义

公平丢包链路(Fair-Loss Link)是一种基础的链路抽象。它有两个接口事件,一个是表示发送消息的输入事件<Send|q,m>,另一个是表示接收消息的输出事件<Receive|p,m>。

抽象4-1定义了公平丢包链路的接口和特性。

根据抽象 4-1,公平丢包链路有如下三个特性。

FLL1公平丢包特性实际上是要求任何一个消息被传递成功的概率大于零,也就是说,链路的丢包行为是无偏见的,包括时间和空间两个维度的无偏见。

所谓时间维度的无偏见,是指不能每当需要传递消息时,链路就处于丢包状态;所谓空间维度的无偏见,是指链路的丢包行为不能仅针对某些特征的消息,例如不能仅针对特定类型或体积的消息,也不能仅针对发送或者接收方向的消息。在有了这项特性保证后,如果一个正确的进程p向另一个正确的进程q不断地发送消息,那么进程q也会不断地接收消息。这个特性使得上层应用可以通过重传的方式确保消息送达接收进程。

FLL2有限重复特性要求链路本身不会无限地重传消息。

FLL3不创造特性要求链路本身不会创造消息或修改消息。

实际上,公平丢包链路定义了一种无过失(即无人为不当)的通信环境。在这种环境下,丢包具有随机性,不会专门针对某个时间段或者具有某种特征的消息;消息不会被无限次地重传,因为传递消息是需要能量的,无过失的环境不会提供无限的能量;消息也不会被无中生有地创造,即使有,也是无效的,因为无法通过校验,例如循环冗余码(Cyclic Redundancy Check,CRC)。往往是有过失(人为不当,包括疏忽或故意)的通信环境才会违反公平丢包链路的特性。

消息系统

在实际系统中,物理链路断开是必然要考虑的事情。然而,公平丢包链路抽象要求消息传递成功率大于0,这实际上定义了一种链路不会完全断开的模型,显然与实际情况不符。这就有必要介绍一下消息系统了。

在一个真实的分布式系统中,进程并非两两直联的,但消息仍然可以从发送进程通过中间进程抵达目标进程,靠的就是消息系统。这个消息系统是虚拟的,在这个消息系统中,我们假设任何两个进程之间都有一条双向的公平丢包链路。

当进程p发送消息m后,消息m就进入了消息系统,这个消息系统负责将消息传递到接收进程r。如果正确的进程p无限次发送消息m,而进程r仍无法接收消息m,那么我们就认为进程r失败了,而非消息系统的链路(公平丢包链路)出了问题。这样就把链路断开的问题转变成了进程失败的问题,统一在进程失败模型中考虑。

细心的读者可能会提两个问题。问题一:如果进程r无法接收进程p发送的消息m,但可以接收进程q发送的消息m',那么进程r仍然被看作失败了吗?

问题二:进程r无法接收进程p发送的消息,为何认为是进程r而不是进程p失败了呢?

这里我们引入一个术语——连通。如果进程q可以接收进程p发送的消息,那么就说进程p到进程q是连通的。如果进程p到进程q是连通的,进程q到进程r也是连通的,那么进程p到进程r肯定也是连通的。因为进程q可以扮演接力者的角色,将进程p发送的消息m转发到进程r。

同理,如果进程p到进程r不连通,而进程p到进程q是连通的,那么进程q到进程r一定是不连通的。更进一步,如果进程p到某个多数派Q中的进程都是连通的,那么多数派Q中的进程到进程r必然是不连通的,那么多数派Q中的进程会认为进程r是失败的。同时,进程r自知无法与多数派Q中的进程连通,因此也意识到自己会被多数派Q中的进程认为失败了。但反过来看,如果是进程p到多数派Q中的进程不连通,而多数派Q中的进程到进程r是连通的,那么进程r会认为进程p失败了,而进程p自己也会意识到这一点。

通过上述分析,如果一个进程不能与多数派 Q 中的进程连通,那么该进程就是失败的。这样我们就把实际环境中存在的链路断开的问题从模型的角度转化成了进程失败的问题,并认为进程间的链路符合公平丢包链路的FLL1公平丢包特性——丢而不断。因此,用公平丢包链路来描述进程间通信的底层通道是合理的抽象。

另外,除非特别说明,本书将采用这种“进程失败而链路正确”的分布式系统模型来描述系统。唯一的例外是7.8节介绍的CAP理论,该理论是基于“链路失败而进程正确”的分布式系统模型描述的。

实践经验

IP网络中的路由协议就可以看成一个消息系统。在一个正确配置的IP网络中,进程间通信符合公平丢包链路的特性要求,但如果网络配置不当,就会破坏公平丢包链路的假设。

例如,在一个由节点p、q、r组成的通过IP网络互联的分布式系统中,由于网络规则配置失误,导致进程 p 和进程 q 不连通,但都可以与进程 r连通。这带来的结果是,进程p和进程q都认为自己与多数派是连通的(进程p认为自己可以与进程r连通,进程q认为自己可以与进程r连通),都认为对方已经失败了(进程p认为进程q失败了,进程q认为进程p失败了),而实际上进程p和进程q都是正确的。这样的消息系统不满足公平丢包链路的FLL1公平丢包特性,从而影响系统的活性。

不满足FLL1公平丢包特性的消息系统曾经给作者留下了深刻的记忆。当时,作者负责了一个单一容器容量超过1000PB的云存储服务。在作者已知的范围内,这是当时国内单一容器容量最大的云存储服务。为了防止单一机房的故障对服务的影响,该云存储服务部署在多个相距上百千米的机房,每个写入的数据都会实时地在多个机房间进行复制冗余。这样即使任何一个机房突然失败,整个云存储服务既不会丢失一个字节,也不会停止服务一分钟。

考虑到广域网的不稳定性,该服务能够确保,即使机房A和机房 B之间的直连网络突然断开,流量也会立即绕经其他机房,实现机房A和机房B的互访。有一天,该云存储服务突然出现了性能明显下降。经过技术排查,我们发现从机房A访问机房B的请求仅有轻微异常,而从机房B访问机房A的请求却出现大量异常。同时,我们还发现机房 B的服务器节点出现了大量的TCP重传现象,而机房A的服务器却很少。因此,我们判断机房B的出口网络设备出现了单向限速。方向确定了后,我们顺藤摸瓜,终于发现在机房 B 的某台网络设备上的某个光模块的激光器部分区域无法正常发光,而收光器件却正常,从而导致发送方向速率大大下降。

此次性能下降事件的直接原因是,在设计该服务之初,我们只考虑进程间双向断网或限速的情况,但未考虑单向断网或限速的情况。而从分布式算法理论的角度看,实际上是因为该分布式系统的消息系统(即网络路由策略)不符合公平丢包链路的假设。最后的改进措施就是优化网络路由策略,当单方向限速情况出现时,流量仍能快速切换到其他节点,使消息系统仍然满足公平丢包链路的假设。

本文给大家讲解的内容是分布式高可用算法链路——公平丢包链路!

  • 下文给大家讲解的内容是分布式高可用算法链路——顽固链路
举报
评论 0