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