Gossip协议
概述
有些人的业务需求具有一定的敏感性,比如监控主机和业务运行的告警系统,大家都希望自己的系统在极端情况下(比如集群中只有一个节点在运行)也能运行。在会以了二阶段提交协议和Raft算法之后,你会发现它们都需要全部节点或者大多数节点正常运行才能稳定运行,并不适合此类场景。而如果采用Base理论,则需要实现最终一致性,那么,怎样才能实现最终一致性呢?
在我看来,可以通过Gossip协议来实现这个目标。
Gossip协议,顾名思义,就像流言蜚语一样,是指利用一种随机、带有传染性的方式将信息传播到整个网络中,并在一定时间内使得系统内的所有节点数据一致。掌握这个协议不仅能帮助我们很好地理解这种最常用的、实现最终一致性的算法,也能让我们在后续工作中得心应手地实现数据的最终一致性。
Gossip的三板斧
Gossip的三板斧分别是直接邮寄(Direct Mail)、反熵(Anti-entropy)和谣言传播(Rumor Mongering)。
直接邮寄
是指直接发送更新数据,当数据发送失败时,将数据缓存下来,然后重传。如图所示,节点A直接将更新数据发送给了节点B、D.
在这里补充一点,直接邮寄虽然实现起来比较容易,数据同步也很及时,但可能会因为缓存队列满了而丢失数据。也就是说,只采用直接邮寄是无法实现最终一致性的。
反熵
那如何实现最终一致性呢?答案就是反熵。本质上,反熵是一种通过异步修复实现最终一致性的方法。常见的最终一致性系统(比如Cassandra)都实现了反熵功能.反熵是指集群中的节点每隔一段时间就随机选择一个其他节点,然后通过互相交换自己的所有数据来消除两者之间的差异,实现数据的最终一致性。如图所示,从图中可以看到,节点A通过反熵的方式修复了节点D中缺失的数据。那具体如何实现呢?
反熵的实现方式主要有推、拉和推拉3种,将以上面图中两个数据副本的不一致问题为例来详细分析。
也许有人会觉得反熵是一个很奇怪的明此。其实,你可以这么来理解,反熵中的熵是指混乱程度,而反熵是指消除不同节点中数据的差异,以提升节点间数据的相似度,降低熵值。
另外需要注意的是,因为反熵需要节点两两交换和比对自己所有的数据,通信成本会很高,所以不建议在实际场景中频繁执行反熵操作,可以通过引入校验和(Checksum)等机制降低需要比对的数据量和通信消息等。
虽然反熵很实用,但是执行反熵操作时,相关的节点都是已知的,而且节点数不能太多。如果是一个动态变化或节点数比较多的分布式环境(比如在DevOps环境中检测节点故障并动态维护集群节点状态),这时反熵就不适用了。此时,我们应该怎样实现最终一致性呢?答案就是谣言传播。
- 推方式是指将自己的所有副本数据推给对方,以修复对方的数据副本中的熵,如图所示
- 拉方式是指拉取对方的所有副本数据,以修复自己的数据副本中的熵,如图所示
- 理解了推方式和拉方式之后,推拉方式就很好理解了,它是指同时修复自己和对方的数据副本中的熵。如图所示
谣言传播
即广泛地散播谣言,是指当一个节点有了新数据后,这个节点就会变成活跃状态,并周期性地联系其他节点向其发送新数据,直到所有的节点都存储了该新数据。如图所示。节点A向节点B、D发送新数据,节点B收到新数据后变成活跃节点,然后节点B向节点C、D发送新数据。其实,谣言传播非常具有传染性,它适合动态变化的分布式系统。
如何使用反熵实现最终一致性
在分布式存储系统中,实现数据副本最终一致性的最常用的方法是反熵。为了帮助我们沉底理解和掌握在实际环境中实现反熵的方法,以InfluxDB的反熵实现为例具体分析一下。
在InfluxDB中,一份数据副本是由多个分片组成的,也就是实现了数据分片。3节点3副本的InfluxDB集群如图所示。
反熵的目标是确保每个DATA节点拥有元信息指定的分片,而且在不同节点上,同一分片组中的分片都没有差异。比如,节点A要拥有分片Shard1和Shard2,而且节点A的Shard1和Shard2与节点B、C中的Shard1和Shard2是一样的。那么,DATA节点熵存在哪些数据缺失的情况呢?换句话说,我们需要解决哪些问题呢?
我们将数据缺失分为这样两种情况。
1.缺失分片:某个节点上的整个分片都丢失了
2.节点之间的分片不一致:节点上的分片都存在,但里面的数据不一样,有数据丢失的情况发生。
第一种情况修复起来很简单,我们只需要通过RPC通信将分片数据从其他节点上复制过来就可以了,如图所示
第二种情况修复起来要复杂一些,我们需要设计一个闭环流程,按照一定的顺序来修复,这样执行完整个流程后也就实现了一致性。具体要怎样设计呢?
它是按照一定顺序来修复节点的数据差异,先随机选择一个节点,该节点生成自己节点有而下一个节点没有的差异数据,并发送给下一个节点,修复下一个节点的数据缺失,然后按照顺序,各节点循环修复,如图所示,从图中可以看到,数据修复的起始节点为节点A,数据修复是按照顺时针顺序循环进行。需要注意的是,最后节点A又对节点B的数据执行了一次数据修复,因为只有这样,节点C、节点B缺失的差异数据才会同步到节点B上。
看到这里,在实现反熵时,实现细节和最初算法的约定有些不同。比如,不是一个节点不断随机选择另一个节点来修复副本上的熵,而是设计了一个闭环的流程,一次修复所有的副本数据不一致问题。
为什么这样设计呢?因为我们希望能在一个确定的时间范围内实现数据副本的最终一致性,而不是基于随机性的概率,在一个不确定的时间范围内实现数据副本的最终一致性。这样做能减少数据不一致对监控试图影响的时长。但是,需要注意的是,技术是要活学活用的,我们要能根据场景特点权衡妥协,设计出最适合这个场景的系统功能。最后需要注意的是,因为反熵需要做一致性对比,很消耗系统资源,所以建议将是否启用反熵功能、执行一致性检测的时间间隔等做成可配置的,以方便在不同场景中按需使用
思维拓展
既然使用反熵实现最终一致性时需要通过一致性检测发现数据副本的差异,如果每次做一致性检测时都要做数据对比,必然会消耗一部分资源,那么,有什么办法可以降低一致性检测时的性能损失呢?
答案是:我们期望最好的做法是花费更少的时间、花费更少的通信资源,如果我们将每个节点上的数据都进行传输,必然会消耗很多网络资源,反过来,如果我们将每条数据都进行传输,也会很消耗时间,所以使用对数据进行Hash计算,将得到的hash值进行比对,这样就无须再逐个进行比对,我们只要保证,同一数据源可以得到同一个Hash值,而且一个hash值所占用的网络资源也是相当少的
重点总结
- 1.Gossip协议作为一种异步修复、实现最终一致性的协议,反熵再存储组件中应用广泛,比如Dynamo、InfluxDB、Cassandra,在需要实现最终一致性的实际工作场景中,优先考虑反熵
- 2.因为谣言传播具有传染性,如一个节点传给了另一个节点,另一个节点又将充当传播者,传给其他节点,所以非常适合动态比那花的分布式系统,比如Cassandra
- 3.一般而言,在实际场景中实现数据副本的最终一致性时,直接邮寄的方式是一定要实现的,因为不需要做一致性对比,只需要通过发送更细年数据或重传缓存来修复数据,新跟那个损耗低。在存储组件中,节点都是已知的,一般采用反熵修复数据副本的一致性。当集群节点是变化的,或者集群节点数比较多时,这时要采用谣言传播的方式更新数据,实现最终一致性。
如果我们在实际场景中涉及了一套AP型分布式系统,并通过反熵实现了各数据副本的最终一致性,且系统也在线上稳定地运行着,此时突然有同时提出希望数据写入成功后,能立即读取到新数据需求,也就是要实现强一致性,这时我们该怎么呢?难道我们必须推倒架构,一切从头再来?