本地伪装使用方式
如何在 Dubbo 中利用本地伪装实现服务降级
本地伪装通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。
配置
<dubbo:reference interface="com.foo.BarService" mock="true" />
<dubbo:reference interface="com.foo.BarService" mock="com.foo.BarServiceMock" />
return
- empty: 代表空,基本类型的默认值,或者集合类的- 空值
- null: null
- true: true
- false: false
- JSON 格式: 反序列化 JSON 所得到的对象
throw
使用 throw 来返回一个 Exception 对象,作为 Mock 的返回值。
<dubbo:reference interface="com.foo.BarService" mock="throw" />
当调用出错时,抛出一个默认的 RPCException:
<dubbo:reference interface="com.foo.BarService" mock="throw com.foo.MockException" />
force 和 fail
在 2.6.6 以上的版本,可以开始在 Spring XML 配置文件中使用 fail: 和 force:。force: 代表强制使用 Mock 行为,在这种情况下不会走远程调用。fail: 与默认行为一致,只有当远程调用发生错误时才使用 Mock 行为。force: 和 fail: 都支持与 throw 或者 return 组合使用
<dubbo:reference interface="com.foo.BarService" mock="force:return fake" />
Dubbo调用流程
![](https://img.haomeiwen.com/i23353704/f2b4f5cfc533f9e6.png)
源码分析
Cluster接口对invoker对象的包装
在@Referce会用jdk创建代理实例,然后InvokeHanlder的invoke方法会调用成员变量bean的方法,bean由ReferenceConfig根据配置再次生成代理,最后注入到属性上。
创建的时候会一个可执行的invoker对象
ReferenceConfig.createProxy
![](https://img.haomeiwen.com/i23353704/3b804f38765c89d8.png)
RegistryProtocol.refer
![](https://img.haomeiwen.com/i23353704/ccfe81961843e156.png)
根据directory经过Cluster修饰返回修饰过后Invoker对象
![](https://img.haomeiwen.com/i23353704/c5dc78037a9cb75e.png)
Cluster成员变量
![](https://img.haomeiwen.com/i23353704/5cc7b6cf6db74d6d.png)
肯定是SPI工厂通过ioc注入进来的
![](https://img.haomeiwen.com/i23353704/b34bac483c9cee61.png)
注入进来的肯定是动态生成的类,里面获取的实例肯定是包装类
![](https://img.haomeiwen.com/i23353704/d7a628eea718dfd0.png)
包装类持有Cluster接口上@Spi默认指定的类
![](https://img.haomeiwen.com/i23353704/54dc39e0083797a8.png)
![](https://img.haomeiwen.com/i23353704/8f664256c7ff116a.png)
最后才是服务列表Directory实例,返回
![](https://img.haomeiwen.com/i23353704/7d9ca8a678c720ec.png)
对invoker对象进行代理
获取到Cluster实例包装过后的invoker之后,需要调用代理工厂,生成代理
![](https://img.haomeiwen.com/i23353704/c9e12a6822802d62.png)
通过javassist技术动态生成,对需要调用的目标类的包装wrapper对象类,然后new 出一个抽象类AbstractProxyInvoker的的实例,重写的的invoker里会由wrapper对象调用invoker的具体方法。
![](https://img.haomeiwen.com/i23353704/e6da48f9d38818f7.png)
远程服务代理实例发起tcp请求之前的调用流程
![](https://img.haomeiwen.com/i23353704/221777482a76cf88.png)
在调用原始invoker之前会先调用Cluster接口实例的invoker方法,附加集群容错的逻辑。
MockClusterInvoker
返回MockClusterInvoker实例,持有默认Cluster实例的FailoverClusterInvoker的引用和RegistryDirectory的引用
FailoverClusterInvoker :当前处理完,由下一个ClusterInvoker实例进行对原始invoker进行修饰
RegistryDirectory :包含服务列表
![](https://img.haomeiwen.com/i23353704/c0b01f5046415c8b.png)
MockClusterInvoker.invoke()
先是从url中获取mock的配置,如果没有配置,直接不做任何其他逻辑,调下一个Cluster实例修饰的invoker的invoke方法。
![](https://img.haomeiwen.com/i23353704/692552ca008012d8.png)
1. force 强制降级
如果你的mock配置以force开头,比如
@Reference(mock = "force:return fake")
private UserService userService;
则会走强制降级的判断,就不会调用后端服务,直接返回你配置的返回值
![](https://img.haomeiwen.com/i23353704/b9d7bd06926cc1af.png)
-
创建MockInvoker对象
先根据invocation里的method去本地服务列表里 拿protocol为mock的invokers,
image
image
image
从RegistryDirectory的methodInvokerMap根据methodName拿invokers
image
拿到之后会进行路由,这里起作用的路由类是MockInvokersSelector,会过滤掉所有协议不是mock的invoker,最后返回的inv okers是null
image
image
image
拿不到protocol为mock的invoker,就会new出 MockInvoker实例,最后调用MockInvoker.invoke方法
image
-
MockInvoker.invoke返回降级结果
-
如果配置的是force : return xxxx
就会解析出return 后面的值,然后判断出类型,创造出对应类型和值的实例,包装到RpcResult中,返回
image
这里解析出来的是个字符串,直接返回
image
-
force:throw com.xx.XXException
这种表示不发起远程调用,直接强制抛出异常
根据throw后面的全限定性异常类名,new 出Throwable实例,throw new RpcException()image
-
force:true / force:default / force:com.lb.mocrk.XXXServiceXX
这是表示强制调本地的某个类的方法,来进行伪装
这个类要和 远程实例类实现同一个接口,这样才能与远程实例类有同样的方法,走对应方法的降级方法
image
getInvoker(mock)根据的你的mock配置获取mock实现类实例
先是获取到服务接口名
image
如果你配置的mock属性的值是true / defalut ,就默认为接口名+ 'Mock' 作为mock的实现类
image
image
如果配置的是 一个全限定性类名,就直接用这个,最后是校验最终得到的mock类是否是服务接口的子类,校验通过 ,mockClass.newInstance()实例化返回
image
得到mock类实例后,用代理包装成invoker对象
image
最终就直接调mock实例的降级方法,不会发起远程调用
-
2. fail 异常降级
如果你的mock配置以fail开头,比如
@Reference(mock = "fail:return fake")
private UserService userService;
和强制降级不同,会先发起远程调用,如果捕获到异常,再走和强制降级一样的降级逻辑
![](https://img.haomeiwen.com/i23353704/d5ed54b09839df55.png)
网友评论