[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 后返回。
天天用心记录一点点。内容也许不重要,但习惯很重要!