4、Dubbo消费端服务mock&降级策略原理
当我们聊服务消费端降级的时候时候,应该考虑哪些问题?
- dubbo如何设置服务消费的降级策略?用到了那个类(保存到zookeeper的configurator节点下)?
- dubbo的服务降级策略有哪些?
- 如何使用dubbo的服务降级策略?
降级策略注册
当服务因为超时等某些原因不可用的时候,我们需要无服务设置降级策略;手动编写伪代码将服务降级信息注册到zookeeper;也可以通过配置mock参数的形式指定某个消费服务的mock策略,返回指定字符串,或者自定义mock处理类;
1、手动伪代码将服务降级注册到zk
/**
* 注册服务降级策略到注册中心
* @Param type 降级策略类型
*/
public static void mockResult(String type){
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));
registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));
}
2、配置mock参数指定返回字符串
<dubbo:reference id="xxService" check="false" interface="com.xx.XxService" timeout="3000" mock="return null" />
<dubbo:reference id="xxService2" check="false" interface="com.xx.XxService2" timeout="3000" mock="return 1234" />
timeout:超过该时间返回null 或者1234。
配置后启动即可,我们在服务提供者例 如 UserServiceImpl 中的 getUser 方法打个断点来模拟请求超时。
然后浏览器访问,断点不过,一致等待,当时间超过3秒,直接返回了空;ok! 这样我们就已经实现了一个简单的服务降级了 。
3、自定义服务降级方法实现类
<dubbo:reference id="xxService" check="false" interface="com.xx.XxService" timeout="3000" mock="true"/>
配置dubbo服务消费引用标签的mock参数值为true,同时定义服务名+Mock 的自定义类;这样在服务接口超时时,就会返回自定义的相应信息;
public class UserServiceMock implements UserService{
public List<Article>getUserArticles(int uid){
return null;
}
public List<User>getUser(String name){
//throw new RuntimeException("服务降级-----");
User user = new User();
user.setUserName("服务降级啦");
user.setUserAge("500");
List<User> list = new ArrayList<User>();
list.add(user);
return list;
}
}
4、通过admin管理平台手动配置服务降级策略
不论使用何种方式,最终降级策略会被注册到zk上的configurators节点下(假设使用zk注册中心),
- mock值默认false:直接发起远程调用,无降级
- mock值设置为force: 不发起远程调用,直接返回降级策略
- mock值设置为fail: 发起远程调用,失败时return降级策略
服务消费端使用降级策略
服务消费端在MockClusterInvoker类的invoke()方法里使用降级策略并在DubboInvoker的invoke()方法里发起远程调用,所以服务降级是在消费端还没有发起远程调用时完成。
1、MockClusterInvoker代码
@Override
public Result invoke(Invocation invocation) throws RpcException {
Result result;
// 获得 "mock" 配置项,有多种配置方式
String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
//【第一种】无 mock
if (value.length() == 0 || value.equalsIgnoreCase("false")) {
// no mock
// 调用原 Invoker ,发起 RPC 调用
result = this.invoker.invoke(invocation);
//【第二种】强制服务降级 https://dubbo.gitbooks.io/dubbo-user-book/demos/service-downgrade.html
} else if (value.startsWith("force")) {
if (logger.isWarnEnabled()) {
logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
}
// force:direct mock
// 直接调用 Mock Invoker ,执行本地 Mock 逻辑
result = doMockInvoke(invocation, null);
// 【第三种】失败服务降级 https://dubbo.gitbooks.io/dubbo-user-book/demos/service-downgrade.html
} else {
// fail-mock
try {
// 调用原 Invoker ,发起 RPC 调用
result = this.invoker.invoke(invocation);
} catch (RpcException e) {
// 业务性异常,直接抛出
if (e.isBiz()) {
throw e;
} else {
if (logger.isWarnEnabled()) {
logger.info("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
}
// 失败后,调用 Mock Invoker ,执行本地 Mock 逻辑
result = doMockInvoke(invocation, e);
}
}
}
return result;
}
directory.getUrl获取服务提供者的动态配置信息,含降级mock参数信息;之后判断三种mock值,决定是否执行降级策略;这里关注如果mock值为fail,如果远程调用失败,则返回降级策略,乳沟成功则代码会调用FailoverClusterInvokerd的doInvoke()方法发起远程调用;