Dubbo——Mock

Mock

在Cluster中,還有最後一個MockClusterWrapper,由它實現了Dubbo的本地僞裝。這個功能的使用場景較多,通常會應用在以下場景中:服務降級;部分非關鍵服務全部不可用,希望主流程繼續進行;在下游某些節點調用異常時,可以以Mock的結果返回。

Mock常見的使用方法

Mock只有在攔截到RpcException的時候會啓用,數異常容錯方式的一種。業務層面其實也可以用try-catch來實現這種功能,如果使用下沉到框架中的Mock機制,則可以讓業務的實現更優雅

常見配置如下:

//配置方式1:可以在配置文件中配置
<dubbo:reference interface="com.foo.BarService" mock="true" />

//配置方式2
<dubbo:reference interface="com.foo.BarService" mock="com.foo.BarServiceMock" />

//配置方式3
<dubbo:reference interface="com.foo.BarService" mock="return null" />
//提供Mock實現,如果Mock配置了true或default,則實現的類名必須是接口名+Mock,如配置方式1
//否則會直接取Mock參數值作爲Mock實現類,如配置方式2
package com.foo;

public class BarServieMock implements BarService {
	public String sayHello(String name) {
		//可以僞造容錯數據,此方法只在出現RpcException時被執行
		return "容錯數據";
	}
}

當接口配置了Mock,在RPC調用拋出RpcException時救護執行Mock方法。最後一種return null的配置方式通常會在想直接忽略異常的時候使用。

服務的降級是在dubbo-admin中通過override協議更新Invoker的Mock參數實現的。如果Mock參數設置爲mock=force:return+null,則表明是強制Mock,強制Mock會讓消費者對該服務的調用直接返回null,不再發起遠程調用。通常使用在非重要服務已經不可用的時候,可以屏蔽下游對上游系統造成的影響。此外,還能把參數設置爲mock=fail:return+null,這樣爲消費者還會發起遠程調用,不過失敗後返回null,但是不拋出異常。

最後,如果配置的參數是以throe開頭的,即mock=throw,則直接拋出RpcException,不會發起遠程調用。

Mock的總體結構

Mock設計的接口比較多,整個流程貫穿Cluster和Protocol層,接口之間的邏輯關係如圖:

在這裏插入圖片描述
主要流程分爲Cluster層和Protocol 層。

  • MockClusterWrapper是一個包裝類,包裝類會被自動注入合適的打展點實現, 它的這輯很簡單,只是把被包裝擴展類作爲初始化參數來創建並返回一個MockClusterInvoker。
  • MockClusterlnvoker和其他的Clusterlnvoker一樣, 在Invoker 方法中完成了主要邏輯。
  • MockInvokersSelector是Router接口的一種實現,用於過濾出Mock的Invokero。
  • MockProtocol根據用戶傳入的URL和類型生成一個MockInvoker。
  • MockInvoker實現最終的Invoker邏輯。

MockInvoker與MockClusterInvoker看起來都是Invoker,它們之間有什麼區別呢?

首先,強制Mock、失敗後返回Mock結果等邏輯是在MockClusterInvoker裏處理的;其次,MockClusterInvoker在某些邏輯下,會生成MockInvoker並進行調用;然後,再MockInvoker裏會處理mock="return null"、mock="throw xxx"或mock=com.xxService這些配置邏輯。最後,MockInvoker還會被MockProtocol在引用遠程服務的時候創建。

可以任意,MockClusterInvoker會處理一些Class級別的Mock邏輯,例如:選擇調用哪些Mock類。MockInvoker處理的是方法級別的Mock邏輯,如返回值。

Mock的實現原理

1. MockClusterInvoker的實現原理:

MockClusterWapper 是一個包裝類, 它在創建MockClusterInvoker的時候會把被包裝的Invoker傳入構造方法,因此Mckiuserivoker 內部天生就含有一個Invoker 的引用。MockClusterlnvoker的invoke方法處理了主要邏輯,步驟如下:

  1. 獲取Invoker 的Mock參數。前面已經說過,該Invoker 是在構造方法中傳入的。如果該Invoker根本就沒有配置Mock,則直接調用Invoker的invoke方法並把結果返回:如果配置了Mock 參數,則進入下一步。

  2. 判斷參數是否以force 開頭,即判斷是否強制Mock。 如果是強制Mock, 則進入doMockInvoke邏輯。 如果不以force 開頭,則進入失敗後Mock的邏輯。

  3. 失敗後調用doMockInvoke 邏輯返回結果。在try代碼塊中直接調用Invoker的invoke方法,如果拋出了異常,則在catch代碼塊中調用doMockInvoke邏輯。

強制Mock和失敗後Mock都會調用doMockInvoke邏輯,其步驟如下:

  1. 通過selectMockInvoker獲得所有Mock類型的Invoker。selectMockInvoker 在對象的attachment屬性中偷偷放進一個invocation.need.mock-true的標識。directory 在list方法中列出所有Invoker的時候,如果檢測到這個標識,則使用MockInvokersSelector來過濾Invoker,而不是使用普通route實現,最後返回Mock類型的Invoker 列表。如果一個Mock類型的Invoker都沒有返回,則通過directory的URL新創建一個MockInvoker;如果有Mock類型的Invoker,則使用第一個。

  2. 調用MockInvoker的invoke方法。在try-catch中調用invoke方法並返回結果。如果出現了異常, 並且是業務異常,則包裝成一個RpcResult 返回,否則返回RpcException異常。

2. MockInvokersSelector的實現原理:
在doMockInvoke中的第一步中,directory會使用MockInvokersSelector來過濾出Mock類型的Invoker。MockInvokersSelector是Router接口的其中一種實現。它的路由時的具體邏輯如下:

  1. 判斷是否需要做Mock過濾。如果attachment爲空,或者沒有invocation.need.mock=true的標識,則認爲不需要做Mock過濾,進入步驟2;如果找到這個標識,則進入步驟3;
  2. 獲取非Mock類型的Inoker。遍歷所有的Invoker,如果它們的protocol中都沒有Mock參數,則整個列表直接返回。否則,把protocol中所有沒有Mock標識的取出來返回。
  3. 獲取Mock類型的Invoker。遍歷所有的Invoker如果它們的protocol中都沒有Mock參數,則直接返回null。否則,把protocol中所有含有Mock標識的取出來並返回。

3. MockProtocol與MockInvoker的實現原理:
MockProtocol也是協議的一種,主要是把註冊中心的Mock URL轉換爲MockInvoker對象。URL可以通過dubbo-admin或其他方式寫入註冊中心,它被定義爲只能引用,不能暴露,如下所示:
在這裏插入圖片描述

例如:我們在註冊中心/dubbo/com.test.xxxSvice/providers這個服務提供者的目錄下,寫入一個Mock的URL:mock://192168.0.123/com.test.xxxService

在MockInvoker的invoke方法中,主要處理邏輯如下:

  1. 獲取Mock參數值。通過URL獲取Mock配置的參數,如果爲空則拋出異常。優先會獲取方法級的Mock參數,例如:以methodName.mock爲key去獲取參數值;如果獲取不到,則嘗試以mock爲key獲取對應的參數值。

  2. 處理參數值是return的配置。如果只配置了一個return,即mock=return,則返回一個空的RpcResult;如果return後面還跟了別的參數,則首先解析返回類型,然後結合Mock參數和返回類型,返回Mock值。現支持一下類型的參數:

    1. Mock參數值等於empty,根據返回類型返回new xxx()空對象;
    2. 如果參數值是null、true、false,則直接返回這些值;
    3. 如果是其他字符串,則返回字符串;
    4. 如果是數字、List、Map類型,則返回對應的JSON傳;
    5. 如果都沒有匹配上,則直接返回Mock的參數值。
  3. 處理參數值是throw的配置。如果throw後面沒有字符串,則包裝成一個RpcException異常,直接拋出;如果throw後面有自定義的異常類,則使用自定義的異常類,幷包裝成一個RpcException異常拋出。

  4. 處理Mock實現類。先從緩存中取,如果有則直接返回。如果緩存中沒有,則先獲取接口的類型,如果Mock的參數配置的是true或default,則嘗試通過"接口名+Mock"查找Mock實現類,例如:TestService會查找Mock實現TestServiceMock。如果是其他配置方法,則通過Mock的參數值進行查找,例如:配置了mock=com.xxx.testServie,則會查找com.xxx.testService

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