Dubbo——Mock 布满荆棘的人生 2021-08-30 23:28 453阅读 0赞 ### 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层,接口之间的逻辑关系如图: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NvbGRfX19wbGF5_size_16_color_FFFFFF_t_70] 主要流程分为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或其他方式写入注册中心,它被定义为只能引用,不能暴露,如下所示: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NvbGRfX19wbGF5_size_16_color_FFFFFF_t_70 1] 例如:我们在注册中心`/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`。 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NvbGRfX19wbGF5_size_16_color_FFFFFF_t_70]: /images/20210813/9ef7159ab9a64322aa6e2acfc53388b3.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NvbGRfX19wbGF5_size_16_color_FFFFFF_t_70 1]: /images/20210813/637a56a3511e452db0273f41be01cb3a.png
还没有评论,来说两句吧...