0%

分布式快照:Chandy-Lamport算法

分布式快照(Snapshot)的作用

作为软件工程师的我们深有体会,能够抓取一个程序运行状态的快照是很重要的一个功能,通过对快照的分析,我们可以定位软件在某个运行时刻上的状态,从而可以发现软件是否按照预定的逻辑运行,是否存在内存泄漏,是否存在死锁等一系列的问题,从而可以定位出软件代码上的bug。

同样的,对于一个分布式系统,如果我们能对整个分布式系统抓取快照,那么这个快照也可以帮我们分析分布式系统出现的问题,比如,进程之间的死锁,状态异常等等。

对于单应用进程的快照,快照一般包含CPU状态以及内存状态,对于一个分布式系统,它的快照除了包含系统中每一个进程的快照外,还要包含进程间通信信道上的信息。

分布式快照的难点

对于一个分布式系统,如果我们想对它做快照,那么一个最简单的思路就是希望在做快照的时候能让整个系统停下来,然后记录每一个进程的信息,再记录每一个信道上的所有信息,这样一个全局快照就生成了,但是这个思路却有以下问题:

  1. 一般我们使用分布式系统是希望达成某种程度的高可用性,所以让整个分布式系统暂时停止下来比较难以接受;
  2. 一般我们的分布式系统都是构建在一个异步网络之上的(消息传输时间不确定),所以想让一个快照控制进程给整个系统发送快照消息,消息并不会即时到达所有节点,而且消息到达不同节点的时间是不一样的,所以做出来的快照状态会不一致。一个简单的例子就是假设节点i节点j上发送了一条消息mm已经从i发出,但是还在信道Ci->j上而没有被j接收到,这时候假设节点k向整个系统发出来快照请求(这要求往节点i进程,信道C进程,节点j进程上发消息通知快照),根据消息抵达速度,节点i在发出了m以后收到了快照消息,做了本地快照,信道C进程在m在它里面的时候收到了快照消息,做了信道消息的快照,但是节点j因为快照消息延时的原因,是在m被它消费了以后才收到了快照消息做了本地快照,这种情况下生成的全局快照就是不一致的,因为如果用这个全局快照重建系统,那么m会被节点j消费两次。

Chandy-Lamport算法

谈到分布式系统的快照算法,比较著名的一个就是Chandy-Lamport算法。它是由K. Mani ChandyLeslie Lamport共同发明的算法。

先决条件

Chandy-Lamport算法对于能应用此算法的系统做出了以下假设:

  1. 系统中的任意两个节点之间都可以进行通信;
  2. 通信信道中的消息满足先进先出(FIFO)的顺序。

算法描述

Chandy-Lamport算法如下:

  1. 系统中的任何一个节点都可以发起快照操作;
  2. 对于发起快照操作的节点(假设此节点为Node i):
    1. 节点对本地进程生成快照,然后给所有其他节点发送一条<Marker>消息,然后;
    1. 对于所有从其他节点发送过来的消息,和本地快照一并记录下来;
    1. 如果此节点收到了从Node j (j != i)发送过来的一条<Marker>消息,则停止记录信道Node j->Node i上的消息。
  3. 对于其他节点Node j (j != i):
    1. 如果Node j第一次从信道Ck->j (k != j)上收到了<Marker>消息,则对本地进程生成快照,然后给所有其他节点发送一条<Marker>消息;
    1. 对于所有从其他节点l (l != k)发送过来的消息,和本地快照一并记录下来;
    1. 如果此节点上收到了从Node l (l != k)发送过来的一条<Marker>消息,则停止记录信道Node l->Node j上的消息。
  4. 当所有的节点都从其他节点收到了<Marker>以后,算法停止,分布式系统的快照生成完成。
  5. 在一个系统中可以有多个快照同时进行,发起节点也可以不同,只要在发起快照时给它赋予一个全局唯一的ID,然后发起节点把这个ID附在<Marker>消息上,同时每个节点在做本地快照时在快照副本上记录这是对于哪一个ID生成的副本,然后也把ID附在发送出的<Marker>消息上。

示例

以下图示展示了一个基于3个节点的Chandy-Lamport算法 (示例摘自于本人以前一个一份英文讲义):