乐趣区

关于dubbo:dubbo服务降级

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 并且继承了 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
退出移动版