从新冠疫情出发,漫谈 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())
}
接收端:
- 接收數據報文,然后解析報文信息,并將信息記錄下來
// 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)
}
func (m *Memberlist) ingestPacket(buf []byte, from net.Addr, timestamp time.Time) {
// …
}
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 协议的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 容器化技术如何在数据中心实践
- 下一篇: 从 301 跳转,聊聊边缘规则的那些小妙