1. 程式人生 > >如果有人問你 Dubbo 中註冊中心工作原理,就把這篇文章給他

如果有人問你 Dubbo 中註冊中心工作原理,就把這篇文章給他

註冊中心作用

開篇首先想思考一個問題,沒有註冊中心 Dubbo 還能玩下去嗎?

當然可以,只要知道服務提供者地址相關資訊,消費者配置之後就可以呼叫。如果只有幾個服務,這麼玩當然沒問題。但是生產服務動輒成千上百,如果每個服務都需要手寫配置資訊,想象一下是多麼麻煩。

好吧,如果上面的問題都不是事的話,試想一下如果一個服務提供者在執行過程中宕機,消費者怎麼辦?消費者不知情,所以它還會不斷把請求發往服務提供者,然後不斷失敗。這個時候唯一的辦法就是修改服務地址資訊,然後重啟服務。

可以看到如果沒有註冊中心,分散式環境中服務查詢發現將會非常麻煩,一切需要手工配置,無法完成自動化。所以這裡就需要一個第三者,協調服務提供者與消費者之間關係,這就是註冊中心。

註冊中心主要作用如下:

  1. 動態加入,服務提供者通過註冊中心動態的把自己暴露給消費者,無需消費者逐個更新配置檔案。
  2. 動態發現服務,消費者可以動態發現新的服務,無需重啟生效。
  3. 統一配置,避免本地配置導致每個服務配置不一致。
  4. 動態調整,註冊中心支援引數動態調整,新引數自動更新到所有相關的服務節點。
  5. 統一管理,依靠註冊中心資料,可以統一管理配置服務節點。

註冊中心工作流程

註冊中心工作流程總體比較簡單,流程圖大致如下:

主要工作流程可以分為如下幾步:

  1. 服務提供者啟動之後,會將服務註冊到註冊中心。
  2. 消費者啟動之後主動訂閱註冊中心上提供者服務,從而獲取到當前所有可用服務,同時留下一個回撥函式。
  3. 若服務提供者新增或下線,註冊中心將通過第二步的註冊的回撥函式通知消費者。
  4. dubbo-admin(服務治理中心)將會會訂閱服務提供者以及消費者,從而可以在控制檯管理所有服務提供者以及消費者。

Dubbo 之前版本主要可以使用 ZooKeeper,Redis 作為註冊中心 ,而隨著 Dubbo 版本不斷更新,目前還支援 nacos,consul,etcd 等做為註冊中心。

Dubbo 註冊中心核心原始碼

ps: 以下原始碼基於 dubbo 2.7.3 版本

註冊中心實現使用模板模式,原始碼位於 dubbo-registry 模組,類關係如下圖:

最上層的 RegistryService 介面定義了核心方法,分別為註冊,取消註冊,訂閱,取消訂閱以及查詢。

中間層抽象類主要實現通用邏輯,如:AbstractRegistry 實現快取機制,FailbackRegistry 實現失敗重試功能。

底層 ZookeeperRegistry等為具體實現類,實現與 ZooKeeper 等註冊中心互動的邏輯。

接下去我們具體分析 AbstractRegistryFailbackRegistry 邏輯。

AbstractRegistry 快取實現的原理

如果每次服務呼叫都需要呼叫註冊中心實時查詢可用服務列表,不但會讓註冊中心承受巨大的流量壓力,還會產生額外的網路請求,導致系統性能下降。

其次註冊中心需要非強依賴,其宕機不能影響正常的服務呼叫。

基於以上幾點,註冊中心模組在 AbstractRegistry 類中實現通用的快取機制。這裡的快取可以分為兩類,記憶體服務快取以及磁碟檔案快取。

記憶體服務快取

記憶體服務快取很好理解也最容易實現,AbstractRegistry使用一個 ConcurrentMap儲存相關資訊。

 private final ConcurrentMap<URL, Map<String, List<URL>>> notified = new ConcurrentHashMap<>();

這個集合中 key 為消費者的 URL,而 value 為一個 Map 集合。這個內層 Map 集合使用服務目錄作為 key,分別為 providers,routers,configurators,consumers 四類,value 則是對應服務列表集合。

磁碟檔案快取

由於服務重啟就會導致記憶體快取消失,所以額外增加磁碟檔案快取。

檔案快取預設位置位於 ${user.home}/.dubbo/資料夾,檔名為dubbo-registry-${application.name}-${register_address}.cache。可以設定 dubbo.registry.file 配置資訊從而修改預設配置,實現原始碼如下:

String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getParameter(Constants.APPLICATION_KEY) + "-" + url.getAddress() + ".cache");

ps: ${application.name} 取自 dubbo.application.name 資訊,${register_address} 取值註冊中心地址資訊。快取檔案完整名稱為:C:\Users\xxx/.dubbo/dubbo-registry-dubbo-auto-configure-consumer-sample-127.0.0.1:2181.cache

快取檔案內容使用 properties 配置檔案格式,即 key=value 格式。key為服務介面名稱,value 為服務列表,由於服務可能存在多個,將會使用空格分隔。

快取檔案的載入

dubbo 程式初始化的時候,AbstractRegistry 建構函式將會從本地磁碟檔案中將資料讀取到 Properties 物件例項中,後續都將會先寫入 Properties,最後再將裡面資訊再寫入檔案。

快取初始化的原始碼為下圖。

快取檔案的儲存與更新

快取檔案將會通過 AbstractRegistry#notify 方法儲存或更新。客戶端第一次訂閱服務獲取的全量資料,或者後續回撥中獲取到新資料,都將會呼叫 AbstractRegistry#notify 方法,用來更新記憶體快取以及檔案快取。

notify 方法原始碼如下圖:

在儲存檔案快取方法中,首先把根據 URL 取出的資料,拼接成字串,然後寫入上面提到過的 properties 物件中,最後輸出到檔案中。

這裡可以選擇兩種儲存方式,同步或非同步。由於 notify 可能被多次呼叫,為了提高系統能,系統預設使用非同步方式儲存。

saveProperties 方法原始碼如下:

doSaveProperties 方法最終將會將資訊寫入快取。考慮到儲存方法可能會被多個執行緒同時呼叫,這裡使用 CAS 方法,首先比較版本大小,若小於,代表有新執行緒正在寫入資訊,本次更新直接丟棄。

其次考慮到多個 dubbo 應用可能共用一份快取檔案,所以這裡使用檔案排他鎖當做分散式鎖,防止多個應用併發操作同一份檔案。

一旦檔案寫入異常或者獲取鎖失敗,儲存操作將會不斷重試,直到超過最大次數。

ps: dubbo 2.7.2 之前重試沒有設定最大次數,如果檔案沒有許可權儲存,儲存將會一直失敗,非同步執行緒將會陷入死迴圈。

doSaveProperties 方法原始碼如下:

FailbackRegistry 重試機制

FailbackRegistry 繼承 AbstractRegistry,實現了 registersubscribe等通用法,並增加 doRegisterdoSubscribe 等模板方法,交由子類實現。

如果 doRegister 等模板方法發生異常,會將失敗任務放入集合,然後定時再次呼叫模板方法。

FailbackRegistry 失敗重試集合分別為:

subscribe 方法為例,這裡將會呼叫這些 doSubscribe 的模板方法。如果發生異常將會讀取快取檔案中內容,然後載入服務。最後新建非同步定時任務加入重試集合中,然後由定時器去重試這些任務。

FailbackRegistry#subscribe 方法原始碼:

addFailedSubscribed 中將會新建定時任務,然後交由定時器執行。定時任務預設最大重試次數為 3 次,呼叫時間間隔預設為 5 s。

addFailedSubscribed 原始碼如下:

其他失敗重試任務都比較類似,全都繼承自 AbstractRetryTask 父類,類關係如下圖。

總結

本文主要講述註冊中心作用,工作流程,通用快取機制以及失敗重試機制。從中可以學到模板模式,以及多執行緒併發技巧。

這裡沒有涉及到具體註冊中心實現,由於目前最主要使用 ZooKeeper 作為註冊中心,所以下篇將會聊聊 ZooKeeper 註冊中心原理,敬請期待。

幫助書籍

『深入理解 Apache Dubbo 與實戰』

相關推薦

如果有人 Dubbo 註冊中心工作原理文章

註冊中心作用 開篇首先想思考一個問題,沒有註冊中心 Dubbo 還能玩下去嗎? 當然可以,只要知道服務提供者地址相關資訊,消費者配置之後就可以呼叫。如果只有幾個服務,這麼玩當然沒問題。但是生產服務動輒成千上百,如果每個服務都需要手寫配置資訊,想象一下是多麼麻煩。 好吧,如果上面的問題都不是事的話,試想一下

如果有人ZooKeeper是什麽文章發給

可用性 一半 connect hbase 視圖 因此 Paxos算法 再次 ref 前言 提到ZooKeeper,相信大家都不會陌生。Dubbo,Kafka,Hadoop等等項目裏都能看到它的影子。但是你真的了解 ZooKeeper 嗎?如果面試官讓你給他講講 ZooKee

如果有人ZooKeeper是什麼文章發給

前言 提到ZooKeeper,相信大家都不會陌生。Dubbo,Kafka,Hadoop等等專案裡都能看到它的影子。但是你真的瞭解 ZooKeeper 嗎?如果面試官讓你給他講講 ZooKeeper 是個什麼東西,你能回答到什麼地步呢? 我會用兩個篇幅介紹ZooKeeper ,第一篇是概念性的認識,這篇你會得到

再有人Java內存模型是什麽文章發給

jpg 職業生涯 ron 英文 順序執行 物理地址 直接 順序 新的 前幾天,發了一篇文章,介紹了一下JVM內存結構、Java內存模型以及Java對象模型之間的區別。有很多小夥伴反饋希望可以深入的講解下每個知識點。Java內存模型,是這三個知識點當中最晦澀難懂的一個,而且涉

再有人volatile是什麽文章發給

存在 創建 前言 算法 允許 代碼 current col 是什麽 前言 Java語言為了解決並發編程中存在的原子性、可見性和有序性問題,提供了一系列和並發處理相關的關鍵字,比如synchronized、volatile、final、concurren包等。在前一篇文章中,

再有人Netty是什麼文章發給

前言 本文基於Netty4.1展開介紹相關理論模型,使用場景,基本元件、整體架構,知其然且知其所以然,希望給大家在實際開發實踐、學習開源專案提供參考。 這是一篇萬字長文,建議先收藏,轉發後再看。 Netty簡介 Netty是 一個非同步事件驅動的網路應用程式框架,用於快速開發可維護的高效能協議伺服

再有人Java記憶體模型是什麼文章發給。(轉)

原文連結:再有人問你Java記憶體模型是什麼,就把這篇文章發給他。 前幾天,發了一篇文章,介紹了一下JVM記憶體結構、Java記憶體模型以及Java物件模型之間的區別。有很多小夥伴反饋希望可以深入的講解下每個知識點。Java記憶體模型,是這三個知識點當中最晦澀難懂的一個,而且涉及到很多背

【Redis資料庫】再有人CAP理論是什麼文章發給

CAP是Consistency(一致性),Availability(可用性),Partition tolerance(分割槽容錯性)的縮寫。在學習redis過程中看到這個名詞,查詢各位大佬的文章發現這篇講得很簡單清晰,文章來自公眾號:codewill。 CAP 定理(C

再有人volatile是什麼文章發給

在再有人問你Java記憶體模型是什麼,就把這篇文章發給他中我們曾經介紹過,Java語言為了解決併發程式設計中存在的原子性、可見性和有序性問題,提供了一系列和併發處理相關的關鍵字,比如synchronized、volatile、final、concurren包等。在前一篇文

再有人Java記憶體模型是什麼文章發給

前幾天,發了一篇文章,介紹了一下JVM記憶體結構、Java記憶體模型以及Java物件模型之間的區別。有很多小夥伴反饋希望可以深入的講解下每個知識點。Java記憶體模型,是這三個知識點當中最晦澀難懂的一個,而且涉及到很多背景知識和相關知識。 網上有很多關於Java記憶體模

面試官再 HashMap 底層原理文章

前言 HashMap 原始碼和底層原理在現在面試中是必問的。因此,我們非常有必要搞清楚它的底層實現和思想,才能在面試中對答如流,跟面試官大戰三百回合。文章較長,介紹了很多原理性的問題,希望對你有所幫助~ 目錄 本篇文章主要包括以下內容: HashMap 的儲存結構 常用變數說明,如載入因子等 HashMap

還不會浮點數轉二進位制?下次有人直接文章

> 作為一名程式猿,假如某一天,有一個妹子拿著一個浮點數,求你教她怎麼換算成二進位制,如果你不能單手求出來,你都不能算一個合格的工具人.....好吧,是一個合格的程式猿(狗頭保命)。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200702100810265.

再有人分布式事務

消息 重新 事務所 啟動數據庫 最終一致性 結合 分布式處理 凍結資金 ima 前言 不知道你是否遇到過這樣的情況,去小賣鋪買東西,付了錢,但是店主因為處理了一些其他事,居然忘記你付了錢,又叫你重新付。又或者在網上購物明明已經扣款,但是卻告訴我沒有發生交易。這一系列情況都是

【漫畫】以後在有面試官AVL樹文章

背景 西天取經的路上,一樣上演著程式設計的樂趣..... 1、若它的左子樹不為空,則左子樹上所有的節點值都小於它的根節點值。 2、若它的右子樹不為空,則右子樹上所有的節點值均大於它的根節點值。 3、它的左右子樹也分別可以充當為二叉查詢樹。 例如:

再有人 Java 的註解文章

什麼是註解? 用一個詞就可以描述註解,那就是元資料,即一種描述資料的資料。所以,可以說註解就是原始碼的元資料。比如,下面這段程式碼: @Override public String toString() {     ret

再有人分散式事務

前言不知道你是否遇到過這樣的情況,去小賣鋪買東西,付了錢,但是店主因為處理了一些其他事,居然忘記

面試官編碼相關的面試題完事!

前情回顧:Java中一個字元佔兩位元組 但為什麼new String("字").getBytes().length 返回3個位元組 今天主要聊一聊: 位元組 字元 字符集 編碼 字元編碼 Java 內碼和外碼 Unicode 位元組 例如 :00001111 這個8位二進位制數就佔了一個位元組的儲

再有人註解文章

自Java5.0版本引入註解之後,它就成為了Java平臺中非常重要的一部分。開發過程中,我們也時常在應用程式碼中會看到諸如@Override,@Deprecated這樣的註解。這篇文章中,我將向大家講述到底什麼是註解,為什麼要引入註解,註解是如何工作的,如何編寫自定義的註解(通過例子),什麼

【資料庫】如果有人資料庫的原理文章

一提到關係型資料庫,我禁不住想:有些東西被忽視了。關係型資料庫無處不在,而且種類繁多,從小巧實用的 SQLite 到強大的 Teradata 。但很少有文章講解資料庫是如何工作的。你可以自己谷歌/百度一下『關係型資料庫原理』,看看結果多麼的稀少【譯者注:百度為您找到相關結果約1,850,000個…】

如果有人資料庫的原理文章

一提到關係型資料庫,我禁不住想:有些東西被忽視了。關係型資料庫無處不在,而且種類繁多,從小巧實用的 SQLite 到強大的 Teradata 。但很少有文章講解資料庫是如何工作的。你可以自己谷歌/百度一下『關係型資料庫原理』,看看結果多麼的稀少【譯者注:百度為您找到相關結果約