天天看點

JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景

Zookeeper學習筆記

  • Zookeeper典型應用場景
    • 配置維護
      • 什麼是配置維護
      • 資料釋出/訂閱
      • 實作原理
    • 命名服務
      • 什麼是命名服務
      • 實作原理
    • 叢集管理
      • 需求
      • 基本原理
      • 分布式日志收集系統
        • 系統組成
        • 收集器的注冊
        • 任務配置設定
        • 狀态收集
        • 任務再配置設定
    • DNS服務
      • 什麼是DNS
      • 基本DNS實作原理
      • 具有狀态收集功能的DNS實作原理
      • 擴充-叢集監控平台
      • 優化設計
    • Master選舉
      • 什麼是Master選舉
      • 廣告推薦系統
        • 需求
        • 分析
        • 架構
        • Master選舉
    • 分布式同步
      • 什麼是分布式同步
      • 實作原理
      • MySQL資料複制總線
        • 資料複制總線組成
        • 資料複制總線工作原理
          • 複制任務注冊
          • replicator熱備
          • 主備切換
    • 分布式鎖
      • 排他鎖
      • 共享鎖
      • 分布式鎖的實作
      • 分布式鎖的改進
    • 分布式隊列
      • FIFO隊列
      • 分布式屏障Barrier隊列

Zookeeper典型應用場景

為進一步加強對 zk 的認識,了解 zk 的作用,下面詳細介紹一下 zk 在生産環境中的典型應用場景。

配置維護

什麼是配置維護

  • 分布式系統中,很多服務都是部署在叢集中的,即多台伺服器中部署着完全相同的應用,起着完全相同的作用。當然,叢集中的這些伺服器的配置檔案時完全相同的。
  • 若叢集中伺服器的配置檔案需要進行修改,那麼我們需要逐台修改這些伺服器中的配置檔案。如果我們叢集服務比較少,那麼這些修改還不是太麻煩;但如果叢集伺服器特别多,比如某些大型網際網路公司的 Hadoop 叢集有數千台伺服器,那麼純手工的更改這些配置檔案幾乎就是一件不可能完成的任務。即使使用大量人力進行修改可行,但過多的人員參與,出錯的機率大大提升,對于叢集所形成的危險是很大的。

資料釋出/訂閱

  • 釋出/訂閱模式是一對多的關系,多個訂閱者對象同時監聽某一主題對象,這個主題對象在自身狀态發生變化時會通知所有的訂閱者對象,使它們能自動的更新自己的狀态。釋出/訂閱可以使得釋出方和訂閱方獨立封裝、獨立改變。當一個對象的改變需要同時改變其他對象,而且它不知道具體有多少對象需要改變時可以使用釋出/訂閱模式。釋出/訂閱模式在分布式系統中的典型應用有配置管理和服務發現、注冊。
  • 參考資料:https://www.cnblogs.com/sky-sql/p/6685531.html

實作原理

JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景
  • zk可以通過“釋出/訂閱模型”實作對叢集配置檔案的管理和維護。“釋出/訂閱模型”分為推模式(Push)與拉模式(Pull)。zk的“釋出/訂閱模型”采用的是推拉相結合的模式。
  • 首先每一個叢集用戶端需要向 zk 注冊一個某資料節點的 watcher 監聽,當釋出者将更新過的配置資料釋出到 zk 後,就會觸發各個 watcher 監聽,即 zk 會向每一個訂閱者推送 watcher 事件,訂閱者接收到 watcher 事件後,就會從 zk 中拉取更新過的配置資料。Zookeeper具有同步操作的原子性,可以確定每個叢集主機的配置資訊都能被正确的更新。

命名服務

什麼是命名服務

  • 命名服務是指可以為一個範圍内的元素命名一個唯一辨別,以與其它元素進行區分。在分布式系統中被命名的實體可以是叢集中的主機、服務位址等。

實作原理

JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景
  • 通過利用 zk 中順序節點自動生成唯一編号的特定來實作命名服務。
  • 首先建立一組業務相關的節點,然後再在這些節點下再建立順序節點,此時的順序節點的路徑+名稱即為生成的唯一辨別。

叢集管理

需求

對于叢集,我們總是希望能夠随時擷取到以下資訊:

  • 目前叢集中各個主機的運作時狀态是怎樣的;
  • 及時了解到叢集中主機的上下線狀況;

    zk 可以提供叢集主機存活性監控功能。

基本原理

JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景

在 zk 中建立一個監控系統節點,例如 /clusterManager,然後将叢集主機均注冊為其臨時子節點。然後,再在 /clusterManager 上注冊一個 watcher 監聽。一旦叢集中添加或删除主機,就會觸發子節點清單數量變化的 watcher 事件。當然,至于監控程式捕獲到的 watcher 事件如何處理,那就要由監控程式的業務來定義了。

分布式日志收集系統

下面以分布式日志收集系統為例來分析 zk 對于叢集的管理

系統組成

JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景

首先,需要清除,分布式日志收集系統由三部分組成:日志源叢集、日志收集器叢集和zk叢集。

  • 日志源叢集:日志都是由該叢集中的主機生成。其會由于擴容、當機、網絡等問題引發叢集中主機數量的變化。
  • 日志收集器叢集:用于收集日志源叢集中主機生成的日志。一個日志收集器要收集基于日志源叢集中的日志資料,其也會由于擴容、當機、網絡等問題引發叢集主機數量的變化,進而引發收集任務的再配置設定。
  • zk叢集:用于注冊日志收集器。其自身主機數量的變化不是這裡要研究的内容。

收集器的注冊

JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景
  • 在 zk 建立一個節點作為收集器的根節點,例如 /logs/collector,然後,每個收集器在啟動時都會在該節點下建立自己的節點,例如 /logs/collector/[host]。這些節點是臨時節點還是持久節點呢?需要根據後面的分析确定。

任務配置設定

  • 等所有收集器都注冊完畢後,系統根據收集器的個數,将所有日志源叢集主機分組後,以資料内容的形式分别寫入到各個收集器節點。各個收集器就可以按照自己的日志源主機清單進行日志收集工作。

狀态收集

  • 這裡的狀态收集指的是兩方面的收集:一個是收集器收集其所管理的日志源主機狀态,一個是日志收集器的運作狀态。
  • 每個收集器會定期從自己所管理的日志源主機上讀取日志,若在指定時間内讀取不到日志資訊,則認為該日志源主機當機;在指定時間内可以讀取到的日志源主機則被認為該日志源主機是健康的。這些是否健康的資訊會作為資料内容定時寫入到收集器資料内容中。
  • 當然,目前收集器主機的運作狀态參數(開發者自己定義,例如已經收集了多少子節的日志、目前CPU、記憶體的使用情況等)也會定時以資料内容的形式寫入到收集器資料内容中。

任務再配置設定

當出現收集器挂掉或擴容,就需要動态進行日志收集任務再配置設定了。日志系統會在 /logs/collector 中注冊一個 watcher,随時關注其子節點數量的變化。當子節點數量發生變化時,進行任務再配置設定。通常配置設定方案有兩種:

  • 全局動态配置設定:将所有日志源主機全部進行統一再配置設定。該方案實作起來簡單,但對日志系統的性能會有較大的影響。
  • 局部動态配置設定:首先到定義各個收集器的負載判别标準,這個判别标準是由開發者自行定義的。可以根據每台收集器所配置設定的日志源主機數量判别,也可以根據收集器主機的硬體條件與日志源主機數量等參數綜合判别。若出現收集器挂掉,則根據各個收集器的負載情況,将挂掉的收集器的原任務配置設定給負載小的一個或若幹個收集器;若收集器擴容,則将負載較大的一個或若幹個收集器的部分日志源主機配置設定給新增的收集器。

DNS服務

zk 的 DNS 服務是命名服務的一種特殊用法,其對外表現出的功能主要是防止提供者的單點問題,實作對提供者的負載均衡。

JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景

什麼是DNS

  • DNS(Domain Name System:域名系統),即可以将一個名稱與特殊的主機IP+端口号進行綁定。zk 可以充當 DNS 的作用,完成域名到主機的映射。
  • 不過需要注意的是,“域名”并不一定必須是類似于“xxx.com”形式的字元串,而可以是任意形式的字元串序列。
  • 例如,在分布式系統中,一些工程是專門為其它工程提供服務的,這些工程稱為“服務提供者”;而一些工程是專門調用其它工程所提供的服務的,這些工程稱為“服務消費者”。為了使消費者與提供者間接解耦,消費者一般都是通過“服務名稱”來消費服務的。那麼,将“服務名稱”與“服務提供者”間建立映射關系的伺服器稱為“服務注冊中心”。這個服務注冊中心所起的作用就是 DNS 的作用——将服務名稱映射為提供者主機。zk 就可以充當服務注冊中心。

基本DNS實作原理

JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景
  • 假設應用程式 app1 與 app2 分别用于提供 service1 與 service2兩種服務,現在要将其注冊到 zk 中。首先需要在 zk 上建立一組節點進行域名配置,例如 /DNS/app1/service1.company.com 與 /DNS/app2/service2.company.com。即為每一個應用程式建立一個節點,然後再以該應用的服務名稱為為名稱再建立子節點,而子節點的資料内容則為該應用程式所部屬的伺服器主機的“主機名+端口号”,若有多個主機上均部屬着同一個應用,即該服務的提供者有多個,那麼這些“主機名+端口号”間用逗号隔開(當然,也可以使用其它分隔方式)。
  • 對于該結構的用法,需要注意一下幾點:
* 若該應用具有多個服務名稱,則可以在該應用節點下添加多個子節點。
* 若某域名下的服務者增加或減少,可直接修改該域名對應節點的資料内容即可。
* 若某域名需要修改,可直接修改該域名對應節點的名稱或再新增一個節點。
* 對于這些節點及節點資料内容的使用,例如多個提供者可以實作負載均衡,都是由程式員編寫的 zk 用戶端程式自己定義的,即這種用戶端代碼需要開發人員自行開發。
           

具有狀态收集功能的DNS實作原理

  • 具有狀态收集功能的DNS的zk節點示意圖:
    JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景
  • 以上模型存在一個問題,如何擷取各個提供者主機的健康狀态、運作狀态呢?可以為每一個域名節點再添加一個狀态子節點,而該節點的資料内容則為開發人員定義好的狀态資料。這些狀态資料是如何h擷取到的呢?是通過狀态收集器(開發人員自行開發的)定期寫入到 zk 的該節點中的。
  • 阿裡的 Dubbo 就是使用 Zookeeper 作為域名伺服器的。

擴充-叢集監控平台

  • 叢集監控平台示意圖:
    JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景

優化設計

  • 前面的設計存在一個問題:當提供者主機當機了,對于狀态聚合系統沒辦法實時感覺到,而且設計比較複雜。
  • 改進後的方案如下:
    JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景

Master選舉

什麼是Master選舉

  • 叢集是分布式系統中不可或卻的組成部分,是為了解決分布式系統中計算單元的單點問題,水準擴充計算單元的處理能力的一種解決方案。
  • 一般情況下,會在群集中選舉出一個 Master,用于協調叢集中的其它 Slave 主機,對于Slave 主機的狀态具有決定權。

廣告推薦系統

需求

  • 系統會根據使用者畫像,将使用者歸結為不同的種類。系統會為不同種類的使用者推薦不同的廣告。每個使用者前端需要從廣告推薦系統中擷取到不同的廣告 ID。

分析

  • 這個向前端提供服務的廣告推薦系統一定是一個叢集,這樣可以更加快速高效的為前端進行響應。
  • 需要注意,推薦系統對于廣告 ID 的計算是一個相對複雜且消耗 CPU 等資源的過程。如果讓叢集中每一台主機都可以執行這個計算邏輯的話,那麼勢必會形成資源浪費,且降低了響應效率。
  • 此時,可以隻讓其中的一台主機去處理計算邏輯,然後将計算的結果寫入到某中間存儲系統中,并通知叢集中的其它主機從該中間存儲系統中共享該計算結果。那麼,這個運作計算邏輯的主機就是 Master,而其它主機則為 Slave。

架構

JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景

Master選舉

  • 使用 DBMS 的主鍵唯一特性可以實作 Master 的選舉。讓所有叢集主機向資料庫某表中插入主鍵相同的記錄,由于 DBMS 具有主鍵沖突檢查功能,是以其隻能有一個主機插入成功,那麼這個成功的主機即為 Master,其它為 Slave。
  • 其存在的弊端是,僅使用 DBMS 的功能無法實作當 Master 當機後對于 Slave 的通知,通知它們進行重新選舉。
這個廣告推薦系統叢集中的 Master 是如何選舉出來的呢?
  • 使用 zk 可以完成。使用 zk 中多個用戶端對同一節點建立時,隻有一個用戶端可以成功的特性實作。
JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景

具體來說,由三步完成:

  • step1:多個用戶端同時發起對同一臨時節點 /master-election/master 進行建立的請求,最終隻能有一個用戶端成功。這個成功的用戶端主機就是 Master,其它用戶端就是 Slave。
  • Step2:讓 Slave 都向這個臨時節點的父節點 /master-election 注冊一個子節點清單的watcher 監聽。
  • Step3:一旦該 Master 當機,臨時節點就會消失,zk 伺服器就會向所有 Slave 發送子節點變更事件,Slave 在接收到事件後會調用相應的回調方法,該回調方法會重新向這個父節點建立相應的臨時子節點。誰建立成功,誰就是新的 Master。

分布式同步

什麼是分布式同步

  • 分布式同步,也稱為分布式協調,是分布式系統中不可缺少的環節,是将不同的分布式元件有機結合起來的關鍵。對于一個在多台機器上運作的應用而言,通常需要一個協調者來控制整個系統的運作流程,例如執行的先後順序,或執行與不執行等。
  • 引入一個協調者,便于将分布式協調的職責從應用中分離出來,進而大大減少了系統間的耦合度,顯著提高系統的可擴充性。

實作原理

使用 zk 實作分布式同步的原理是:不同的用戶端都對 zk 上的同一個 znode 節點進行 watcher 監聽,監聽資料節點本身或其子節點的變化情況。根據不同的變化,用戶端收到不同的 watcher 事件通知,做出不同的處理。

MySQL資料複制總線

下面以“MySQL資料複制總線”為例來分析 zk 的分布式同步服務。

資料複制總線組成

  • MySQL資料複制總線是一個實時資料複制架構,用于在不同的 MySQL 資料庫執行個體間進行異步資料複制。其核心部分由三部分組成:生産者、複制管道、消費者。
    JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景
  • 複制管道重要功能由兩部分構成:複制任務完成者 replicator 與 MySQL中繼資料提供者 metadataServer。
    JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景
  • replicator:實作了資料複制的核心邏輯,從生産者(确切地說是MySQL的Binlog日志)中複制出資料寫入到消費者。
  • metadataServer:存放複制任務所對應的生産者、消費者的中繼資料,例如資料庫名、表名,連接配接資料的使用者名、密碼等資訊。
  • 那麼,MySQL資料複制總線系統中哪裡需要使用 zk 的分布式同步功能呢?這需要了解複制總線系統的整個工作原理。下面就詳細解析其工作過程與原理。

資料複制總線工作原理

JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景
複制任務注冊
  • 在資料複制總線系統啟動時,首先需要在 zk 中建立相應的任務清單節點,例如:/mysql_replicator/tasks。
  • 當 replicator 線程啟動,說明要有複制任務了,其會在任務清單下建立一個任務子節點,例如要完成“支付記錄”複制,則建立/mysql_replicator/tasks/pay_record子節點。若發現該子節點已經存在,則說明已經在replicator注冊了任務,自己就不需要再建立該子節點了。
replicator熱備
  • 為了防止 replicator 在複制過程中出現故障,replicator 采用熱備容災方案,即将同一個複制任務部署到多個不同的主機上,但又使一個處于 RUNNING 狀态,而其他的主機處于 STANDBY 狀态。當 RUNNING 狀态的主機出現故障,無法完成複制任務時,式某一個 STANDBY 狀态主機轉換為 RUNNING 狀态,繼續完成複制任務。
  • 為了實作上述熱備方案,會在相應任務節點下建立多個 replicator 主節點,這些節點屬于臨時順序節點。其節點名稱形式如下: /mysql_replicatir/tasks/pay_record/instances/[hostname]-序号,例如:/mysql_replicatir/tasks/pay_record/instances/host1-0000000001。
  • 在完成該任務下所有 replicator 主機對應的子節點建立後,每個任務節點(如pay_record)均可擷取到該任務的所有 replicator 主機清單,然後将讓序号最小的 replicator 狀态設定為 RUNNING,其它的設定為 STANDBY。當然,這些狀态資訊會寫入到目前任務節點下的 status 子節點的資料内容中,即寫入到 /mysql_replicatir/tasks/pay_record/status 中。
主備切換
  • 當 RUNNING 态的主機出現當機,則該主機對應的子節點馬上就被删除了,然後在目前處于 STANDBY 狀态中的 replicator 中找到序号最小的子節點,然後将其狀态馬上修改為RUNNING,完成“主備切換”。

分布式鎖

排他鎖

  • 排他鎖(eXclusive locks),也稱為 X 鎖、寫鎖。若事務 T 對資料對象 O 加了排他鎖,那麼在整個加鎖期間,隻允許事務 T 對 O 進行讀寫操作,其他任何事務都不能對該資料對象進行任何操作,直到 T 釋放了排他鎖。

共享鎖

  • 共享鎖(Shared locks):又稱為 S 鎖、讀鎖。若事務 T 對資料對象 O 加了共享鎖,那麼該事務隻能對 O 進行讀操作。當然,其他事務同時也可以對資料對象 O 再添加共享鎖,且可以對 O 進行讀操作。隻有當所有共享鎖全部釋放後,才可以對 O 添加排他鎖,即才可以對 O 進行寫操作。

分布式鎖的實作

在 zk 上對于分布式鎖的實作,使用的是類似于“/xs_lock/[hostname]-請求類型-序号”的臨時順序節點。

JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景

具體實作過程如下:

  • 首先用戶端會對 xs_lock 節點注冊子節點清單變更事件的 watcher 事件。
  • 然後,若用戶端需要擷取分布式鎖時,會到 xs_lock 節點下建立一個臨時順序節點。若目前是讀請求,則會建立一個 “hostname-R-序号”的子節點;若目前是寫請求,則會建立一個“hostname-W-序号”的子節點。讀寫操作的順序性就是通過這些子節點的順序性展現的。
  • 在目前子節點建立完後,目前子節點會對比其它子節點序号的大小關系,并根據讀寫操作的不同,執行不同的邏輯。

    讀請求:若沒有比自己序号小的子節點,或所有比自己序号小的子節點都是讀請求,則表明自己可以擷取到共享鎖,即可以開始資料讀取了;若比自己序号小的子節點中有寫請求,則目前用戶端無法擷取到共享鎖,不能對資料進行讀操作,而是進入等待狀态,等待前面寫操作的完成。

    寫請求:若發現自己是序号最小的子節點,則表明目前用戶端可以擷取到排他鎖,即可以開始資料更新了;若發現還有比自己序号更小的子節點,則目前用戶端無法擷取到排他鎖,不能對資料進行寫操作,而是進入到等待狀态,等待前面的操作完成。

  • 存在的問題:前面每一個節點的變化,都會導緻後面所有節點 watcher 事件的回調(下載下傳子節點清單),這樣就會很影響性能。

分布式鎖的改進

  • 前面的實作方式存在驚群效應【由于一個操作而引發了大量的低效或無用的操作的執行,這種情況稱為驚群效應。】,為了解決其所帶來的性能下降,可以對前述分布式鎖的實作進行改進。
  • 在用戶端請求發出後,在 zk 中建立相應的臨時順序節點後馬上擷取目前的 /xs_lock 的所有子節點清單,但任何用戶端都不向 /xs_lock 注冊 watcher,而是改根據請求類型的不同向“對其有影響的”子節點注冊watcher。

    讀請求:若直接擷取到共享鎖,則直接開始讀操作;若不能擷取到共享鎖,則說明其前面有寫請求,其隻需要向序号小于自己的最後一個寫請求節點注冊 watcher,然後等待。

    寫請求:若檢視到自己的序号是最小的節點,則直接擷取到排他鎖,然後開始寫操作;若其不是序号最小的節點,則其隻需向序号小于自己的最後一個節點注冊 watcher,然後等待。

分布式隊列

說到分布式隊列,我們馬上可以想到 RabbitMQ、Kafka等消息中間件産品。zk 也可以實作簡單的消息隊列。

FIFO隊列

JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景
  • zk實作FIFO隊列的思路是:利用順序節點的有序性,為每個資料在 zk 中都建立一個相應的節點。然後為每個節點都注冊 watcher 監聽。一個節點被消費,則會引發消費者消費下一個節點,直到消費完畢。

    具體實作過程如下:

  • 為每一個資料按照其到達的順序為其建立順序子節點,且将資料作為節點的資料内容。
  • 每個子節點在建立時,若其不是序号最小的節點,則需要再向小于其序号的最後一個節點中注冊一個 watcher 監聽。
  • 當應用程式對一個節點的資料内容消費過後,馬上修改該節點的資料内容。當後面節點監聽到其前面節點的資料内容發生了改變,則表示目前節點的資料可以被消費了。

分布式屏障Barrier隊列

  • Barrier,屏障、障礙物。Barrier隊列是分布式系統中的一種同步協調器,規定了一個隊列中的元素必須全部聚集齊後才能繼續執行後面的任務,否則一直等待。其常見于大規模分布式并行計算的應用場景中:最終的合并計算需要基于很多并行計算的子結果來進行。
    JavaEE 企業級分布式進階架構師(八)Zookeeper學習筆記(3)Zookeeper典型應用場景
    zk對于Barrier的實作原理是:
  • 首先要建立一個 /barrier 節點,其資料内容設定為屏障打開的門檻值,即當其下的子節點數量達到該門檻值後,app才可進行最終的計算,否則一直等待。
  • 然後應用程式再在 /barrier 中注冊一個 watcher 監聽,監聽其下的子節點數量變化。
  • 然後就可以開始每一個并行計算了。對于每一個并行計算,每計算出一個子結果,就會在 /barrier 下建立一個子節點,而每增加一個節點,/barrier 就會向app發送一個子節點數量變化的 watcher 事件,通知應用程式擷取 /barrier 子節點清單,以便擷取子節點個數。
  • 應用程式在擷取到子節點個數後,會馬上對比其與門檻值的大小。當子節點個數達到門檻值時,會開啟最終的合并計算,即打開了屏障。

繼續閱讀