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/forceService类全名 + 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 并且继承了Clusterjoin 办法就是生成一个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