<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一舉拿下高可用與分布式協(xié)調(diào)系統(tǒng)設(shè)計(jì)!

          共 5630字,需瀏覽 12分鐘

           ·

          2021-05-20 09:52

          本文已整理致我的 github 地址 https://github.com/allentofight/easy-cs,歡迎大家 star 支持一下

          前言

          上文中我們了解到, canal 可以通過訂閱 binlog 日志來提供增量數(shù)據(jù)訂閱和消費(fèi),通過這種方式可以實(shí)現(xiàn)數(shù)據(jù)庫的實(shí)時(shí)備份,實(shí)時(shí)索引構(gòu)建等

          我們?cè)賮碓敿?xì)看看它的工作原理

          如圖示,每個(gè) server 會(huì)啟動(dòng)多個(gè)實(shí)例(instance),每個(gè)實(shí)例會(huì)訂閱不同表的 binlog,實(shí)例主要負(fù)責(zé)將 binlog 日志解析成程序易讀的結(jié)構(gòu)化數(shù)據(jù)(解析后包含變更記錄的主鍵,字段變更后的值等),Canal Client 拿到結(jié)構(gòu)化數(shù)據(jù)后再將其同步給其他 DB, MQ 等,同一時(shí)間某張表的 binlog 只能被一個(gè) Instance 訂閱處理,這是為了保障 binlog 處理的順序性

          現(xiàn)在問題來了,如果這臺(tái) Canal Server 掛掉了該怎么辦,換句話說如何實(shí)現(xiàn) server 的 HA(High Availability,即高可用)呢。

          最容易想到的當(dāng)然是準(zhǔn)備幾個(gè)備份的 Canal Server(以下簡(jiǎn)稱備機(jī)),這樣的話當(dāng)主 Canal Server(工作中的 Server,以下簡(jiǎn)稱主機(jī))宕機(jī)了,備機(jī)就可以頂上去工作了。那么這里就涉及到兩個(gè)問題

          1. 如果有三臺(tái)機(jī)器(一臺(tái)充當(dāng)主機(jī),兩臺(tái)充當(dāng)備機(jī)),啟動(dòng)后到底哪個(gè) Canal Server 為主機(jī)?

          2. 如果主機(jī)掛了,備機(jī)如何及時(shí)發(fā)現(xiàn)并接手主機(jī)的工作呢

          顯然這兩個(gè)問題都涉及到多進(jìn)程通信,所以最好引入一個(gè)中間層來處理,我們需要設(shè)計(jì)一個(gè)分布式協(xié)調(diào)系統(tǒng)來完成這兩個(gè)需求,先給這個(gè)系統(tǒng)取個(gè)名字吧,姑且將其稱為 Zookeeper,簡(jiǎn)稱 ZK。

          接下來我們來看看 ZK 需要如何設(shè)計(jì)才能滿足我們上述的兩個(gè)需求

          設(shè)計(jì)分布式鎖來解決主備角色問題

          先來看問題一

          如果有三臺(tái)或更多機(jī)器啟動(dòng)的話,到底哪個(gè) Canal Server 為主機(jī)?

          顯然分布式鎖是可以滿足這個(gè)需求的,三臺(tái)機(jī)器啟動(dòng)后主動(dòng)去獲取這個(gè)分布式鎖,獲取成功的則充當(dāng)主機(jī),獲取失敗的則作為備機(jī)。

          所以 ZK 必須要具有分布式鎖的功能,可能有人說我可以引入 Redis,MySQL,因?yàn)檫@兩者都具有分布式鎖的功能,但這樣相當(dāng)于在一個(gè)系統(tǒng)里又引入了額外的組件,系統(tǒng)的復(fù)雜度上升了,而且也要保證新引入組件的高可用,不太可取,所以這次我們打算另辟蹊徑直接在 ZK 中設(shè)計(jì)這樣的分布式鎖,首先要為 ZK 設(shè)計(jì)一個(gè)數(shù)據(jù)結(jié)構(gòu)。

          我們用類似 Linux 文件系統(tǒng)的樹狀結(jié)構(gòu)來作為此分布式系統(tǒng)的數(shù)據(jù)結(jié)構(gòu)。

          根節(jié)點(diǎn)為 /,每個(gè)節(jié)點(diǎn)下的子節(jié)點(diǎn)名稱是唯一的(也就意味著根節(jié)點(diǎn)下的所有節(jié)點(diǎn)名稱唯一),這樣的話三個(gè) Canal Server 啟動(dòng)后,會(huì)首先嘗試著在根節(jié)點(diǎn)下去創(chuàng)建同樣名字的節(jié)點(diǎn)(假設(shè)為 /lock1),那么由于這個(gè)節(jié)點(diǎn)名稱是唯一的,只能創(chuàng)建一個(gè) /lock1 這樣的節(jié)點(diǎn),其他的再申請(qǐng)創(chuàng)建會(huì)失敗,于是給我們一個(gè)思路:誰先創(chuàng)建成功,誰就為主節(jié)點(diǎn),其余的為備機(jī),這樣的話主備問題就解決了。

          如何讓備機(jī)發(fā)現(xiàn)主機(jī)宕機(jī)

          再來看第二個(gè)問題

          備機(jī)如何知道主機(jī)宕機(jī)?

          前文提到主機(jī)會(huì)在 ZK 中創(chuàng)建一個(gè)節(jié)點(diǎn) /lock1,創(chuàng)建成功即為主機(jī),在創(chuàng)建節(jié)點(diǎn)之前,主機(jī)首先要通過 TCP 與分布式協(xié)調(diào)系統(tǒng)建立連接,這個(gè)連接會(huì)長期保活,主機(jī)會(huì)定期發(fā)送心跳來讓 ZK 感知到它的存在,這樣 ZK 就會(huì)知道主機(jī)還存活著,如果在指定的時(shí)間內(nèi)(比如 2s )ZK 沒有收到主機(jī)發(fā)來的心跳,就會(huì)認(rèn)為主機(jī)宕機(jī)了,此時(shí)就會(huì)發(fā)通知給備機(jī)了。

          這個(gè)通知該怎么發(fā)呢,ZK 不僅是為 Canal 服務(wù)的,還有很多其他的服務(wù)可能也需要在 ZK 上創(chuàng)建節(jié)點(diǎn),不同的服務(wù)對(duì)應(yīng)創(chuàng)建的節(jié)點(diǎn)都是不一樣的,比如 canal 服務(wù)創(chuàng)建的節(jié)點(diǎn)是 /lock1,庫存服務(wù)創(chuàng)建的節(jié)點(diǎn)為 /lock2,總不可能 canal 服務(wù)的主機(jī)宕機(jī)了,卻要通知庫存服務(wù)的備機(jī)吧。我們應(yīng)該只通知當(dāng)時(shí)創(chuàng)建 /lock1 失敗的那些機(jī)器。

          于是我們需要建立一個(gè)/lock1 與對(duì)應(yīng)機(jī)器的映射列表,如下

          每個(gè)創(chuàng)建 /lock1 節(jié)點(diǎn)失敗的備機(jī)都被放到 key 為 /lock1 對(duì)應(yīng)的列表里

          我們把這個(gè)流程稱為注冊(cè),這樣的話每個(gè)節(jié)點(diǎn)對(duì)應(yīng)的機(jī)器就可以查到了,如果創(chuàng)建 /lock1 節(jié)點(diǎn)的主機(jī)宕機(jī)了,ZK 發(fā)現(xiàn)后只要通過映射列表通知/lock1對(duì)應(yīng)的備用機(jī)器列表就行啦(同時(shí) ZK 要把 /lock1 這個(gè)節(jié)點(diǎn)給刪掉,代表分布式鎖釋放了。)我們把這種備用機(jī)器能感知到節(jié)點(diǎn)被刪除的機(jī)制稱為 watch 機(jī)制,它的運(yùn)行機(jī)制如下

          1、 注冊(cè):client 創(chuàng)建節(jié)點(diǎn) /lock1 失敗后會(huì)監(jiān)聽此結(jié)點(diǎn),一旦此節(jié)點(diǎn)消失會(huì)收到 ZK 的通知
          2、 存儲(chǔ):將此 watcher 保存在客戶端的 watcherManager 中,我們可以簡(jiǎn)單地認(rèn)為 watcher 的格式如下

          注:為了簡(jiǎn)化講解流程,我們給 watcher 加了一個(gè) path 字段,實(shí)際 watcher 并沒有,不過原理其實(shí)是一樣的

          path 即我們監(jiān)聽的結(jié)點(diǎn),如本例中的 /lock1,keepState 即事件狀態(tài),比如連接成功,連接斷開等,EventType 即節(jié)點(diǎn)對(duì)應(yīng)的事件,如創(chuàng)建,刪除,子節(jié)點(diǎn)變更等事件。

          3、通知:ZK 將節(jié)點(diǎn)路徑及其刪除/創(chuàng)建事件通知到 client,client 通過這些信息在 watchManager 中找到相應(yīng)的 watcher,就可以做相應(yīng)的處理,比如監(jiān)聽到節(jié)點(diǎn)被刪了,就可以感知到主機(jī)宕機(jī)了,它就可以嘗試著去創(chuàng)建 /lock1 了,哪個(gè)創(chuàng)建成功則哪個(gè)備機(jī)就成為主機(jī)了。

          通過以上的 watch 工作機(jī)制不難設(shè)計(jì)出我們的高可用方案,如下:

          1. 假設(shè)現(xiàn)在有 A,B 兩臺(tái) Canal Server,為了成為主節(jié)點(diǎn),它們首先向 zk 申請(qǐng)創(chuàng)建 /lock1節(jié)點(diǎn)

          2. A 創(chuàng)建成功后即成為主節(jié)點(diǎn)開始工作, B 再去創(chuàng)建節(jié)點(diǎn)則失敗,會(huì)作為備機(jī),但同時(shí) B 會(huì)使用 watch 機(jī)制來監(jiān)聽 /lock1 節(jié)點(diǎn)(即將 B 機(jī)器注冊(cè)到 ZK 中 /lock1 對(duì)應(yīng)的機(jī)器列表中,/local1被刪除后會(huì)通過此機(jī)器列表,B 就能感知到 A 宕機(jī)了)

          3. 一旦 A 不可用,則 ZK 會(huì)刪除 /lock1 節(jié)點(diǎn),同時(shí)也會(huì)通知 B,B 收到通知后,通過本地的 watchManager 找到此 watcher,得知是 /lock1 這個(gè)節(jié)點(diǎn)被刪除事件后會(huì)再次嘗試創(chuàng)建/lock1 節(jié)點(diǎn),創(chuàng)建成功后則啟動(dòng)作為主節(jié)點(diǎn)工作,此刻 Canal Client 也會(huì)注意到 /lock1 創(chuàng)建了(Canal Client 啟動(dòng)后也用 watch 機(jī)制監(jiān)聽 /lock1 節(jié)點(diǎn)的創(chuàng)建事件),lock1 節(jié)點(diǎn)可以存儲(chǔ) Canal Server 地址的,這樣的話通知 Canal Client 時(shí)可以把 B 地址傳過來,Canal Client 就會(huì)與此機(jī)器建立連接了。

          驚群效應(yīng)與解決方案

          按照上述的設(shè)計(jì)方案其實(shí)已經(jīng)能滿足 Canal 的高可用設(shè)計(jì)了,不過我們目前設(shè)計(jì)的  ZK 系統(tǒng)實(shí)現(xiàn)的分布式鎖有兩個(gè)問題

          1. 假設(shè)有幾十臺(tái)備機(jī),當(dāng)主機(jī)宕機(jī)后,這幾十臺(tái)備機(jī)都會(huì)嘗試著去創(chuàng)建 /lock1 這個(gè)節(jié)點(diǎn),但只有一臺(tái)機(jī)器創(chuàng)建成功并成為主機(jī),另外幾十臺(tái)機(jī)器在創(chuàng)建節(jié)點(diǎn)失敗后則會(huì)馬上處于等待狀態(tài),這就是我們所說的驚群效應(yīng)(也叫羊群效應(yīng)),不難發(fā)現(xiàn)驚群效應(yīng)會(huì)造成資源的極大浪費(fèi),那么宕機(jī)后能否只通知一個(gè)備用節(jié)點(diǎn)響應(yīng),這樣其他備用節(jié)點(diǎn)就不用群驚群乍了,能極大地節(jié)省資源。

          2. 當(dāng)前的分布式鎖是非公平鎖,這樣會(huì)造成饑餓現(xiàn)象,可能一些備機(jī)永遠(yuǎn)沒有機(jī)會(huì)獲取這個(gè)鎖了,如何讓它成為公平鎖,讓每個(gè)備用節(jié)點(diǎn)都有機(jī)會(huì)獲取這個(gè)鎖以讓它們都有機(jī)會(huì)成為主機(jī)呢。

          解決方案如下:

          每個(gè)機(jī)器都會(huì)在 /lock1 下創(chuàng)建一個(gè)子節(jié)點(diǎn),子節(jié)點(diǎn)的編號(hào)會(huì)按申請(qǐng)順序遞增,編號(hào)最小的那個(gè)節(jié)點(diǎn)表示其對(duì)應(yīng)的機(jī)器持有了分布式鎖,其余機(jī)器只會(huì)監(jiān)聽比它小一級(jí)的那個(gè)節(jié)點(diǎn),這樣當(dāng)某個(gè)節(jié)點(diǎn)宕機(jī)了,只會(huì)通知這一個(gè)監(jiān)聽機(jī)器,避免了驚群效應(yīng)

          如圖示,工作機(jī)制如下:

          1. 一開始,多臺(tái)機(jī)器都在 /lock1 節(jié)點(diǎn)下創(chuàng)建以 sub-xxx 依序遞增的節(jié)點(diǎn),假設(shè)現(xiàn)在有一臺(tái)機(jī)器創(chuàng)建了一個(gè) sub-000001,則由于它的序號(hào)最小,表示它占有的分布式鎖,其他機(jī)器則會(huì)依次創(chuàng)建 sub-000002,sub-000003這樣依序遞增的節(jié)點(diǎn)。

          2. 每個(gè)節(jié)點(diǎn)對(duì)應(yīng)的機(jī)器只會(huì)監(jiān)聽(watch)比它小一號(hào)的節(jié)點(diǎn)

          3. 這樣的話,如果分布式鎖釋放了,則只會(huì)通知比它大一號(hào)的節(jié)點(diǎn),如 sub-000001 節(jié)點(diǎn)對(duì)應(yīng)的主機(jī)宕機(jī)了,只會(huì)通知機(jī)器 B(此時(shí) ZK 也會(huì)把 sub-000001 這個(gè)臨時(shí)節(jié)點(diǎn)給刪了)這樣 B 持有的節(jié)點(diǎn)序號(hào)最小,它也就持有了分布式鎖。

          通過這種方式我們可以也注意到機(jī)器獲取鎖的順序與其創(chuàng)建的節(jié)點(diǎn)順序保持了一致,也就實(shí)現(xiàn)了公平鎖。

          ZK 簡(jiǎn)介

          以上就是利用 ZK 來實(shí)現(xiàn)分布式鎖的一個(gè)簡(jiǎn)單案例,當(dāng)然分布式鎖只是它眾多功能中的一個(gè)而已,作為一個(gè)分布式協(xié)調(diào)服務(wù),它還有配置管理,名字服務(wù),分布式同步以及集群管理等功能。先來簡(jiǎn)單看一下 ZK 的一些概念。

          節(jié)點(diǎn)

          我們已經(jīng)知道 ZK 采用的是一種類似 Linux 文件系統(tǒng)的樹形結(jié)構(gòu),樹上的每個(gè)節(jié)點(diǎn)我們把它稱為 Znode,Znode 節(jié)點(diǎn)分為以下四大類

          1. 臨時(shí)節(jié)點(diǎn):前文我們提到主機(jī)宕機(jī)后 /lock1 就會(huì)被刪除,這種主機(jī)與 ZK 斷開鏈接就被刪除的節(jié)點(diǎn)我們稱其為臨時(shí)節(jié)點(diǎn)

          2. 臨時(shí)順序節(jié)點(diǎn): 前文我們?yōu)榱藢?shí)現(xiàn)公平鎖而創(chuàng)建的按申請(qǐng)次序順序遞增的節(jié)點(diǎn)

          3. 永久節(jié)點(diǎn):客戶端與 ZK 的連接斷開后,節(jié)點(diǎn)還在,甚至 ZK 重啟后節(jié)點(diǎn)也還在

          4. 永久順序節(jié)點(diǎn):在永久節(jié)點(diǎn)的基礎(chǔ)上加上了按申請(qǐng)順序遞增的功能的節(jié)點(diǎn)

          每個(gè) Znode 節(jié)點(diǎn)都是可以存儲(chǔ)數(shù)據(jù)的,默認(rèn)是 1M,這樣的話可以作為配置管理中心存儲(chǔ)一些比較重要的數(shù)據(jù)

          watcher 監(jiān)聽事件

          前文我們提到了備份機(jī)器可以通過  watcher 機(jī)制來監(jiān)聽節(jié)點(diǎn)是否存在,從而可以及時(shí)響應(yīng)這些事件作出處理,除了監(jiān)聽節(jié)點(diǎn)是否存在外,ZK 還提供了以下事件

          public enum EventType {
               None (-1), // 客戶端連接狀態(tài)發(fā)生變化的時(shí)候 會(huì)受到none事件
               NodeCreated (1), // 節(jié)點(diǎn)創(chuàng)建事件
               NodeDeleted (2), // 節(jié)點(diǎn)刪除事件
               NodeDataChanged (3), // 節(jié)點(diǎn)數(shù)據(jù)變化
               NodeChildrenChanged (4); // 子節(jié)點(diǎn)被創(chuàng)建,刪除觸發(fā)該事件
          }

          ZK 就是通過這些 watcher 事件來實(shí)現(xiàn)分布式協(xié)調(diào)服務(wù)的,接下來我們來看看 ZK 在生產(chǎn)上的兩個(gè)應(yīng)用

          ZK應(yīng)用

          作為 dubbo 注冊(cè)中心

          假設(shè)現(xiàn)在有兩個(gè)服務(wù),用戶服務(wù)和訂單服務(wù),為了高可用,訂單服務(wù)會(huì)部署多臺(tái)機(jī)器,每個(gè)訂單服務(wù)的機(jī)器都會(huì)在 ZK 中注冊(cè),每個(gè)節(jié)點(diǎn)為臨時(shí)節(jié)點(diǎn),如下

          調(diào)用方(用戶服務(wù))會(huì)獲取 /orders 下的所有子節(jié)點(diǎn),即所有機(jī)器列表,也會(huì)監(jiān)聽 /orders 節(jié)點(diǎn)的 NodeChildrenChanged(子節(jié)點(diǎn)被創(chuàng)建或被刪除)事件,然后通過一定的負(fù)載均衡算法選擇一個(gè)來連接,假如其中一臺(tái)機(jī)器如 192.168.11.1 宕機(jī)了,則其對(duì)應(yīng)的臨時(shí)節(jié)點(diǎn)會(huì)被刪除,同時(shí)用戶服務(wù)會(huì)能收到此 watch 事件,于是會(huì)重新獲取 /orders 的子節(jié)點(diǎn),此時(shí)由于宕機(jī)對(duì)應(yīng)的子節(jié)點(diǎn)被刪除了,所以只會(huì)獲取 192.168.11.2 和  192.168.11.3 這兩個(gè)子節(jié)點(diǎn),這樣就避免了連接 192.168.11.1 這個(gè)不可用的機(jī)器了

          作為配置中心

          在生產(chǎn)環(huán)境上 ,我們經(jīng)常需要配置一些變動(dòng)頻繁,需要實(shí)時(shí)生效的數(shù)據(jù),比如某個(gè)功能上線,我們需要針對(duì)某些用戶做灰度,這個(gè)灰度規(guī)模是逐漸擴(kuò)大的,我們就需要配置一下這個(gè)百分比來讓每個(gè)機(jī)器實(shí)時(shí)生效,這時(shí)就可以讓 ZK 作為配置中心,讓每臺(tái)線上的機(jī)器監(jiān)聽配置節(jié)點(diǎn)的 NodeDataChanged(節(jié)點(diǎn)數(shù)據(jù)變化) 事件,這樣只要這個(gè)節(jié)點(diǎn)數(shù)據(jù)變化了,其他機(jī)器可以立即收到通知更新,讓此修改立即生效,我司用的 360 開源的分布式配置管理系統(tǒng) QConf 就是基于 ZK 開發(fā)的。

          總結(jié)

          通過本文相信大家不難理解 ZK 的工作機(jī)制,主要要理解它的樹狀結(jié)構(gòu),節(jié)點(diǎn)及 watch 工作機(jī)制,掌握了這些就能理解 ZK 作為配置中心,分布式鎖,域名服務(wù)的原理,當(dāng)然如果要更深入地了解 ZK,光掌握這些還不夠,比如 ZK 如果只有一臺(tái),那會(huì)有單點(diǎn)故障,就要配置 ZK 集群,既然是集群,那如何保證數(shù)據(jù)一致呢,你需要去了解 ZAB 協(xié)議,選舉機(jī)制等,建議大家看看《ZooKeeper分布式過程協(xié)同技術(shù)詳解》這本書,會(huì)讓你對(duì) ZK 有更深入的理解。

          推薦閱讀:

          一文讀懂微內(nèi)核架構(gòu)

          架構(gòu)師之路一-架構(gòu)師入門指引

          Linux 文件搜索神器 find 實(shí)戰(zhàn)詳解,建議收藏!

          貓撲,涼了!

          搞清楚這 10 幾個(gè)后端面試問題,工作穩(wěn)了!


          關(guān)號(hào)互聯(lián)網(wǎng)全棧架構(gòu),價(jià)。

          瀏覽 21
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  四色成人AV永久网址 | 国产一级a毛一级a看免费观看 | 一级黄色性爱免费网站 | 久久er99热精品一区二区 | 国产喷水福利 |