10.3、節(jié)點(diǎn)通信

節(jié)點(diǎn)通信

  1. 通信流程

    在分布式存儲(chǔ)中需要提供維護(hù)節(jié)點(diǎn)元數(shù)據(jù)信息的機(jī)制劫樟,所謂元數(shù)據(jù)是指:節(jié)點(diǎn)負(fù)責(zé)那些數(shù)據(jù),是否出現(xiàn)故障等狀態(tài)信息织堂。常見的元數(shù)據(jù)維護(hù)方式分為:集中式和P2P方式毅哗。Redis集群采用P2P的Gossip(留言)協(xié)議,Gossip協(xié)議工作原理就是節(jié)點(diǎn)彼此不斷通信交換信息捧挺,一段時(shí)間后所有的節(jié)點(diǎn)都會(huì)知道集群完整的信息虑绵,這種方式類似留言傳播。

    通信過程說明闽烙;

    1)集群中的每個(gè)節(jié)點(diǎn)都會(huì)單獨(dú)開辟一個(gè)TCP通道翅睛,用于節(jié)點(diǎn)之間彼此通信,通信端口號(hào)在基礎(chǔ)端口上加10000.

    2)每個(gè)節(jié)點(diǎn)在固定周期內(nèi)通過特定規(guī)則選擇幾個(gè)節(jié)點(diǎn)發(fā)送ping消息黑竞。

    3)接收到ping消息的節(jié)點(diǎn)用pong消息作為相應(yīng)捕发。

    集群中每個(gè)節(jié)點(diǎn)通過一定規(guī)則挑選要通信的節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)可能知道全部節(jié)點(diǎn)很魂,也可能僅知道部分節(jié)點(diǎn)扎酷,只要這些節(jié)點(diǎn)彼此可以正常通信,最終它們會(huì)達(dá)到一致的狀態(tài)遏匆。當(dāng)節(jié)點(diǎn)出故障法挨、新節(jié)點(diǎn)加入、主從角色變化幅聘、槽信息變更等時(shí)間發(fā)生時(shí)凡纳,通過不斷的ping/pong消息通信,經(jīng)過一段時(shí)間后所有的節(jié)點(diǎn)都會(huì)知道整個(gè)集群全部節(jié)點(diǎn)的最新狀態(tài)帝蒿,從而達(dá)到集群狀態(tài)同步的目的荐糜。

  2. Gossip消息

    Gossip協(xié)議的主要職責(zé)就是信息交換。信息交換的載體就是節(jié)點(diǎn)彼此發(fā)送的Gossip消息,了解這些消息有助于我們理解集群如何完成信息交換暴氏。

    常用的Gossip消息可分為:ping消息延塑、pong消息、meet消息答渔、fail消息等关带。

    • meet消息:用于通知新節(jié)點(diǎn)加入。消息發(fā)送者通知接收者加入到當(dāng)前集群研儒,meet消息通信正常完成后豫缨,接收節(jié)點(diǎn)會(huì)加入到集群中并進(jìn)行周期性的ping独令、pong消息交換端朵。

    • ping消息:集群內(nèi)交換最頻繁的消息,集群內(nèi)每個(gè)節(jié)點(diǎn)每秒向多個(gè)其他節(jié)點(diǎn)發(fā)送ping消息燃箭,用于檢測節(jié)點(diǎn)是否在線和交換彼此狀態(tài)信息冲呢。ping消息發(fā)送封裝了自身節(jié)點(diǎn)和部分其他節(jié)點(diǎn)的狀態(tài)數(shù)據(jù)。

    • pong消息:當(dāng)接收到ping招狸、meet消息時(shí)敬拓,作為響應(yīng)消息回復(fù)給發(fā)送方確認(rèn)消息正常通信。pong消息內(nèi)部封裝了自身狀態(tài)數(shù)據(jù)裙戏。節(jié)點(diǎn)也可以向集群內(nèi)廣播自身的pong消息來通知整個(gè)集群對自身狀態(tài)進(jìn)行更新乘凸。

    • fail消息:當(dāng)節(jié)點(diǎn)判定集群內(nèi)另一個(gè)節(jié)點(diǎn)下線是,會(huì)向集群內(nèi)廣播一個(gè)fail消息累榜,其他節(jié)點(diǎn)接收到fail消息之后吧對應(yīng)節(jié)點(diǎn)更新為下線狀態(tài)营勤。

    所有的消息格式劃分為:消息頭和消息體。消息頭包含發(fā)送節(jié)點(diǎn)自身狀態(tài)數(shù)據(jù)壹罚,接收節(jié)點(diǎn)根據(jù)消息頭就可以獲取到發(fā)送節(jié)點(diǎn)的相關(guān)數(shù)據(jù)葛作,結(jié)構(gòu)如下:

    typedef struct {
        char sig[4]; /* 信號(hào)標(biāo)示 */
        uint32_t totlen; /* 消息總長度 */
        uint16_t ver; /* 協(xié)議版本 */
        uint16_t type; /* 消息類型,用于區(qū)分meet猖凛,ping赂蠢,ping等消息 */
        uint16_t count; /* 消息體包含的節(jié)點(diǎn)數(shù)量,僅用于meet辨泳,ping虱岂,pong消息類型 */
        uint64_t currentEpoch; /* 當(dāng)前發(fā)送節(jié)點(diǎn)的配置紀(jì)元 */
        uint64_t configEpoch; /* 主節(jié)點(diǎn)/從節(jié)點(diǎn)的主節(jié)點(diǎn)配置紀(jì)元 */
        uint64_t offset; /* 復(fù)制偏移量 */
        char sender[CLUSTER_NAMELEN]; /* 發(fā)送節(jié)點(diǎn)的nodeId */
        unsigned char myslots[CLUSTER_SLOTS/8]; /* 發(fā)送節(jié)點(diǎn)負(fù)責(zé)的槽信息 */
        char slaveof[CLUSTER_NAMELEN]; /* 如果發(fā)送節(jié)點(diǎn)是從節(jié)點(diǎn),記錄對應(yīng)主節(jié)點(diǎn)的nodeId */
        uint16_t port; /* 端口號(hào) */
        uint16_t flags; /* 發(fā)送節(jié)點(diǎn)標(biāo)識(shí)菠红,區(qū)分主從角色量瓜,是否下線等 */
        unsigned char state; /* 發(fā)送節(jié)點(diǎn)所處的集群狀態(tài) */
        unsigned char mflags[3]; /* 消息標(biāo)識(shí) */
        union clusterMsgData data; /* 消息正文 */
    } clusterMsg;
    

    集群內(nèi)所有的消息都采用相同的消息頭結(jié)構(gòu)clusterMsg,它包含了發(fā)送節(jié)點(diǎn)關(guān)鍵信息途乃,如節(jié)點(diǎn)id绍傲、槽映射、節(jié)點(diǎn)標(biāo)識(shí)(主從角色,是否下線)等烫饼。消息體在Redis內(nèi)部采用clusterMsgData結(jié)構(gòu)聲明猎塞,結(jié)構(gòu)如下:

    union clusterMsgData {
        /* ping,meet,pong 消息體 */
        struct {
            /* gossip消息結(jié)構(gòu)數(shù)組 */
            clusterMsgDataGossip gossip[1];
        } ping;
        /* FAIL 消息體 */
        struct {
            clusterMsgDataFail about;
        } fail;
    // ...
    };
    

    消息體clusterMsgData定義發(fā)送消息的數(shù)據(jù),其中ping杠纵、meet荠耽、pong都采用clusterMsgDataGossip數(shù)組作為消息體數(shù)據(jù),實(shí)際消息類型使用消息頭的type屬性區(qū)分比藻。每個(gè)消息體包含該節(jié)點(diǎn)的多個(gè)clusterMsgDataGossip結(jié)構(gòu)數(shù)據(jù)铝量,用于信息交換,結(jié)構(gòu)如下:

    typedef struct {
        char nodename[CLUSTER_NAMELEN]; /* 節(jié)點(diǎn)的nodeId */
        uint32_t ping_sent; /* 最后一次向該節(jié)點(diǎn)發(fā)送ping消息時(shí)間 */
        uint32_t pong_received; /* 最后一次接收該節(jié)點(diǎn)pong消息時(shí)間 */
        char ip[NET_IP_STR_LEN]; /* IP */
        uint16_t port; /* port */
        uint16_t flags; /* 該節(jié)點(diǎn)標(biāo)識(shí) */
    } clusterMsgDataGossip;
    

    當(dāng)接收到ping银亲、meet消息時(shí)慢叨,接收節(jié)點(diǎn)會(huì)解析消息內(nèi)容并根據(jù)自身的識(shí)別情況作出相應(yīng)處理,執(zhí)行解析消息頭和消息體的流程:

    • 解析消息頭過程:消息頭包含了發(fā)送節(jié)點(diǎn)的信息务蝠,如果發(fā)送節(jié)點(diǎn)是新節(jié)點(diǎn)且消息是meet類型拍谐,則加入本地節(jié)點(diǎn)列表;如果是已知節(jié)點(diǎn)馏段,則嘗試更新發(fā)送節(jié)點(diǎn)的狀態(tài)轩拨,如槽映射關(guān)系、主從角色等狀態(tài)院喜。

    • 解析消息體過程:如果消息體的clusterMsgDataGossip數(shù)組包含的節(jié)點(diǎn)是新節(jié)點(diǎn)亡蓉,則嘗試發(fā)起與新節(jié)點(diǎn)的meet握手流程;如果是已知節(jié)點(diǎn)喷舀,則根據(jù)clusterMsgDataGossip中的flags字段判斷該節(jié)點(diǎn)是否下線砍濒,用于故障轉(zhuǎn)移。

    消息處理完后回復(fù)pong消息元咙,內(nèi)容同樣包含消息頭和消息體梯影,發(fā)送節(jié)點(diǎn)接收到回復(fù)的pong消息后,采用類似的流程解析處理消息并更新與接收節(jié)點(diǎn)最后通信時(shí)間庶香,完成那個(gè)一次消息通信甲棍。

  3. 節(jié)點(diǎn)選擇

    雖然Gossip協(xié)議的信息交換機(jī)制具有天然的分布式特性,但它是有成本的赶掖。由于內(nèi)部需要頻繁地進(jìn)行節(jié)點(diǎn)信息交換感猛,而ping/pong消息會(huì)攜帶當(dāng)前節(jié)點(diǎn)和部分其他節(jié)點(diǎn)的狀態(tài)數(shù)據(jù),勢必會(huì)加重帶寬和計(jì)算的負(fù)擔(dān)奢赂。Redis集群內(nèi)節(jié)點(diǎn)通信采用固定頻率(定時(shí)任務(wù)每秒執(zhí)行10次)陪白。因此節(jié)點(diǎn)每次選擇需要通信的節(jié)點(diǎn)列表變得非常重要。通信節(jié)點(diǎn)選擇過多雖然可以做到信息及時(shí)交換但成本過高膳灶。節(jié)點(diǎn)選擇過少會(huì)降低集群內(nèi)所有節(jié)點(diǎn)彼此信息交換頻率咱士,從而影響故障判定立由、新節(jié)點(diǎn)發(fā)現(xiàn)等需求的速度。因此Redis集群Gossip協(xié)議需要兼顧信息交換實(shí)時(shí)性和成本開銷序厉。

    消息交換的成本主要體現(xiàn)在單位時(shí)間選擇發(fā)送消息的節(jié)點(diǎn)數(shù)量和每個(gè)消息攜帶的數(shù)據(jù)量锐膜。

    1. 選擇發(fā)送消息的節(jié)點(diǎn)數(shù)量

      集群內(nèi)每個(gè)節(jié)點(diǎn)維護(hù)定時(shí)任務(wù)默認(rèn)每秒執(zhí)行10次,每秒會(huì)隨機(jī)選取5個(gè)節(jié)點(diǎn)找出最久沒有通信的節(jié)點(diǎn)發(fā)送ping消息弛房,用于保證Gossip信息交換的隨機(jī)性道盏。每100毫秒都會(huì)掃描本地節(jié)點(diǎn)列表,如果發(fā)現(xiàn)節(jié)點(diǎn)最近一次接收pong消息的時(shí)間大于cluster_node_timeout/2文捶,則立刻發(fā)送ping消息荷逞,防止該節(jié)點(diǎn)的信息太長時(shí)間未更新。根據(jù)以上規(guī)則的出每個(gè)節(jié)點(diǎn)每秒需要發(fā)送ping消息的數(shù)量=1 + 10 * num(node.pong_received > cluster_node_timeout/2)粹排,因此cluster_node_timeout參數(shù)對消息發(fā)送的節(jié)點(diǎn)數(shù)量影響非常大种远。當(dāng)我們的帶寬資源緊張是,可以適當(dāng)調(diào)大這個(gè)參數(shù)恨搓,如從默認(rèn)15秒改為30秒來減低帶寬占用率院促。過度調(diào)大cluster_node_timeout會(huì)影響消息交換的頻率從而影響故障轉(zhuǎn)移筏养、槽信息更新斧抱、新節(jié)點(diǎn)發(fā)現(xiàn)的速度。因此需要根據(jù)業(yè)務(wù)容忍度和資源消耗進(jìn)行平衡渐溶。同時(shí)整個(gè)集群消息總交換量也跟節(jié)點(diǎn)數(shù)成正比辉浦。

    2. 消息數(shù)據(jù)量

      每個(gè)ping消息的數(shù)據(jù)量體現(xiàn)在消息頭和消息體中,其中消息頭主要占用空間的字段是myslots[CLUSTER_SLOTS/8]茎辐,占用2KB宪郊,這塊空間占用相對固定。消息體會(huì)攜帶一定數(shù)量的其他節(jié)點(diǎn)信息用于信息交換拖陆。具體數(shù)量見一下偽代碼:

      def get_wanted():
          int total_size = size(cluster.nodes)
          # 默認(rèn)包含節(jié)點(diǎn)總量的1/10
          int wanted = floor(total_size/10);
          if wanted > 3:
              # 至少攜帶3個(gè)其他節(jié)點(diǎn)信息
              wanted = 3;
          if wanted > total_size - 2 :
              # 最多包含total_size - 2 個(gè)
              wanted = total_size - 2;
      return wanted;
      

      根據(jù)偽代碼可以看出消息體攜帶數(shù)據(jù)量跟集群的節(jié)點(diǎn)數(shù)息息相關(guān)弛槐,更大的集群每次消息通信的成本也就更高,因此對于Redis集群來說并不是大而全的集群更好依啰,對于集群規(guī)暮醮控制的建議在后面的章節(jié)會(huì)介紹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末速警,一起剝皮案震驚了整個(gè)濱河市叹誉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌闷旧,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件忙灼,死亡現(xiàn)場離奇詭異匠襟,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)酸舍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門弱匪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來萧诫,“玉大人枝嘶,你說我怎么就攤上這事帘饶〖翱蹋” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵竞阐,是天一觀的道長缴饭。 經(jīng)常有香客問我,道長骆莹,這世上最難降的妖魔是什么颗搂? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮幕垦,結(jié)果婚禮上丢氢,老公的妹妹穿的比我還像新娘。我一直安慰自己先改,他們只是感情好疚察,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著仇奶,像睡著了一般貌嫡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猜嘱,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天衅枫,我揣著相機(jī)與錄音,去河邊找鬼朗伶。 笑死弦撩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的论皆。 我是一名探鬼主播益楼,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼猾漫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了感凤?” 一聲冷哼從身側(cè)響起悯周,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎陪竿,沒想到半個(gè)月后禽翼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡族跛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年闰挡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片礁哄。...
    茶點(diǎn)故事閱讀 38,094評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡长酗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出桐绒,到底是詐尸還是另有隱情夺脾,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布茉继,位于F島的核電站咧叭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏馒疹。R本人自食惡果不足惜佳簸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一乙墙、第九天 我趴在偏房一處隱蔽的房頂上張望颖变。 院中可真熱鬧,春花似錦听想、人聲如沸腥刹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衔峰。三九已至,卻和暖如春蛙粘,著一層夾襖步出監(jiān)牢的瞬間垫卤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工出牧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留穴肘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓舔痕,卻偏偏與公主長得像评抚,于是被迫代替她去往敵國和親豹缀。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評論 2 345