Dubbo+Zookeeper入門(上篇)

1. 前言

微服務架構做分佈式系統開發時的四大問題:
1 客戶端如何訪問這麼多的服務

  • API、網關

2 服務與服務之間如何通信
同步通信:

  • HTTP(Apache Http Client)
  • RPC(Dubbo,只支持Java)

異步通信:

  • 消息隊列(kafka RabbitMQ RocketMQ)

3 這麼多服務如何管理(管理實質是管理微服務的地址)
服務治理:服務註冊與發現

  • 基於客戶端的服務註冊與發現(Dubbo+Zookeeper)
  • 基於服務端的服務註冊與發現(Spring Cloud中的Eureka)

4 服務掛了怎麼辦

  • 重試機制
  • 服務熔斷
  • 服務降級
  • 服務限流

微服務架構中就需要解決以上四個大問題,這些問題最大根源就是網絡是不可靠的。
常用的有兩套微服務解決方案:

  • Spring Boot+Spring Cloud
    基於HTTP
    使用Eureka(Netflix旗下)完成服務註冊與發現
    組件多,功能完備
  • Spring Boot+Dubbo+Zookeeper(Apache旗下)
    基於RPC的通信框架
    使用Dubbo+Zookeeper完成服務註冊與發現
    組件少,功能沒有第一套方案完備

2. RPC

2.1 概念

RPC(Remote Procedure Call)是指遠程過程調用,是一種進程間通信方式,是一種技術思想,而不是規範。
它允許程序調用另一個地址空間(通常是共享網絡的另一臺機器)的過程或函數,而不是程序員顯示編碼這個遠程調用的細節。即程序員無論是調用本地的還是遠程的函數,本質上編寫的調用代碼基本相同。

分佈式服務框架Dubbo和微服務架構如SpringCloud中的各個服務之間的通信區別:Dubbo基於的RPC協議通信,而微服務Cloud中採用的是基於HTTP的RESTful API。

2.2 RPC基本原理

假設A服務器上的程序要調用B服務器上的程序,A、B服務器上執行的代碼如下,A服務器調用B服務器上的hi()方法,hi()方法返回一個字符串給A服務器:
客戶端程序A:

hello(){
   String msg = B.hi(new User(“張三”));
   System.out.println(msg);
}

服務器端程序B:

String hi(user){
   return “你好:+user.getName();
}

下圖展示了RPC的基本原理。A服務器和B服務器分別使用了RPC框架,當它發現A服務器調用B服務器的接口時,便和B服務器建立起連接。並且客戶端需要攜帶參數,便對參數進行序列化處理。
在這裏插入圖片描述
可以看出RPC的兩個核心模塊就是:通訊(通過網絡通信傳遞數據)和序列化(對參數和結果進行序列化和反序列化)。
RPC框架有很多,如dubbo、gRPC、THrift、HSF(High Speed Service Framework)

下面再以一張分佈式服務架構簡圖進一步瞭解RPC:
在這裏插入圖片描述
在分佈式服務框架中,各個服務之間會(後臺服務之間,前臺和後臺服務之間)相互調用接口,這些調用接口的管理交給RPC框架處理。

3. Zookeeper

Zookeeper是一個分佈式協調服務。分佈式協調服務主要用來解決分佈式環境中的多個進程之間的同步控制,讓它們有序的訪問某種臨界資源,防止造成“髒數據”的後果。
Dubbo官方推薦使用Zookeeper作爲註冊中心,註冊中心負責服務地址的註冊與查找,相當於目錄服務,服務提供者和消費者只在啓動時與註冊中心交互,註冊中心不轉發請求,壓力較小。

下面通過畫圖來幫助理解這個概念,多個訂單服務部署在多個服務器上(多個進程),當有15個併發到達Nginx服務器時,Nginx轉發到訂單服務上,三個訂單服務分別分到5個請求,去訪問商品服務,商品只剩下5個(臨界資源),如果三個訂單服務沒有組織管理地去訪問商品服務,則可能會出現扣減的商品庫存大於5,這就是造成了“髒數據”。

這種問題不會存在於單體應用中,因爲單體應用一個JVM是一個進程,一個進程裏有多個線程,單體應用下,多線程會導致臨界資源訪問問題,可以通過同步代碼的方式保證資源的有序訪問。

在這裏插入圖片描述
分佈式協調服務Zookeeper就是爲了解決這一問題。協調的本質爲“分佈式鎖”,當第一個服務請求到臨界資源的時候,就給臨界資源上鎖,其他服務就無法再請求得到臨界資源。

3.1 Zookeeper的數據模型

Zookeeper的數據模型像樹結構,也像文件系統目錄。樹由節點組成,Zookeeper的數據存儲也同樣是基於節點,這種節點稱爲Znode。
但是不同於樹的節點,Zonde的引用方式是路徑引用,類似於文件路徑:/用戶服務/用戶登錄/訂單服務/處理訂單
在這裏插入圖片描述

每個Znode節點存儲的數據不能超過1MB

Zonde節點包含的元素

  • data:存儲數據信息(比如服務ip、服務名稱)
  • ACL:記錄Znode的訪問權限,即哪些用戶或哪些IP可以訪問本節點
  • child:記錄子節點的地址
  • stat:包含Zonde的各種元數據,比如事務ID、版本號、時間戳、大小等等

3.2 Zookeeper的事件通知

當Znode發生改變,也就是調用了create(創建節點)、delete(刪除節點)、setData(設置節點數據)方法的時候,將會觸發Znode上註冊的對應事件,請求Watch的客戶端會接收到異步通知。

具體交互過程如下:
在這裏插入圖片描述

  • 客戶端調用 getData 方法,watch 參數是 true。服務端接到請求,返回節點數據,並且在對應的哈希表裏插入被 Watch 的 Znode 路徑,以及 Watcher 列表。
  • 當被 Watch 的 Znode 已刪除,服務端會查找哈希表,找到該 Znode 對應的所有 Watcher,異步通知客戶端,並且刪除哈希表中對應的 Key-Value。

3.3 服務註冊與發現

前言中提到常用的有兩套微服務解決方案,解決了服務註冊與發現,下面通過一張圖來理解服務註冊與發現的概念:
在這裏插入圖片描述

  • 開啓Zookeeper服務註冊與發現中心
  • 各個微服務向服務中心註冊信息(ip、服務名稱等)
  • 用戶請求訂單服務,並設置watch爲true,假設此時返回給服務註冊與發現服務的微服務ip是192.168.0.1
  • API Geteway(API網關)中由zkClient(Zookeeper客戶端)維護了一個列表,列表中存儲了訂單服務服務的地址爲192.168.0.1,當用戶下次再請求訂單服務時,就可以直接拿到這個ip
  • 如果192.168.0.1宕機了,Zookeeper的服務發現功能能夠發送異步通知給API網關通知該192.168.0.1服務已下線,然後zkClient從列表中刪除這個服務ip
  • 當用戶再次訪問訂單服務時,返回的服務ip就是192.168.0.4或者192.168.0.5

事件通知機制就是幫助Zookeeper完成服務註冊與發現

3.4 Zookeeper一致性問題

如果只部署一臺Zookeeper服務中心(單機),那麼Zookeeper服務中心掛掉之後,整個服務就無法使用了。這時候就可以部署多個服務中心(集羣),微服務可以連接任意一個服務中心,那麼新的問題又產生了,就是數據如何同步。
Zookeeper服務是一主多從結構,在更新數據時,首先更新到Zookeeper服務集羣的主節點(Leader),再同步到從節點(Follower)。讀取數據時,直接讀取任意節點。【讀取分離】。
當主節點崩潰時,會從從節點中重新選舉新的主節點。

數據寫入流程保證數據一致性:

  • 客戶端發出寫入數據請求給任意Follower
  • Follower把寫入數據請求轉發給Leader
  • Leader採用二階段提交方式,先發送Propose廣播給Follower
  • Follower接收到Propose消息,寫入日誌成功後,返回ACK消息給Leader
  • Leader接到半數以上ACK消息,返回成功給客戶端,並且廣播Commit請求給Follower

3.5 Zookeeper的基本使用

Zookeeper是一個服務,需要被單獨啓動,有服務端和客戶端,客戶端可用命令行工具,Zookeeper的安裝和基本使用見下面的Dubbo環境搭建小節。

4. 分佈式鎖

爲了防止分佈式系統中的多個進程之間相互干擾,我們需要一種分佈式協調技術來對這些進程進行調度,而這個分佈式協調技術的核心就是來實現這個分佈式鎖。

4.1 分佈式鎖應該具備的條件

  • 在分佈式系統環境下,一個方法在同一時間只能被一個機器的一個線程執行
  • 高可用的獲取鎖與釋放鎖
    保證鎖能正常獲取和釋放(比如獲取到鎖的程序在未釋放鎖之前就掛掉,導致鎖不能正常釋放,其他程序就一直無法獲取鎖)
  • 高性能的獲取鎖和釋放鎖
    獲取鎖和釋放鎖的時間快
  • 具備可重入特性
    可由多個任務併發使用,不會造成數據錯誤
  • 具備鎖失效機制,防止死鎖
  • 具備非阻塞鎖特性,即沒有獲取到鎖將直接返回獲取鎖失效

4.2 現有分佈式鎖的實現

  • Memcached
    利用Memcache的add命令,此命令是原子性操作,只有在key不存在的情況下,才能add成功
  • Redis
    利用Redis的setnx命令,此命令是原子性操作,只有在key不存在的情況下,才能set成功
  • Zookeeper
    利用Zookeeper的順序臨時節點,來實現分佈式鎖和等待隊列。Zookeeper的設計初衷就是爲了實現分佈式鎖服務。
  • Chubby
    利用了Paxos一致性算法

4.3 分佈式鎖的三大問題及解決

1 非原子性操作
設置鎖的鍵值對和鍵值對的過期時間必須是一個原子操作,利用redis的set(key, value, expire)保證原子操作
2 誤刪鎖

  • 假設JVM1設置鎖的過期時間爲30s
  • JVM1執行程序的時間超過了30s,這時JVM2便可獲得鎖
  • JVM1執行完程序後去刪除鎖,這時候刪除的實際上是JVM2的鎖

解決:
刪除鎖之前先判斷是不是自己線程設置的鎖
3 鎖過期時間小於程序執行時間
也就是說程序還未執行完成,鎖就過期了。
解決:利用守護線程,當檢測到程序持有的鎖塊過期了,但是程序還未執行完,則增加鎖的過期時間

4.4 Zookeeper如何實現分佈式鎖

利用Zookeeper的順序臨時節點,來實現分佈式鎖和等待隊列。Zookeeper 的數據存儲結構就像一棵樹,這棵樹由節點組成,這種節點叫做 Znode。

Znode 分爲四種類型

  • 持久節點:默認的節點類型。創建節點的客戶端與 Zookeeper 斷開連接後,該節點依舊存在。
  • 持久順序節點:順序節點,就是在創建節點時,Zookeeper 根據創建的時間順序給該節點名稱進行編號。
  • 臨時節點:當創建節點的客戶端與 Zookeeper 斷開連接後,臨時節點會被刪除。
  • 臨時順序節點::在創建節點時,Zookeeper 根據創建的時間順序給該節點名稱進行編號;當創建節點的客戶端與 Zookeeper 斷開連接後,臨時節點會被刪除。

Zookeeper應用了臨時順序節點實現分佈式鎖,步驟如下:

  • 獲取鎖:在 Zookeeper 當中創建一個持久節點 ParentLock。當第一個客戶端想要獲得鎖時,需要在 ParentLock 這個節點下面創建一個臨時順序節點 Lock1。之後,客戶端遍歷ParentLock 下的所有鎖,判斷自己的鎖是不是最靠前的(鎖編號最小),如果是,則獲取鎖成功,否則向它的前一個鎖註冊Watcher,用於監聽前一個鎖是否存在,直到前一個鎖釋放之後(由於是臨時順序節點,因此鎖釋放之後,鎖就刪除了),該監聽該鎖的客戶端獲取鎖成功。
  • 在這裏插入圖片描述
    在這裏插入圖片描述
  • 釋放鎖,釋放鎖分兩種情況:任務完成,客戶端顯示釋放;執行任務過程中,客戶端崩潰。這兩種情況都會導致相關聯的鎖刪除。

4.5 Zookeeper和Redis分佈式鎖的比較

Zookeeper分佈式鎖的優點:

  • 有封裝好的框架,容易實現
  • 有等待鎖的隊列,大大提升搶鎖效率。因爲Zookeeper中利用順序鎖,後一個鎖的客戶端監聽前一個客戶端的鎖即可,監聽的鎖一旦釋放,則該客戶端即可獲得鎖。而Redis中沒有等待鎖的隊列,假設此時創建了三個鎖,其中一個客戶端獲取鎖成功,則另外兩個客戶端要不斷地去競爭鎖,沒有向Zookeeper一樣的Watcher機制和隊列機制。

Zookeeper的缺點:

  • 添加和刪除節點性能較低

Redis分佈式鎖的優點:

  • Set和Del指令的性能較高

Redis分佈式鎖的缺點:

  • 實現複雜,需要考慮超時、原子性、誤刪等情況
  • 沒有等待鎖的隊列,只能在客戶端自旋來等鎖(沒有獲取到鎖的客戶端都不停地去看有沒有可用鎖),效率低下

5. Dubbo

5.1 簡介

Dubbo是一款高性能、輕量級的開源Java RPC分佈式服務框架,特性如下【來自dubbo官網】:

  • 面向接口代理的高性能RPC調用
    即當A服務器調用B服務器中的方法時,A服務把B服務的方法接口拿過來,A服務只需要調用接口的方法即可,Dubbo會自動去B服務器上找到相應的方法並執行,爲開發者屏蔽了遠程調用的底層細節。
  • 智能負載均衡
    比如當“用戶業務”服務器訪問量很大,增加“用戶業務”服務器後,Dubbo管理具體調用哪臺服務器。
  • 自動服務註冊和發現
    Dubbo支持多個服務註冊中心(其中一個就是Zookeeper),可以立即在線/離線檢測服務。
  • 高擴展性
    Dubbo的微內核和插件設計確保了它可以通過第三方實現輕鬆地跨核心功能(如協議、傳輸和序列化)進行擴展。
  • 運行時流量路由
    Dubbo可以在運行時進行配置,以便流量可以根據不同的規則進行路由,這使得它很容易支持如灰度發佈、數據中心感知路由等功能。
  • 可視化服務治理
    Dubbo爲服務治理和維護提供了豐富的工具,如查詢服務元數據、運行狀況和統計信息

簡單來說,Dubbo就是一個遠程服務調用的分佈式框架,在分佈式系統中,Dubbo提供了RPC遠程服務調用方案,以及SOA服務治理方案。

5.2 Dubbo核心功能

  • Remoting:遠程通訊,提供對多種 NIO(Dubbo使用了著名的通信框架:Netty) 框架抽象封裝,包括“同步轉異步”和“請求-響應”模式的信息交換方式。
    通訊模型BIO:同步並阻塞;NID:異步並阻塞;AIO:異步非阻塞
  • Cluster:服務框架,提供基於接口方法的透明遠程過程調用,包括多協議支持,以及軟負載均衡,失敗容錯,地址路由,動態配置等集羣支持。
  • Registry:服務註冊中心,服務自動發現: 基於註冊中心目錄服務,使服務消費方能動態的查找服務提供方,使地址透明,使服務提供方可以平滑增加或減少機器。

5.3 Dubbo組件角色

在這裏插入圖片描述
Dubbo的五個角色

  • Provider:暴露服務的服務提供方
  • Consumer:調用遠程服務的服務消費方
  • Registry:服務註冊與發現的註冊中心(是一個軟件系統,一般使用Zookeeper)
  • Monitor:統計服務的調用次數和調用時間的監控中心
  • Container:服務運行容器

上圖中的調用關係說明

  • 服務容器Container:負責啓動、加載、運行服務提供者
  • 服務提供者Provider:在啓動時,向註冊中心註冊自己提供的服務
  • 服務消費者Consumer:在啓動時,向註冊中心訂閱自己所需的服務
  • 註冊中心Registry:返回服務提供者地址列表給消費者,如果有變更,註冊中心將基於長連接推送變更數據給消費者
  • 服務消費者Consumer:從提供者地址列表中,基於軟負載均衡算法,選擇一臺服務提供者進行調用,如果調用失敗,再選擇另一臺調用
  • 服務消費者Consumer和提供者Provider:在內存中累計調用次數和調用時間,定時分鐘發送一次統計數據到監控中心Monitor

總結圖:
在這裏插入圖片描述

5.4 Dubbo環境搭建

【參考官方文檔:http://dubbo.apache.org/zh-cn/docs/user/quick-start.html】
1 配置註冊中心使用官方推薦的zookeeper

  • 到zookeeper官網下載,我這裏下載的版本是3.4.11
  • zookeeper-3.4.11\conf下的zoo_sample.cfg文件複製一份改名爲zoo.cfg
  • 新建文件夾zookeeper-3.4.11\data
  • 修改zoo.cfg配置項 dataDir=../data
  • 進入zookeeper-3.4.11\bin目錄,在Windows上的命令窗口中運行zkServer.cmd打開註冊中心服務
  • 運行zkCli.cmd進入zookeeper客戶端管理終端
    在這裏插入圖片描述

2 安裝dubbo的管理控制檯
dubbo的管理控制檯提供了可視化web界面供我們管理各個服務

  • 到GitHub上下載安裝包(https://github.com/locationbai/incubator-dubbo-ops-master)
  • 打開incubator-dubbo-ops-master\dubbo-admin\src\main\resources\application.properties文件,修改配置項 dubbo.registry.address=zookeeper://127.0.0.1:2181
  • 保證maven環境搭配好
  • 在incubator-dubbo-ops-master\dubbo-admin\下打開命令窗口,運行命令mvn clean package,意思是將dubbo-admin程序打成一個jar包(application.properties默認配置是jar包)
    在這裏插入圖片描述
  • 將incubator-dubbo-ops-master\dubbo-admin\target下的dubbo-admin-0.0.1-SNAPSHOT.jar文件包放到incubator-dubbo-ops-master的同級目錄下
  • 在命令終端中執行java -jar dubbo-admin-0.0.1-SNAPSHOT.jar,即以SpringBoot的方式啓動dubbo-admin程序(保證在第一步zookeeper註冊中心服務已開啓)
    在這裏插入圖片描述
  • 瀏覽器方法http://localhost:7001/,用戶名和密碼都是root在這裏插入圖片描述

3 配置控制中心

  • incubator-dubbo-ops-master\dubbo-monitor-simple目錄下打開控制檯,執行命令mvn package
  • 將dubbo-monitor-simple-2.0.0-assembly.tar.gz文件放到incubator-dubbo-ops-master的同級目錄下並解壓
  • 打開dubbo-monitor-simple-2.0.0\conf\dubbo.properties做如下配置:
dubbo.container=log4j,spring,registry,jetty-monitor
dubbo.application.name=simple-monitor
dubbo.application.owner=dubbo
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.protocol.port=7070
dubbo.jetty.port=8080
dubbo.jetty.directory=${user.home}/monitor
dubbo.charts.directory=${user.home}/monitor/charts
dubbo.statistics.directory=${user.home}/monitor/statistics
dubbo.log4j.file=logs/dubbo-monitor-simple.log
dubbo.log4j.level=WARN
  • 雙擊dubbo-monitor-simple-2.0.0\assembly.bin\start.bat
    在這裏插入圖片描述
  • 瀏覽器訪問http://localhost:8080
    在這裏插入圖片描述

5.5 在SSM中使用Dubbo

下面模擬一個用戶-訂單地址管理服務

  • user-service-provider是生產者服務,提供查詢用戶訂單的服務
  • order-service-consumer消費者服務,調用生產者服務中的接口
  • gmall-interface是將生產者服務和消費者服務中的接口提取出來,接口的具體實現在user-service-provider或order-service-consumer中
  • provider.xml中配置dubbo服務中心的相關信息(服務中心的ip和端口,申明暴露的服務接口等)
  • consumer.xml中配置dubbo消費者相關信息(申明需要調用的遠程服務的接口等)

在這裏插入圖片描述

源碼
鏈接:https://pan.baidu.com/s/1TRyjIVErV1GwTtYyza2fvQ
提取碼:0lsd

5.6 在Spring Boot中使用Dubbo

在這裏插入圖片描述
hello-dubbo-service-user-api爲服務暴露的接口
hello-dubbo-service-user-provider爲服務提供者
hello-dubbo-service-user-consumer爲服務消費者

使用:

  • 啓動dubbo
  • 啓動hello-dubbo-service-user-provider
  • 啓動hello-dubbo-service-user-consumer
  • 瀏覽器訪問:http://localhost:9000/hi
  • 使用incubator-dubbo-ops管理控制檯查看:
    在這裏插入圖片描述

源碼:
鏈接:https://pan.baidu.com/s/1ZIYVJKY-5PjGbH3ZwjF8zA
提取碼:bafg

6 附錄

參考:
https://www.funtl.com/zh/guide/Apache-Dubbo.html
http://dubbo.apache.org/en-us/

《Dubbo入門(中篇)》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章