从新冠疫情出发,漫谈 Gossip 协议

众所周知周知,疫情仍然在全球各地肆虐。据最新数据统计,截至北京时间 2020-05-28,全球累计确诊 5698703 例,累计死亡 352282 例,累计治愈 2415237 例。
从上面的统计数据,我们可以看出,新冠病毒在人与人之间的传播是极其高效的,且影响范围广。如果我们把「新冠病毒」想象成一小段数据,将「人与人之间传播」想象成数据交换,那么,我们可以得出结论,在不考虑免疫系统和人为干预等一些因素,经过反复迭代,数据(新冠病毒)可以被发送(感染)到每个节点(人)上。
这个就是今天要介绍的 Gossip 协议,该协议早在 1987 年就被发表在 ACM 上的论文《Epidemic Algorithms for Replicated Database Maintenance》中。当时主要用在分布式数据库系统中各个副本节点间同步数据。

Gossip 协议简介
Gossip 协议分为 Push-based 和 Pull-based 两种模式,具体工作流程如下:
Push-based 的 Gossip 协议:
网络中的某个节点随机选择N个节点作为数据接收对象
该节点向其选中的N个节点传输相应数据
接收到数据的节点对数据进行存储
接收到数据的节点再从第一步开始周期性执行

Pull-based 的 Gossip 协议,正好相反:
集群内的所有节点,随机选择其它 k 个节点询问有没有新数据
接收到请求的节点,返回新数据

如何实现 Gossip
这边简单分析下 HashiCorp 公司的 Serf 的核心库 Memberlist。这家公司研发了 Consul(基于 raft 实现的分布式存储)、Vagrant(声明式虚拟机编排)等优秀的产品。最近由于中美矛盾升级,也陷入到了舆论的漩涡中,爆出禁止在中国使用他们的产品的传闻。不过,这是题外话。
Memberlist 这个 Golang 的代码库,基于 Gossip 协议,实现了集群内节点发现、 节点失效探测、节点故障转移、节点状态同步等。
其核心实现的大致如下:
newMemberlist():初始化 Memberlist 对象,根据配置监听 TCP/UDP 端口,用于之后通信。这边需要注意一点,虽然是基于 Gossip 协议实现的,但是并不是所有信息都采用 Gossip 进行数据交换。比如节点加入集群的时候,为了尽快的让集群内所有节点感知到,采用遍历当前已知的所有节点并通过 TCP 连接发送并接收数据的方式,来确保跟所有节点完成数据交换。
gossip():Memberlist 对象启动之后,会定期使用 Gossip 协议,随机选择集群内的节点,采用 UDP 传输方式发送当前节点状态以及用户自定义的数据。
pushPull():还会定期随机选择一个节点,通过 TCP 传输方式与其做全量数据交换,加速集群内数据一致性收敛。
probe():还会定期轮训集群内的一个节点,通过 UDP 方式发送心跳探测包,做到节点感知。
深入 Gossip 核心代码
发送端处理流程:
- 周期性地随机选择 m.config.GossipNodes 个节点,然后广播正在等待发送的信息
// Create a gossip ticker if needed
if m.config.GossipInterval > 0 && m.config.GossipNodes > 0 {
t := time.NewTicker(m.config.GossipInterval)
go m.triggerFunc(m.config.GossipInterval, t.C, stopCh, m.gossip)
m.tickers = append(m.tickers, t)
}
// gossip is invoked every GossipInterval period to broadcast our gossip
// messages to a few random nodes.
func (m *Memberlist) gossip() {
defer metrics.MeasureSince([]string{"memberlist", "gossip"}, time.Now())
// Get some random live, suspect, or recently dead nodes
m.nodeLock.RLock()
kNodes := kRandomNodes(m.config.GossipNodes, m.nodes, func(n *nodeState) bool {
if n.Name == m.config.Name {
return true
}
switch n.State {
case StateAlive, StateSuspect:
return false
case StateDead:
return time.Since(n.StateChange) > m.config.GossipToTheDeadTime
default:
return true
}
})
m.nodeLock.RUnlock()
// ...
for _, node := range kNodes {
// Get any pending broadcasts
msgs := m.getBroadcasts(compoundOverhead, bytesAvail)
if len(msgs) == 0 {
return
}
addr := node.Address()
if len(msgs) == 1 {
// Send single message as is
if err := m.rawSendMsgPacket(node.FullAddress(), &node.Node, msgs[0]); err != nil {
m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", addr, err)
}
} else {
// Otherwise create and send a compound message
compound := makeCompoundMessage(msgs)
if err := m.rawSendMsgPacket(node.FullAddress(), &node.Node, compound.Bytes()); err != nil {
m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", addr, err)
}
}
}
}
接收端:
- 接收数据报文,然后解析报文信息,并将信息记录下来
// packetListen is a long running goroutine that pulls packets out of the
// transport and hands them off for processing.
func (m *Memberlist) packetListen() {
for {
select {
case packet := <-m.transport.PacketCh():
m.ingestPacket(packet.Buf, packet.From, packet.Timestamp)
case <-m.shutdownCh:
return
}
}
}
func (m *Memberlist) ingestPacket(buf []byte, from net.Addr, timestamp time.Time) {
// ...
// See if there's a checksum included to verify the contents of the message
if len(buf) >= 5 && messageType(buf[0]) == hasCrcMsg {
crc := crc32.ChecksumIEEE(buf[5:])
expected := binary.BigEndian.Uint32(buf[1:5])
if crc != expected {
m.logger.Printf("[WARN] memberlist: Got invalid checksum for UDP packet: %x, %x", crc, expected)
return
}
m.handleCommand(buf[5:], from, timestamp)
} else {
m.handleCommand(buf, from, timestamp)
}
}
Gossip 协议的优缺点
看了 Memberlist 的实现,难免会有这样的疑问,为什么要使用 Gossip 协议,直接在集群内广播不香么?接下来,我们可以通过 Gossip 协议的优缺点来分析,使用 Gossip 协议的意义。
优点:
协议简单,实现起来很方便
扩展性强,可以允许集群内节点任意增加或者减少,新增节点最终会与其他节点一致
去中心化,节点之间是完全对等的
最终一致性
缺点:
数据同步延迟,因为只保证最终一致性,所以会出现某个时间点,部分节点数据不同步的情况
传输数据冗余,相同数据在节点间会反复被传输
今天对 Gossip 的协议就简单介绍到这里,如果有同学对内容感兴趣,可以回复评论,我们私下多多探讨和交流。
参考资料
https://en.wikipedia.org/wiki/Gossip_protocol
https://github.com/hashicorp/serf
https://github.com/hashicorp/memberlist
https://zhuanlan.zhihu.com/p/41228196
https://www.jianshu.com/p/de7b026f4997

推荐阅读
从新冠疫情出发,漫谈 Gossip 协议的更多相关文章
- Python小白的数学建模课-B6. 新冠疫情 SEIR 改进模型
传染病的数学模型是数学建模中的典型问题,常见的传染病模型有 SI.SIR.SIRS.SEIR 模型. SEIR 模型考虑存在易感者.暴露者.患病者和康复者四类人群,适用于具有潜伏期.治愈后获得终身免疫 ...
- Raft算法和Gossip协议
简单介绍下集群数据同步,集群监控用到的两种常见算法. Raft算法 raft 集群中的每个节点都可以根据集群运行的情况在三种状态间切换:follower, candidate 与 leader.lea ...
- P2P 网络核心技术:Gossip 协议
背景 Gossip protocol 也叫 Epidemic Protocol (流行病协议),实际上它还有很多别名,比如:“流言算法”.“疫情传播算法”等. 这个协议的作用就像其名字表示的意思一样, ...
- 浅谈集群版Redis和Gossip协议
昨天的文章写了关于分布式系统中一致性哈希算法的问题,文末提了一下Redis-Cluster对于一致性哈希算法的实现方案,今天来看一下Redis-Cluster和其中的重要概念Gossip协议. 1.R ...
- Python小白的数学建模课-B5. 新冠疫情 SEIR模型
传染病的数学模型是数学建模中的典型问题,常见的传染病模型有 SI.SIR.SIRS.SEIR 模型. 考虑存在易感者.暴露者.患病者和康复者四类人群,适用于具有潜伏期.治愈后获得终身免疫的传染病. 本 ...
- Python小白的数学建模课-B4. 新冠疫情 SIR模型
Python小白的数学建模课-B4. 新冠疫情 SIR模型 传染病的数学模型是数学建模中的典型问题,常见的传染病模型有 SI.SIR.SIRS.SEIR 模型. SIR 模型将人群分为易感者(S类). ...
- Dynamo涉及的算法和协议——p2p架构,一致性hash容错+gossip协议获取集群状态+向量时钟同步数据
转自:http://www.letiantian.me/2014-06-16-dynamo-algorithm-protocol/ Dynamo是Amazon的一个分布式的键值系统,P2P架构,没有主 ...
- Cassandra1.2文档学习(2)——节点间通信协议之gossip协议
参考文档:http://www.datastax.com/documentation/cassandra/1.2/webhelp/index.html#cassandra/architecture/a ...
- 【协议】5、gossip 协议
Gossip是一种去中心化.容错并保证最终一致性的协议. Background:分布式环境 Gossip是为了解决分布式遇到的问题而设计的.由于服务和数据分布在不同的机器上,节点之间的每次交互都伴随着 ...
随机推荐
- Asp.Net Core入门之配置文件
ASP.NET Core配置框架已内建支持 JSON.XML 和 INI 配置文件,内存配置(直接通过代码设置值),环境变量配置等方式配置参数. 本文主要和大家讲一下我们在项目中常用的以配置文件的方式 ...
- Java Serializable(序列化)的总结
1.序列化是干什么的? 简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来.虽然你可以用你自己的各种各样的方法来保存object states,但 ...
- Docker scratch 无法正常运行golang二进制程序的问题
使用Docker构建容器能够极大的降低运维成本,提高部署效率,同时非常方便对服务的平行扩展.然而在构建容器镜像过程中的,存在着一个难以避免的问题,就是如果使用常见的发行版本作为程序运行的基础环境,那么 ...
- 基于 kubeadm 搭建高可用的kubernetes 1.18.2 (k8s)集群 部署 dashboard 2.x
1. 部署dashboard 2.x版本 Dashboard 分为 1.x版本 和 2.x版本, k8s 使用的是1.18.2 故部署2.x版本的 # dashboard 2.x版本的部署 # 上传d ...
- [256个管理学理论]002.青蛙效应(Frog Effect)
青蛙效应(Frog Effect) 从一个话题开始: 当下,社会发展突飞猛进,日新月异.在世界经济危机中,我国国民生产总值增长幅度始终在8%以上,引起世人的瞩目. 但,在国内时常也能听到广大投资者对股 ...
- [书籍分享]0-008.商业模式新生代[Business Model Generation]
封面 内容简介 <商业模式新生代>内容简介:当你愉快的看完第一章:商业模式画布,赫然发现这些构成要素全都交织成一幅清晰的图像在脑海中呈现,它们如何互相影响.如何交互作用全都历历在目.利用商 ...
- Python所有异常错误的父类--BaseException
BaseException # 所有异常的基类 +-- SystemExit # 解释器请求退出 +-- KeyboardInterrupt # 用户中断执行(通常是输入^C) +-- Generat ...
- 【转】Android安全研究经验谈
本文转载自:http://www.cnblogs.com/whp2011/archive/2015/01/26/4250875.html 一.安全研究做什么 攻击角度:对某个模块进行漏洞挖掘的方法,对 ...
- CSS选择器-类-ID-伪类
类选择器(Class selectors) 通过设置元素的 class 属性,可以为元素指定类名.类名由开发者自己指定. 文档中的多个元素可以拥有同一个类名. 在写样式表时,类选择器是以英文句号(.) ...
- Spring-boot01
本文记录Spring-Boot学习途中的点点滴滴. 其实Spring-Boot出来好长时间了,但是一直没去关注过.之前在我的印象里Spring-Boot好就好在减去了很多XML配置,加入了很多自动配置 ...