1. 通信流程
分布式系統(tǒng)元數(shù)據(jù)存儲的兩種方案
- 集中式存儲,典型產品:Zookeeper
- 優(yōu)勢:更新效率快
- 劣勢:所有的元數(shù)據(jù)信息集中在一個外部系統(tǒng)中粱玲,這個外部系統(tǒng)的壓力很大
使用Gossip協(xié)議進行通信
優(yōu)勢:減少了元數(shù)據(jù)存儲壓力
-
劣勢:元數(shù)據(jù)更新有延遲,需等待全部master的元數(shù)據(jù)達成一致
每個master都會自己維護一份完整的元數(shù)據(jù)信息备籽,只要自己的元數(shù)據(jù)有變化绸硕,就會發(fā)送消息給其他master钠署,通過master之間的兩兩通信來保持元數(shù)據(jù)一致
Redis Cluster采用P2P的Gossip協(xié)議進行通信勾怒,節(jié)點之間不斷的交換信息婆排,這些信息包括節(jié)點負責哪些slot、是否出現(xiàn)故障等信息
- 集群中的每個節(jié)點都會單獨開通一個TCP通道笔链,用于節(jié)點之間彼此通信段只,通信端口號為基礎端口號+10000,例如10.0.0.100:6379的通信端口號為16379
- 每個節(jié)點在固定周期內通過特定規(guī)則選擇幾個節(jié)點發(fā)送ping消息
- 接收到ping消息的節(jié)點用pong消息作為響應
集群中的每個節(jié)點通過一定的規(guī)則挑選要通信的節(jié)點鉴扫,每個節(jié)點可能知道其他全部節(jié)點赞枕,也可能僅知道部分節(jié)點,只要這些節(jié)點之間可以正常通信坪创,最終它們會達到一致狀態(tài)炕婶,當節(jié)點出現(xiàn)故障、新節(jié)點加入误堡、主從角色變化古话、slot信息變更等事件發(fā)生時,通過不斷的ping/pong消息通信锁施,經過一段時間后所有的節(jié)點都會知道整個集群全部節(jié)點的最新狀態(tài),從而達到集群狀態(tài)同步的目的杖们。
2. Gossip消息
Gossip消息類型
Gossip協(xié)議的主要職責就是信息交換悉抵。信息交換的載體就是節(jié)點彼此發(fā)送的Gossip消息,常用的Gossip消息可分為:ping
消息摘完、pong
消息姥饰、meet
消息、fail
消息等孝治。
- meet消息:用于通知新節(jié)點加入列粪。消息發(fā)送者通知接收者加入到當前集群审磁,meet消息通信正常完成后,接收節(jié)點會加入到集群中并進行周期性的ping岂座、pong消息交換态蒂。
- ping消息:集群內交換最頻繁的消息,集群內每個節(jié)點每秒向多個其他節(jié)點發(fā)送ping消息费什,用于檢測節(jié)點是否在線和交換彼此狀態(tài)信息钾恢。ping消息發(fā)送封裝了自身節(jié)點和部分其他節(jié)點的狀態(tài)數(shù)據(jù)。
- pong消息:當接收到ping鸳址、meet消息時瘩蚪,作為響應消息回復給發(fā)送方確認消息正常通信。pong消息內部封裝了自身狀態(tài)數(shù)據(jù)稿黍。節(jié)點也可以向集群內廣播自身的pong消息來通知整個集群對自身狀態(tài)進行更新疹瘦。
- fail消息:當節(jié)點判定集群內另一個節(jié)點下線時,會向集群內廣播一個fail消息巡球,其他節(jié)點接收到fail消息之后把對應節(jié)點更新為下線狀態(tài)言沐。
Gossip消息中的包含的信息
一個Gossip的消息頭中包含的信息:
- 消息總長度
- 協(xié)議版本
- 消息類型,用于區(qū)分是meet辕漂、ping呢灶、pong等消息
- 當前發(fā)送消息的節(jié)點的配置版本
- 主/從節(jié)點的配置版本
- 復制偏移量
- 發(fā)送節(jié)點的nodeId
- 發(fā)送節(jié)點負責的slot信息
- 如果發(fā)送節(jié)點是slave,那么還包括對應的master的nodeId
- 端口號
- 集群狀態(tài)
- 節(jié)點標識(主從角色/是否下線等)
消息體包含的信息:
- 目標節(jié)點的nodeId
- 最后一次向目標節(jié)點發(fā)送ping消息的時間
- 最后一次接收目標節(jié)點的pong消息時間
- 目標節(jié)點的IP和port
- 目標節(jié)點的標識(主從角色/是否下線等)
一個節(jié)點處理ping/meet消息的流程
-
解析消息頭钉嘹,消息頭包含了發(fā)送節(jié)點的信息
如果發(fā)送節(jié)點是新節(jié)點且消息是meet類型鸯乃,則加入到本地節(jié)點列表
如果是已知節(jié)點,則嘗試更新發(fā)送節(jié)點的狀態(tài)跋涣,如槽映射關系缨睡、主從角色等狀態(tài)
-
解析消息體
如果消息體內包含的節(jié)點是新節(jié)點,則嘗試發(fā)起與新節(jié)點的meet握手流程
如果是已知節(jié)點陈辱,則根據(jù)消息體中的目標節(jié)點的標識判斷該節(jié)點是否下線奖年,用于故障轉移
消息處理完后回復pong消息,內容同樣包含消息頭和消息體沛贪,發(fā)送節(jié)點接收到回復的pong消息后陋守,采用類似的流程解析處理消息并更新與接收節(jié)點最后通信時間,完成一次消息通信
3. 節(jié)點選擇
雖然Gossip協(xié)議的信息交換機制具有天然的分布式特性利赋,但它是有成本的水评。由于內部需要頻繁地進行節(jié)點信息交換,而ping/pong消息會攜帶當前節(jié)點和部分其他節(jié)點的狀態(tài)數(shù)據(jù)媚送,勢必會加重帶寬和計算的負擔中燥。Redis集群內節(jié)點通信采用固定頻率(定時任務每秒執(zhí)行10次)。因此節(jié)點每次選擇需要通信的節(jié)點列表變得非常重要塘偎。通信節(jié)點選擇過多雖然可以做到信息及時交換但成本過高疗涉。節(jié)點選擇過少會降低集群內所有節(jié)點彼此信息交換頻率拿霉,從而影響故障判定、新節(jié)點發(fā)現(xiàn)等需求的速度咱扣。因此Redis集群的Gossip協(xié)議需要兼顧信息交換實時性和成本開銷绽淘,通信節(jié)點選擇的規(guī)則如下:
選擇發(fā)送消息的節(jié)點數(shù)量
- 每秒會隨機選取5個節(jié)點,找出其中最久沒有通信的節(jié)點發(fā)送ping消息偏窝,用于保證Gossip信息交換的隨機性
- 每100毫秒都會掃描本地節(jié)點列表收恢,如果發(fā)現(xiàn)節(jié)點最近一次接受pong消息的時間大于
cluster_node_timeout/2
,則立刻發(fā)送ping消息祭往,防止該節(jié)點信息太長時間未更新 - 根據(jù)以上規(guī)則得出每個節(jié)點每秒需要發(fā)送ping消息的數(shù)量=
1+10*num(node.pong_received>cluster_node_timeout/2)
伦意,因此cluster_node_timeout
參數(shù)對消息發(fā)送的節(jié)點數(shù)量影響非常大 - 當我們的帶寬資源緊張時,可以適當調大這個參數(shù)硼补,如從默認15秒改為30秒來降低帶寬占用率驮肉。
- 過度調大cluster_node_timeout會影響消息交換的頻率從而影響故障轉移、槽信息更新已骇、新節(jié)點發(fā)現(xiàn)的速度离钝,因此需要根據(jù)業(yè)務容忍度和資源消耗進行平衡。
- 整個集群消息總交換量也跟節(jié)點數(shù)成正比褪储,所以并非redis cluster的節(jié)點越多卵渴,其性能越好,隨著節(jié)點數(shù)的增多鲤竹,交換元數(shù)據(jù)的消耗也會加大
cluster_node_timeout
真實世界的機房網絡往往并不是風平浪靜的浪读,它們經常會發(fā)生各種各樣的小問題。比如網絡抖動就是非常常見的一種現(xiàn)象辛藻,突然之間部分連接變得不可訪問碘橘,然后很快又恢復正常。為解決這種問題吱肌,Redis Cluster 提供了一個配置選項cluster-node-timeout
痘拆,表示當某個節(jié)點持續(xù) timeout 的時間失時,才可以認定該節(jié)點出現(xiàn)故障氮墨,需要進行主從切換纺蛆。如果沒有這個選項,網絡抖動會導致主從頻繁切換 (數(shù)據(jù)的重新復制)规揪。
每個從節(jié)點都要檢查最后與主節(jié)點斷線時間犹撒,判斷是否有資格替換故障的主節(jié)點。如果從節(jié)點與主節(jié)點斷線時間超過cluster-node-time*cluster-slave-validity-factor
粒褒,則當前從節(jié)點不具備故障轉移資格,cluster-slave-validity-factor
設置為0代表任何slave都可以被轉換為master