您的位置:首页 > 其它

dubbo解析-服务降级原理、Mock功能实现

2020-06-28 04:24 555 查看

本文基于dubbo 2.7.5版本代码

当远程服务不可用、网络异常或者远程服务执行异常,dubbo提供了Mock功能,可以实现服务降级。远程服务执行异常是远程服务执行过程中出现错误,向客户端返回Exception异常。
dubbo实现该功能是在类MockClusterInvoker中完成,MockClusterInvoker实现了Invoker接口。
在现有dubbo的SPI机制下,客户端发起任何远程调用,都必须通过MockClusterInvoker。先介绍一下MockClusterInvoker是如何被创建的。
在介绍集群容错的时候,提到过类MockClusterWrapper,该类是包装类。在SPI机制下加载Cluster接口对象的时候,MockClusterWrapper会将Cluster对象封装起来返回调用方,所有对Cluster对象的调用都必须通过MockClusterWrapper。

MockClusterWrapper是在上图蓝色方块中通过SPI创建的。MockClusterWrapper根据对cluster的设置,封装了Cluster对象。MockClusterWrapper的join方法如下:

public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
return new MockClusterInvoker<T>(directory,this.cluster.join(directory));
}

代码非常简单。this.cluster.join方法返回一个Cluster Invoker对象,默认是FailoverClusterInvoker。MockClusterWrapper使用MockClusterInvoker对Cluster Invoker对象做了一层封装。之后将MockClusterInvoker对象使用JDK代理再做一次封装,这个JDK代理对象就是客户端调用远程服务时使用的对象了。因此每次客户端访问远程服务时,首先调用MockClusterInvoker的invoke方法。
下面介绍MockClusterInvoker的invoke方法。
invoke方法中首先查看mock参数配置。mock参数在@Reference或者@Service中配置。mock参数可以配置的值以及其作用如下:

  1. mock=false或者空,不使用服务降级功能,调用封装的Cluster Invoker对象,默认值是false;
  2. mock=以"force"开头,那么直接使用mock服务;
  3. mock=其他,那么首先访问远程服务,如果网络不通、访问远程服务失败或者远程服务抛出异常后,再使用mock服务。

因为mock参数非常重要,是客户端能正常访问服务的最后一道防线,为了避免在客户端访问远程服务时才暴露问题,所以mock参数在客户端启动的时候,ReferenceConfig的init方法会调用checkMock方法检查mock参数。checkMock方法的检查流程是:

  1. normalizeMock方法会对mock参数正则化,正则化的规则是(1)去除多余的不可见字符(2)如果mock=“return ”,则将mock修改为“return null”(3)如果mock以“fail:”或者"force:"开头,则将“fail:”或者"force:"去掉,mock等于“fail:”或者"force:"后面的内容(4)替换return或者throw后面的非法字符(5)如果mock为fail、force、true、default,则将mock统一修改为default。normalizeMock不会对原始mock参数值修改,正则化只是为了后续的检查提供方便。
  2. 正则化后的mock如果以return开头,则对return后面的内容分析,分析规则比较简单,使用if/else根据可能的值分别判断,比如校验return后面的内容是否是JSON格式,如果是,则调用JSON.parseObject方法解析。
  3. 正则化后的mock如果以throw开头,那么throw后面跟的必须是Throwable的子类,且必须有一个以字符串为入参的构造方法,否则抛出异常,启动失败。
  4. 如果上述都不满足,则检查classpath下是否有以接口+Mock为名字的类,而且必须有一个无参构造方法。这里的接口指的是访问远程服务的接口,另外该类必须实现了远程服务的接口。比如客户端要访问远程服务WebsiteProcessor下的一个方法,那么此时classpath下必须要有一个类名为WebsiteProcessorMock,且必须有一个无参构造方法,WebsiteProcessorMock实现了WebsiteProcessor接口。

当配置mock不为false或者空时,使用mock服务都是调用doMockInvoke方法。
doMockInvoke方法头:

private Result doMockInvoke(Invocation invocation, RpcException e)

当配置mock=以"force"开头,入参e是null,否则是异常对象,比如服务超时,e是超时异常对象。
doMockInvoke方法的执行流程是:

  1. 调用selectMockInvoker方法选择mock的invoker列表;
  2. 如果上一步的invoker列表是空的,那么创建MockInvoker对象,否则获取列表中的第一个invoker;
  3. 调用上一步的invoker对象或者MockInvoker对象的invoke方法。invoke方法的返回值作为服务返回值返回客户端。

selectMockInvoker主要调用Directory接口的list方法选择invoker列表,invoker可以简单的认为是远程服务提供者。该方法的流程是:

上图的multiGroup属性表示是否匹配多个组的服务,当配置了group参数且group参数的是“*”或者配置了多个组(组名之间使用","分隔)时,multiGroup为true。
在过滤invoke列表的时候,需要遍历Route对象,默认Route对象有四个。Route对象是在创建RouterChain对象的时候通过SPI加载的。与mock相关的Route对象是MockInvokersSelector。MockInvokersSelector当没有设置invocation.need.mock时,MockInvokersSelector过滤掉mock协议的服务,将非mock协议的服务提供者列表返回,如果设置invocation.need.mock=false,则不对invoker列表做任何过滤,如果设置invocation.need.mock=true,则将mock协议的服务提供者列表返回。关于Route对象后面的文章在做详细介绍。
因为在遍历Route对象前已经设置invocation.need.mock=true,所以MockInvokersSelector只会将mock协议的服务提供者列表返回。mock协议服务提供者用对象MockInvoker表示。
selectMockInvoker方法执行完毕后,接下来就是对MockInvoker对象的invoke方法调用了。
MockInvoker实现了Invoker接口,invoke方法的执行逻辑如下:

上述流程中,在处理以return开头的mock值时,会根据return后面的内容,对返回值做类型上的转换,比如return false,那么返回值value是布尔类型的false,如果return后面是JSON格式,调用JSON.parseObject将其转为对象,返回值value即为该对象,最后还会根据要访问的远程方法返回类型,将返回值value转换为该类型的对象。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: