?【Alibaba微服務技術系列】「Dubbo3.0技術專題」回顧Dubbo2.x的技術原理和功能實現及原始碼分析(溫故而知新)

李浩宇Alex發表於2021-08-31

RPC服務

什麼叫RPC?

RPC【Remote Procedure Call】是指遠端過程呼叫,是一種程式間通訊方式,他是一種技術的思想,而不是規範。它允許程式呼叫另一個地址空間(通常是共享網路的另一臺機器上)的過程或函式,而不用程式設計師顯式編碼這個遠端呼叫的細節。即程式設計師無論是呼叫本地的還是遠端的函式,本質上編寫的呼叫程式碼基本相同。a

RPC基本原理,RPC兩個核心模組:通訊,序列化

一次完整的RPC呼叫流程(同步呼叫,非同步另說)如下:

  • 1)服務消費方(client)呼叫以本地呼叫方式呼叫服務;

  • 2)client stub接收到呼叫後負責將方法、引數等組裝成能夠進行網路傳輸的訊息體;

  • 3)client stub找到服務地址,並將訊息傳送到服務端;

  • 4)server stub收到訊息後進行解碼;

  • 5)server stub根據解碼結果呼叫本地的服務;

  • 6)本地服務執行並將結果返回給server stub;

  • 7)server stub將返回結果打包成訊息併傳送至消費方;

  • 8)client stub接收到訊息,並進行解碼;

  • 9)服務消費方得到最終結果。

RPC框架的目標就是要2~8這些步驟都封裝起來,這些細節對使用者來說是透明的,不可見的

netty通訊原理

Netty是一個非同步事件驅動的網路應用程式框架, 用於快速開發可維護的高效能協議伺服器和客戶端。它極大地簡化並簡化了TCP和UDP套接字伺服器等網路程式設計。

BIO:(Blocking IO)

NIO (Non-Blocking IO)

  • Selector 一般稱 為選擇器 ,也可以翻譯為 多路複用器,

  • Connect(連線就緒)、Accept(接受就緒)、Read(讀就緒)、Write(寫就緒)

Netty基本原理:

Dubbo

基本簡介

Apache Dubbo (incubating) 是一款高效能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向介面的遠端方法呼叫,智慧容錯和負載均衡,以及服務自動註冊和發現。官網:http://dubbo.apache.org/

框架設計

  • config 配置層:對外配置介面,以 ServiceConfig, ReferenceConfig 為中心,可以直接初始化配置類,也可以通過 spring 解析配置生成配置類
  • proxy 服務代理層:服務介面透明代理,生成服務的客戶端 Stub 和伺服器端 Skeleton, 以 ServiceProxy 為中心,擴充套件介面為 ProxyFactory.
  • registry 註冊中心層:封裝服務地址的註冊與發現,以服務 URL 為中心,擴充套件介面為 RegistryFactory, Registry, RegistryService
  • cluster 路由層:封裝多個提供者的路由及負載均衡,並橋接註冊中心,以 Invoker 為中心,擴充套件介面為 Cluster, Directory, Router, LoadBalance
  • monitor 監控層:RPC 呼叫次數和呼叫時間監控,以 Statistics 為中心,擴充套件介面為 MonitorFactory, Monitor, MonitorService
  • protocol 遠端呼叫層:封裝 RPC 呼叫,以 Invocation, Result 為中心,擴充套件介面為 Protocol, Invoker, Exporter
  • exchange 資訊交換層:封裝請求響應模式,同步轉非同步,以 Request, Response 為中心,擴充套件介面為 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
  • transport 網路傳輸層:抽象 mina 和 netty 為統一介面,以 Message 為中心,擴充套件介面為 Channel, Transporter, Client, Server, Codec
  • serialize 資料序列化層:可複用的一些工具,擴充套件介面為 Serialization, ObjectInput, ObjectOutput, ThreadPool

啟動解析、載入配置資訊

服務暴露

服務引用

基本組成

  • 服務提供者(Provider):暴露服務的服務提供方,服務提供者在啟動時,向註冊中心註冊自己提供的服務。
  • 服務消費者(Consumer): 呼叫遠端服務的服務消費方,服務消費者在啟動時,向註冊中心訂閱自己所需的服務,服務消費者,從提供者地址列表中,基於軟負載均衡演算法,選一臺提供者進行呼叫,如果呼叫失敗,再選另一臺呼叫。
  • 註冊中心(Registry):註冊中心返回服務提供者地址列表給消費者,如果有變更,註冊中心將基於長連線推送變更資料給消費者
  • 監控中心(Monitor):服務消費者和提供者,在記憶體中累計呼叫次數和呼叫時間,定時每分鐘傳送一次統計資料到監控中心

呼叫關係說明

  • 服務容器負責啟動,載入,執行服務提供者。
  • 服務提供者在啟動時,向註冊中心註冊自己提供的服務。
  • 服務消費者在啟動時,向註冊中心訂閱自己所需的服務。
  • 註冊中心返回服務提供者地址列表給消費者,如果有變更,註冊中心將基於長連線推送變更資料給消費者。
  • 服務消費者,從提供者地址列表中,基於軟負載均衡演算法,選一臺提供者進行呼叫,如果呼叫失敗,再選另一臺呼叫。
  • 服務消費者和提供者,在記憶體中累計呼叫次數和呼叫時間,定時每分鐘傳送一次統計資料到監控中心。

環境搭建

windows-安裝zookeeper
  • 下載zookeeper 網址 https://archive.apache.org/dist/zookeeper/zookeeper-3.4.13/
  • 解壓zookeeper 解壓執行zkServer.cmd ,初次執行會報錯,沒有zoo.cfg配置檔案
  • 修改zoo.cfg配置檔案 將conf下的zoo_sample.cfg複製一份改名為zoo.cfg即可。 注意幾個重要位置: dataDir=./ 臨時資料儲存的目錄(可寫相對路徑)clientPort=2181 zookeeper的埠號 修改完成後再次啟動zookeeper
  • 使用zkCli.cmd測試 ls /:列出zookeeper根下儲存的所有節點 create –e /atguigu 123:建立一個atguigu節點,值為123 get /atguigu:獲取/atguigu節點的值
windows-安裝dubbo-admin管理控制檯
  • dubbo本身並不是一個服務軟體。它其實就是一個jar包能夠幫你的java程式連線到zookeeper,並利用zookeeper消費、提供服務。所以你不用在Linux上啟動什麼dubbo服務。

  • 但是為了讓使用者更好的管理監控眾多的dubbo服務,官方提供了一個視覺化的監控程式,不過這個監控即使不裝也不影響使用。

  • 下載dubbo-admin https://github.com/apache/incubator-dubbo-ops

  • 進入目錄,修改dubbo-admin配置 修改 src\main\resources\application.properties 指定zookeeper地址

  • 打包dubbo-admin mvn clean package -Dmaven.test.skip=true

  • 執行dubbo-admin java -jar dubbo-admin-0.0.1-SNAPSHOT.jar 注意:【有可能控制檯看著啟動了,但是網頁打不開,需要在控制檯按下ctrl+c即可預設使用root/root 登陸

Dubbo配置

配置原則

  • JVM 啟動 -D 引數優先,這樣可以使使用者在部署和啟動時進行引數重寫,比如在啟動時需改變協議的埠。

  • XML 次之,如果在 XML 中有配置,則 dubbo.properties 中的相應配置項無效。

  • Properties 最後,相當於預設值,只有 XML 沒有配置時,dubbo.properties 的相應配置項才會生效,通常用於共享公共配置,比如應用名。

重試次數

  • 失敗自動切換,當出現失敗,重試其它伺服器,但重試會帶來更長延遲。可通過 retries="2" 來設定重試次數(不含第一次)。

重試次數配置如下:

<dubbo:service retries="2" />  或  <dubbo:reference retries="2" />  或  <dubbo:reference>    <dubbo:method  name="findFoo" retries="2" />  </dubbo:reference>

超時時間

由於網路或服務端不可靠,會導致呼叫出現一種不確定的中間狀態(超時)。為了避免超時導致客戶端資源(執行緒)掛起耗盡,必須設定超時時間。

Dubbo消費端

全域性超時配置

<dubbo:consumer timeout="5000" />     指定介面以及特定方法超時配置  
<dubbo:reference  interface="com.foo.BarService" timeout="2000">      
    <dubbo:method name="sayHello" timeout="3000"  />  
</dubbo:reference>

Dubbo服務端

全域性超時配置

<dubbo:provider timeout="5000" />     指定介面以及特定方法超時配置  
<dubbo:provider  interface="com.foo.BarService" timeout="2000">      
    <dubbo:method name="sayHello" timeout="3000"  />  
</dubbo:provider>  

配置原則

dubbo推薦在Provider上儘量多配置Consumer端屬性

  1. 作服務的提供者,比服務使用方更清楚服務效能引數,如呼叫的超時時間,合理的重試次數,等等

  2. 在Provider配置後,Consumer不配置則會使用Provider的配置值,即Provider配置可以作為Consumer的預設值。否則,Consumer會使用Consumer端的全域性設定,這對於Provider不可控的,並且往往是不合理的

配置的覆蓋規則:

  • 方法級配置別優於介面級別,即小Scope優先
  • Consumer端配置 優於 Provider配置 優於 全域性配置,
  • 最後是Dubbo Hard Code的配置值(見配置文件)

版本號

當一個介面實現,出現不相容升級時,可以用版本號過渡,版本號不同的服務相互間不引用。

  • 可以按照以下的步驟進行版本遷移:

  • 在低壓力時間段,先升級一半提供者為新版本

  • 再將所有消費者升級為新版本

  • 然後將剩下的一半提供者升級為新版本

老版本服務提供者配置:

<dubbo:service interface="com.foo.BarService" version="1.0.0" />

新版本服務提供者配置:

<dubbo:service interface="com.foo.BarService" version="2.0.0" />

老版本服務消費者配置:

<dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />

新版本服務消費者配置:

<dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />

如果不需要區分版本,可以按照以下的方式配置:

<dubbo:reference id="barService" interface="com.foo.BarService" version="*" />

高可用

zookeeper當機與dubbo直連

  • 現象:zookeeper註冊中心當機,還可以消費dubbo暴露的服務。
  • 原因: 健壯性

  • 監控中心宕掉不影響使用,只是丟失部分取樣資料
  • 資料庫宕掉後,註冊中心仍能通過快取提供服務列表查詢,但不能註冊新服務
  • 註冊中心對等叢集,任意一臺宕掉後,將自動切換到另一臺
  • 註冊中心全部宕掉後,服務提供者和服務消費者仍能通過本地快取通訊
  • 服務提供者無狀態,任意一臺宕掉後,不影響使用
  • 服務提供者全部宕掉後,服務消費者應用將無法使用,並無限次重連等待服務提供者恢復

叢集下dubbo負載均衡配置

在叢集負載均衡時,Dubbo 提供了多種均衡策略,預設為random隨機呼叫。

負載均衡策略

Random LoadBalance

隨機,按權重設定隨機概率:在一個截面上碰撞的概率高,但呼叫量越大分佈越均勻,而且按概率使用權重後也比較均勻,有利於動態調整提供者權重。

RoundRobin LoadBalance

輪循,按公約後的權重設定輪循比率: 存在慢的提供者累積請求的問題,比如:第二臺機器很慢,但沒掛,當請求調到第二臺時就卡在那,久而久之,所有請求都卡在調到第二臺上。

LeastActive LoadBalance

最少活躍呼叫數,相同活躍數的隨機,活躍數指呼叫前後計數差,使慢的提供者收到更少請求,因為越慢的提供者的呼叫前後計數差會越大。

ConsistentHash LoadBalance

一致性 Hash,相同引數的請求總是發到同一提供者。

當某一臺提供者掛時,原本發往該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引起劇烈變動。演算法參見:http://en.wikipedia.org/wiki/Consistent_hashing

預設只對第一個引數 Hash,如果要修改,請配置<dubbo:parameter key="hash.arguments" value="0,1" />
預設用 160 份虛擬節點,如果要修改,請配置 <dubbo:parameter key="hash.nodes" value="320" />

整合hystrix,服務熔斷與降級處理

服務降級

什麼是服務降級?

當伺服器壓力劇增的情況下,根據實際業務情況及流量,對一些服務和頁面有策略的不處理或換種簡單的方式處理,從而釋放伺服器資源以保證核心交易正常運作或高效運作。

可以通過服務降級功能臨時遮蔽某個出錯的非關鍵服務,並定義降級後的返回策略。

向註冊中心寫入動態配置覆蓋規則:
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));
registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));
其中:
  • mock=force:return+null 表示消費方對該服務的方法呼叫都直接返回 null 值,不發起遠端呼叫。用來遮蔽不重要服務不可用時對呼叫方的影響。

  • 還可以改為 mock=fail:return+null 表示消費方對該服務的方法呼叫在失敗後,再返回 null 值,不拋異常。用來容忍不重要服務不穩定時對呼叫方的影響。

叢集容錯

在叢集呼叫失敗時,Dubbo 提供了多種容錯方案,預設為 failover 重試。

叢集容錯模式

Failover Cluster

失敗自動切換,當出現失敗,重試其它伺服器。通常用於讀操作,但重試會帶來更長延遲。可通過 retries="2" 來設定重試次數(不含第一次)。

重試次數配置如下:
<dubbo:service retries="2" />
或
<dubbo:reference retries="2" />
或
<dubbo:reference>
  <dubbo:method name="findFoo" retries="2" />
</dubbo:reference>
Failfast fast

快速失敗,只發起一次呼叫,失敗立即報錯。通常用於非冪等性的寫操作,比如新增記錄。

Failsafe Safe

失敗安全,出現異常時,直接忽略。通常用於寫入審計日誌等操作。

Failback over

失敗自動恢復,後臺記錄失敗請求,定時重發。通常用於訊息通知操作。

Forking Cluster

並行呼叫多個伺服器,只要一個成功即返回。通常用於實時性要求較高的讀操作,但需要浪費更多服務資源。可通過 forks="2" 來設定最大並行數。

Broadcast Cluster

廣播呼叫所有提供者,逐個呼叫,任意一臺報錯則報錯 [2]。通常用於通知所有提供者更新快取或日誌等本地資源資訊。

叢集模式配置

按照以下示例在服務提供方和消費方配置叢集模式

<dubbo:service cluster="failsafe" />
或
<dubbo:reference cluster="failsafe" />

整合hystrix

Hystrix 旨在通過控制那些訪問遠端系統、服務和第三方庫的節點,從而對延遲和故障提供更強大的容錯能力。Hystrix具備擁有回退機制和斷路器功能的執行緒和訊號隔離,請求快取和請求打包,以及監控和配置等功能

置spring-cloud-starter-netflix-hystrix

spring boot官方提供了對hystrix的整合,直接在pom.xml里加入依賴:

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
   <version>1.4.4.RELEASE</version>
</dependency>

然後在Application類上增加@EnableHystrix來啟用hystrix starter:

@SpringBootApplication
@EnableHystrix
public class ProviderApplication {

配置Provider端

在Dubbo的Provider上增加@HystrixCommand配置,這樣子呼叫就會經過Hystrix代理。

@Service(version = "1.0.0")
public class HelloServiceImpl implements HelloService {
    @HystrixCommand(commandProperties = {
     @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
     @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") })
    @Override
    public String sayHello(String name) {
        // System.out.println("async provider received: " + name);
        // return "annotation: hello, " + name;
        throw new RuntimeException("Exception to show hystrix enabled.");
    }
}

配置Consumer端

對於Consumer端,則可以增加一層method呼叫,並在method上配置@HystrixCommand。當呼叫出錯時,會走到fallbackMethod = "reliable"的呼叫裡。

@Reference(version = "1.0.0")
private HelloService demoService;

@HystrixCommand(fallbackMethod = "reliable")
public String doSayHello(String name) {
   return demoService.sayHello(name);
}
public String reliable(String name) {
   return "hystrix fallback value";
}

尋找註冊中心、服務提供者、服務消費者(啟動載入篇-原始碼分析)

Dubbo自定義標籤實現。

dubbo通過Spring載入配置檔案後,是如何觸發註冊中心、服務提供者、服務消費者按照Dubbo的設計執行相關的功能。所謂的執行相關功能如下:

  • 註冊中心啟動,監聽訊息提供者的註冊服務、接收訊息消費者的服務訂閱(服務註冊與發現機制)。
  • 服務提供者向註冊中心註冊服務。
  • 服務消費者向註冊中心訂閱服務。

接下來從使用dubbo的角度,從配置檔案入手:

Dubbo服務提供者的一般配置如下:

<!-- 提供方應用資訊,用於計算依賴關係 -->
<dubbo:application name="uop" owner="uce"/>
<!-- 使用zookeeper註冊中心暴露服務地址 -->
<dubbo:registry protocol="zookeeper" address="zookeeper://192.168.xx.xx:2181?backup=192.168.xx.xx:2182,192.168.xx.xx:2183" />
<!--dubbox中引入Kryo和FST這兩種高效Java序列化實現,來逐步取代原生dubbo中的hessian2,如果使用kryo記得新增依賴 -->
<dubbo:protocol name="dubbo" serialization="kryo"  port="20990"  />
<!-- 定義服務提供者預設屬性值 -->
<dubbo:provider timeout="5000" threadpool="fixed"  threads="100" accepts="1000" token="true"/>
<!-- 暴露服務介面 一個服務可以用多個協議暴露,一個服務也可以註冊到多個註冊中心-->
<!--Provider上儘量多配置Consumer端的屬性,讓Provider實現者一開始就思考Provider服務特點、服務質量的問題-->
<dubbo:service interface="com.yingjun.dubbox.api.UserService" ref="userService" />

上面通過dubbo提供的dubbo:application、dubbo:registry、dubbo:protocol、dubbo:provider、dubbo:service分別定義dubbo應用程式名、註冊中心、協議、服務提供者引數預設值、服務提供者,這些配置後面的實現原理是什麼呢?是如何啟動併發揮相關作用的呢?

Spring自定義標籤實現原理

dubbo自定義標籤與名稱空間其實現程式碼在模組dubbo-config中,下面將詳細介紹其實現原理。

DubboNamespaceHandler

dubbo名稱空間實現handler,其全路徑:com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler,其原始碼實現如下:

public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

   @Override
    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }
}

從這裡可以看出,dubbo自定義的標籤主要包括:application、module、registry、monitor、provider、consumer、protocol、service、reference、annotation,其具體解析實現類主要由DubboBeanDefinitionParser(基於xml配置檔案)、AnnotationBeanDefinitionParser(基於註解),下文會詳細分析上述兩個解析類的實現。

定義dubbo.xsd 檔案

在dubbo-config-spring模組下的src/main/resouce/META-INF中分別定義dubbo.xsd、spring.handlers、spring.schemas。

關於Spring如何新增名稱空間與標籤,在原始碼分析ElasticJob時已經詳細介紹過,再這裡就不做過多重複,如需瞭解,請檢視:Spring自定義命令空間詳解

Bean解析機制

我們應該知道,Spirng的配置支援xml配置檔案與註解的方式,故Dubbo也支援兩種配置方式,xml與註解方式。

相關文章