[toc]html
相關文檔推薦:java
Dubbo 的集羣容錯中默認會組裝 MockClusterWrapper,它實現了 Dubbo 的服務降級和本地假裝。spring
服務降級配置方式,更多參考官網 Dubbo 實戰 - 服務降級apache
<dubbo:reference interface="com.foo.BarService" mock="force:return+null"/>
或向註冊中心寫入動態配置覆蓋規則:json
"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 實戰 - 本地假裝api
<dubbo:reference interface="com.foo.BarService" mock="true"/> <dubbo:reference interface="com.foo.BarService" mock="com.foo.BarServiceMock"/> <dubbo:reference interface="com.foo.BarService" mock="return null"/> <dubbo:reference interface="com.foo.BarService" mock="throw com.foo.MockException" />
以上幾種方式,和 mock=fail:return+null
同樣,表示消費方對該服務的方法調用在失敗後,執行 mock 配置的代碼。緩存
在上一篇講解 Dubbo Cluster 時能夠看到, Dubbo 默認會將 Cluster#join
生成的 ClusterInvoker 對象包裝 MockClusterInvoker。app
總結: Dubbo Mock 主要流程以下:dom
MockClusterWrapper
:因爲這個類是 Cluster 的包裝類,因此 Dubbo 默認裝配 MockClusterWrapper,對 ClusterInvoker 進行包裝。MockClusterInvoker
:核心類,對 ClusterInvoker 進行包裝,主要功能:一是判斷是否須要開啓 Mock 機制;二是根據 MockInvokersSelector 過濾出對應的 Mock Invoker;三是執行 MockInvoker。MockInvokersSelector
:Mock 路由策略,因爲是 @Activate 修辭,所以會自動裝配。當不開啓 Mock 時返回正常的 Invoker,當開啓了 Mock 後返回 Mock Invoker。MockProtocol
:建立 MockInvoker。這個 MockProtocol 只能引用,不能暴露。MockInvoker
:核心類,真正執行服務降級,處理 mock="return null"
、mock="throw com.foo.MockException"
、mock="com.foo.BarServiceMock"
。MockClusterInvoker 的主要功能是判斷是否須要開啓 Mock 機制,若是開啓 Mock 則須要過濾出 MockInvoker 後執行服務降級。MockClusterWrapper 和 MockClusterInvoker 位於 dubbo-cluster
工程下。ide
MockClusterWrapper 是包裝類,按 Dubbo SPI 機制,會將默認的 Cluster 進行包裝。
public class MockClusterWrapper implements Cluster { private Cluster cluster; public MockClusterWrapper(Cluster cluster) { this.cluster = cluster; } @Override public <T> Invoker<T> join(Directory<T> directory) throws RpcException { return new MockClusterInvoker<T>(directory, this.cluster.join(directory)); } }
總結: Dubbo 默認的 Cluster 是 FailoverCluster,也就是說 MockClusterWrapper 會對 FailoverCluster 進行包裝。接下來看一下 Mock 的核心 MockClusterInvoker 執行過程。
MockClusterInvoker 是 Dubbo Mock 的核心類,主要功能有三個:
invoke 判斷是否須要開啓 Mock 機制,若是須要開啓,則調用 doMockInvoke 進行服務降級。
@Override public Result invoke(Invocation invocation) throws RpcException { Result result = null; String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim(); if (value.length() == 0 || value.equalsIgnoreCase("false")) { //no mock result = this.invoker.invoke(invocation); } else if (value.startsWith("force")) { //force:direct mock result = doMockInvoke(invocation, null); } else { //fail-mock try { result = this.invoker.invoke(invocation); } catch (RpcException e) { if (e.isBiz()) { throw e; } result = doMockInvoke(invocation, e); } } return result; }
總結: **invoke 關注一個問題,是否須要開啓 Mock,若是開啓 Mock 調用 doMockInvoke 執行。**代碼註釋已經很清楚了,分別對 no mock:(正常流程)
、force:(強制mock)
、fail:(失敗mock,默認)
分別處理。若是 mock=false 則正常處理,若是配置 mock="return null"
和 mock="fail:return+null"
處理流程是同樣的。
doMockInvoke 執行服務降級。
private Result doMockInvoke(Invocation invocation, RpcException e) { Result result = null; Invoker<T> minvoker; // 1. 過濾能夠用 mockInvokers List<Invoker<T>> mockInvokers = selectMockInvoker(invocation); // 2. 若是沒有,建立 MockInvoker if (CollectionUtils.isEmpty(mockInvokers)) { minvoker = (Invoker<T>) new MockInvoker(directory.getUrl(), directory.getInterface()); } else { minvoker = mockInvokers.get(0); } // 3. 執行服務降級 mockInvoker try { result = minvoker.invoke(invocation); } catch (RpcException me) { if (me.isBiz()) { result = AsyncRpcResult.newDefaultAsyncResult(me.getCause(), invocation); } else { throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause()); } } catch (Throwable me) { throw new RpcException(getMockExceptionMessage(e, me), me.getCause()); } return result; }
總結: doMockInvoke 最終調用 minvoker.invoke(invocation)
進行服務降級,其中須要關注的是 selectMockInvoker(invocation)
過濾緩存中的 MockInvoker,若是沒有就須要建立新的 MockInvoker。
selectMockInvoker 方法很奇怪,沒有看到真正的 MockInvoker 過濾究竟是怎麼完成的。實際上 Dubbo 的默認路由策略就包含了 MockInvokersSelector,由這個類完成規則路由。
private List<Invoker<T>> selectMockInvoker(Invocation invocation) { List<Invoker<T>> invokers = null; if (invocation instanceof RpcInvocation) { // 1. 設置invocation.need.mock=true ((RpcInvocation) invocation).setAttachment(INVOCATION_NEED_MOCK, Boolean.TRUE.toString()); // 2. 調用 MockInvokersSelector 路由規則過濾服務列表 invokers = directory.list(invocation); ... } return invokers; }
總結: selectMockInvoker 方法偷偷在將 invocation 的 invocation.need.mock 屬性設置爲 false,這個參數在 MockInvokersSelector 中就頗有用了。而後經過 directory.list(invocation)
方法從新獲取服務列表,在 Dubbo 系列(07-1)集羣容錯 - 服務字典 分析 RegisterDirectory 源碼時,咱們知道 list 方法會調用 routeChain.route
路由規則過濾服務。 下面看一下 MockInvokersSelector 代碼。
MockInvokersSelector 在未開啓 Mock 時返回正常的 Invokers,開啓後返回 MockInvoker。
@Override public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers, URL url, final Invocation invocation) throws RpcException { if (CollectionUtils.isEmpty(invokers)) { return invokers; } if (invocation.getAttachments() == null) { // 1. 返回 -> 非MockedInvoker return getNormalInvokers(invokers); } else { String value = invocation.getAttachments().get(INVOCATION_NEED_MOCK); if (value == null) { return getNormalInvokers(invokers); // 2. invocation.need.mock=true則返回 -> MockedInvoker(MockProtocol) } else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) { return getMockedInvokers(invokers); } } // 3. invocation.need.mock=false則返回 -> 非MockedInvoker + MockedInvoker // ??? return invokers; }
**總結:**directory.list 調用 MockInvokersSelector.route 時,有三種狀況:
非MockedInvoker
。invocation.need.mock=true
則返回 MockedInvoker
。invocation.need.mock=false
則返回 非MockedInvoker + MockedInvoker
???MockProtocol 和 MockInvoker 位於 dubbo-rpc-api
工程下。
在 MockClusterInvoker#doMockInvoke 方法中,若是 directory.list 過濾出的 MockedInvoker 爲空,則會直接建立一個 MockedInvoker,代碼以下:
minvoker = (Invoker<T>) new MockInvoker(directory.getUrl(), directory.getInterface());
其實 Mock 也是一種協議,能夠在註冊中心 /dubbo/com.foo.BarService/providers 目錄下寫入:
"mock://192.168.139.101/com.foo.BarService"
這樣消費者訂閱 com.foo.BarService 服務後會根據 MockProtocol 協議建立對應的 MockedInvoker。
MockProtocol 只能經過 reference 引入,不能經過 export 暴露服務。其實也就是直接建立了一個 MockInvoker。
final public class MockProtocol extends AbstractProtocol { @Override public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { throw new UnsupportedOperationException(); } @Override public <T> Invoker<T> protocolBindingRefer(Class<T> type, URL url) throws RpcException { return new MockInvoker<>(url, type); } }
總結: MockProtocol 很是簡單,就很少說了。下面看一下 MockInvoker 代碼。
MockInvoker 執行服務降級。在 MockClusterInvoker 判斷是否須要開啓 Mock 後,MockInvokersSelector 過濾出可用的 MockInvoker,最後執行服務降級。
return
、throw
、xxxServiceMock
xxxServiceMock
須要查找對應的實現類:getInvoker。先看一下總體的執行流程 invoke 方法。
@Override public Result invoke(Invocation invocation) throws RpcException { // 1. 獲取mock值,URL 中 methodname.mock 或 mock 參數 String mock = getUrl().getParameter(invocation.getMethodName() + "." + MOCK_KEY); if (invocation instanceof RpcInvocation) { ((RpcInvocation) invocation).setInvoker(this); } if (StringUtils.isBlank(mock)) { mock = getUrl().getParameter(MOCK_KEY); } if (StringUtils.isBlank(mock)) { throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url)); } // 2. 對mock字符串進行處理,好比去除 `force:`、`fail:` 前綴 mock = normalizeMock(URL.decode(mock)); // 3. return if (mock.startsWith(RETURN_PREFIX)) { mock = mock.substring(RETURN_PREFIX.length()).trim(); try { Type[] returnTypes = RpcUtils.getReturnTypes(invocation); Object value = parseMockValue(mock, returnTypes); return AsyncRpcResult.newDefaultAsyncResult(value, invocation); } catch (Exception ew) { throw new RpcException(ew); } // 3. throw } else if (mock.startsWith(THROW_PREFIX)) { mock = mock.substring(THROW_PREFIX.length()).trim(); if (StringUtils.isBlank(mock)) { throw new RpcException("mocked exception for service degradation."); } else { // user customized class Throwable t = getThrowable(mock); throw new RpcException(RpcException.BIZ_EXCEPTION, t); } // 5. xxxServiceMock } else { //impl mock try { Invoker<T> invoker = getInvoker(mock); return invoker.invoke(invocation); } catch (Throwable t) { throw new RpcException("Failed to create mock implementation class " + mock, t); } } }
總結: invoke 執行服務降級,首先獲取 mock 參數,並對 mock 參數進行處理,如去除 force:
、fail:
前綴。Dubbo 服務降級有三種處理狀況:
return
:直接返回,能夠是 empty、null 、true 、false 、json 格式,由方式 parseMockValue 進行解析。throw
:直接拋出異常。若是沒有指定異常,拋出 RpcException,不然拋出指定的 Exception。xxxServiceMock
:執行 xxxServiceMock 方法。若是 mock=true
或 mock=defalut
則查找 xxxServiceMock 方法後執行,若是 mock=com.dubbo.testxxxService
則執行指定的方法。getInvoker 方法查找指定對流的 Invoker。
private Invoker<T> getInvoker(String mockService) { // 1. 緩存命中 Invoker<T> invoker = (Invoker<T>) MOCK_MAP.get(mockService); if (invoker != null) { return invoker; } // 2. 根據serviceType查找mock的實現類,默認爲 xxxServiceMock Class<T> serviceType = (Class<T>) ReflectUtils.forName(url.getServiceInterface()); T mockObject = (T) getMockObject(mockService, serviceType); // 3. 包裝成Invoker invoker = PROXY_FACTORY.getInvoker(mockObject, serviceType, url); if (MOCK_MAP.size() < 10000) { MOCK_MAP.put(mockService, invoker); } return invoker; } public static Object getMockObject(String mockService, Class serviceType) { // mock=true或default時,查找 xxxServiceMock if (ConfigUtils.isDefault(mockService)) { mockService = serviceType.getName() + "Mock"; } // mock=testxxxService,指定Mock實現類 Class<?> mockClass = ReflectUtils.forName(mockService); ... return mockClass.newInstance(); }
總結: Dubbo 若是不指定 Mock 實現類,默認查找 xxxServiceMock。若是存在該實現類,則將其包裝成 Invoker 後返回。
天天用心記錄一點點。內容也許不重要,但習慣很重要!