Dubbo 作为一款服务治理框架,自身也蕴含了服务降级的性能,次要是对非重要的服务进行降级解决免得影响整体业务,本篇次要介绍一下如何做服务降级以及 Dubbo 是怎么实现的。
如何做服务降级
Dubbo 提供了两种配置能够实现服务降级,一个是 stub
,一个是mock
,当然为了兼容以前版本还有个local
(和 stub 根本一样,不做具体介绍),实际上stub
不是用作降级解决的,它本来的设计用意是在服务端接口层做一层代理,在服务调用前后通过自定义的扩大退出一些客户端的逻辑,所以官网称它叫本地存根,当然咱们也能够用它实现一些相似降级的性能。而 mock
就是真的服务降级,除了用于降级,还能够作用一些接口测试。
他们的区别大略就是:stub
与 AOP 的 around advice 相似,mock
等同于 AOP 中的 after-throwing advice。
从 Dubbo 官网提供的图,咱们也能看到区别:
stub
咱们先说说如何通过 stub
的形式实现服务降级,对于stub
,有三种赋值形式:
值 | 含意 |
---|---|
true | 取对应 Service 对应类全名 +Stub |
default | 同 true |
类全名(含包名) | 取本人指定的类 |
举个例子:
当初有个 service 接口如下:
package com.example.dubboprovider.rpc;
public interface CityService {String getCityName();
}
消费者端引入:
@Reference(stub = "true")
CityService cityService;
消费者端还须要新建一个类:
package com.example.dubboprovider.rpc;
/**
* @author Don
* @date 2021/11/8.
*/
public class CityServiceStub implements CityService {
CityService cityService;
public CityServiceStub(CityService cityService){this.cityService = cityService;}
@Override
public String getCityName() {
//before call
cityService.getCityName();
//after call
return "stub";
}
}
这里有两点要留神:
1、包名必须和服务端提供的 Service 包名统一,这里是 com.example.dubboprovider.rpc;
2、类要继承服务端提供的 Service 接口并要有一个有该 Service 类型参数的构造函数;
对于第一点的束缚,咱们能够通过第三种配置来躲避。
比方包名改为 com.example.dubboanalyze.stub
之后,对应的生产端引入的 stub
改为具体的类全名
@Reference(stub = "com.example.dubboanalyze.stub.CityServiceStub")
CityService cityService;
mock
再来说一下真正的降级 mock,这里要留神的一点是,mock 只反对客户端,不反对服务端,2.7.0 之后的版本如果在定义的 Service 中加了 mock 会导致启动失败,如:@Service(mock = "true")
。
mock 反对以下值配置,长度不能大于 200
值 | 含意 |
---|---|
true/default/fail/force | Service 类全名 + Mock |
return | 返回 null |
return xxx | 返回 xxx |
throw xxx | 抛出指定异样,xxx 必须是异样类全名,如:throw com.example.dubboanalyze.exception.DubboException |
fail: | 近程调用失败发动 mock 调用 |
force: | 不做近程调用,间接发动 mock 调用,可用于测试 |
类全名 | 同 fail |
有下面的表格我想差不多都明确了,这里再举个例子:
service 接口还是和下面一样,消费者端引入:
@Reference(mock = "force:com.example.dubboanalyze.mock.CityServiceMock")
CityService cityService;
再看 CityServiceMock
类的定义:
package com.example.dubboanalyze.mock;
public class CityServiceMock implements CityService {
@Override
public String getCityName() {return "mock";}
}
实现原理
看过了服务降级是怎么用的之后,也来看看服务降级是怎么做的。对于配置的值是怎么设置下来的就不讲了,在罕用配置这一文中曾经介绍了。
stub
先说一下 stub,这里须要看到十分重要的一个类 StubProxyFactoryWrapper
,它继承了ProxyFactory
基于 spi 机制 Dubbo 内置了加载该类,能够看 org.apache.dubbo.rpc.ProxyFactory 文件,定义了所有的 ProxyFactory
基于有 spi 机制的有 wrapper 先用 wapper 的准则,Reference 在获取代理的时候((T) proxyFactory.getProxy(invoker)
)会先应用 StubProxyFactoryWrapper
,也就是上面这段逻辑
从逻辑中能够看出以下信息:
1)泛化调用是不会被 stub 的;
2)除了默认的 ServiceName + Stub 就是自定义类全名;
3)其实是对于 Local 的一种兼容替换;
4)Stub 类须要有构造函数带 Service 类型的参数;
5)外面有一个 export
,然而目前看起来没什么用;
最终返回的代理就是这个 stub 类,所以咱们在发动接口调用的时候会先执行 stub。
mock
说到 mock,须要看到十分重要的一个类 MockClusterWrapper
和下面一样,也是 Wrapper 并且继承了 Cluster
,join
办法就是生成一个MockClusterInvoker
,同样外部也封装了一个一般的 Invoker。那在什么时候调用的呢?是在启动的时候生成接口代理类时,也就是org.apache.dubbo.config.ReferenceConfig#createProxy
中
如果有多个注册核心或者多直连 url,则间接合并集群的时候生成,如果只有一个的话,则是在协定援用的时候 org.apache.dubbo.registry.integration.RegistryProtocol#doRefer
所以咱们一开始近程调用时候其实是先走的 MockClusterInvoker
,它也是继承了Invoker
接口
从代码中咱们能够看到除了 force 的时候调用就是在抛出 RpcException
的时候发动调用,持续跟进代码 org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker#doMockInvoke
-> org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker#selectMockInvoker
能够看到这里去找 Invoker,持续跟进到 org.apache.dubbo.registry.integration.RegistryDirectory#doList
能够看到这里应用路由链的形式去找
前面的很简略了,这里要留神的是 MockInvokersSelector
,除非咱们定义了 Mock 协定的服务,否则就会返回 null,而后新建一个MockInvoker
,而新建的MockInvoker
就会通过解析咱们定义的各种 mock 值来发动调用
总结
这一篇大略介绍了一些 stub 和 mock 两种服务降级形式,大家应该有一些感悟了,比方能够通过 stub 对 dubbo 接口调用对立记日志,通过 mock 做接口屏蔽测试。另外还有两个点没有钻研也没有写
- mock 的协定还没有钻研
- 服务端是否可能做 stub