Dubbo的配置

  1. 昨天演示了Dubbo的环境搭建与演示,今天来说一下Dubbo的具体配置文件:

1、配置流程

官方给了一张图,通俗易懂:
1.jpg

首先,从Dubbo支持的配置来源说起,默认有四种配置来源:

  • JVM System Properties,-D参数
  • Externalized Configuration,外部化配置
  • ServiceConfig、ReferenceConfig等编程接口采集的配置
  • 本地配置文件dubbo.properties

加载配置文件的优先级,从上到下依次降低(如何配置官网上有详细说明:http://dubbo.apache.org/zh-cn/docs/user/configuration/configuration-load-process.html)

接下来我们具体看属性如何配置与作用

2、启动时检查

Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check="true"

可以通过 check="false" 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。

另外,如果你的 Spring 容器是懒加载的,或者通过 API 编程延迟引用服务,请关闭 check,否则服务临时不可用时,会抛出异常,拿到 null 引用,如果 check="false",总是会返回引用,当服务恢复时,能自动连上。

通俗点说:就是调用者每次启动的时候要检查自身依赖的服务是不是可用,也就是注册中心里有没有我需要的服务,如果没有就抛出异常,阻止Spring初始化完成

  1. dubbo.reference.com.foo.BarService.check=false #关闭某个服务的启动时检查 (没有提供者时报错)
  2. dubbo.reference.check=false #关闭所有服务的启动时检查 (没有提供者时报错)
  3. dubbo.consumer.check=false
  4. dubbo.registry.check=false #关闭注册中心启动时检查 (注册订阅失败时报错)

含义:

dubbo.reference.check=false,强制改变所有 reference 的 check 值,就算配置中有声明,也会被覆盖。

dubbo.consumer.check=false,是设置 check 的缺省值,如果配置中有显式的声明,如:<dubbo:reference check="true"/>,不会受影响。

dubbo.registry.check=false,前面两个都是指订阅成功,但提供者列表是否为空是否报错,如果注册订阅失败时,也允许启动,需使用此选项,将在后台定时重试。

3、超时限制

若多个调用者同时调用一个服务,会引起性能下降,导致速度变慢,调用者接收不到消息,抛出错误

  1. #服务方法调用超时时间(毫秒)
  2. dubbo.consumer.timeout=3000

dubbo的超时是争对客户端的,由于是一种NIO模式,消费端发起请求后得到一个ResponseFuture,然后消费端一直轮询这个ResponseFuture直至超时或者收到服务端的返回结果。虽然超时了,但仅仅是消费端不再等待服务端的反馈并不代表此时服务端也停止了执行。

如果只针对某一个服务进行超时设置的话,可以@DubboReference(timeout = 3000) 这样设置

查看源码,默认为0:
2.png

但是官网给出的则是一秒:
3.png

那么上面,我如果全局和局部都配置了,哪个会生效呢?

官网也给了一张图:
4.jpg

  • 方法级优先,接口级次之,全局配置再次之。
  • 如果级别一样,则消费方优先,提供方次之。

4、重试次数

远程服务调用重试次数,不包括第一次调用,不需要重试请设为0,仅在cluster为failback/failover时有效

  1. #当服务没请求通,重试的次数(不包括第一次)
  2. dubbo.consumer.retries=3

不配置的话,默认为两次:
5.png

具体到某个方法:@DubboReference(retries = 3)
6.png

查看源码,默认重试次数为2,timeout可能是新版本改动了,官网没来得及更新

如果有多个服务提供者,当调用者请求一个服务失败后,会去请求另一个提供者提供的服务(轮询)

具体测试参考:https://blog.csdn.net/nangeali/article/details/82470268

5、多版本

当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。

可以按照以下的步骤进行版本迁移:

  1. 在低压力时间段,先升级一半提供者为新版本
  2. 再将所有消费者升级为新版本
  3. 然后将剩下的一半提供者升级为新版本

老版本服务提供者配置:

  1. <dubbo:service interface="com.foo.BarService" version="1.0.0" />

新版本服务提供者配置:

  1. <dubbo:service interface="com.foo.BarService" version="2.0.0" />

老版本服务消费者配置:

  1. <dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />

新版本服务消费者配置:

  1. <dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />

如果不需要区分版本,可以按照以下的方式配置:

  1. <dubbo:reference id="barService" interface="com.foo.BarService" version="*" />

用SpringBoot的话,这里是直接可以将服务提供方注解标明版本号@DubboService(version = “1.0.2”)

调用方需要在@DubboReference(version = “1.0.2”) 来表示调用的是哪个版本

当然,如果提供方提供了多个版本,调用方可以随意调用:@DubboReference(version = “1.0.2”)

6、本地存根

远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub,然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。
7.jpg

下面举个例子,比如我们想在调用服务前先验证姓名为不为空:

其他代码看之前的测试demo

创建TicketServiceStub:

  1. package com.zym.service;
  2. import org.springframework.util.StringUtils;
  3. public class TicketServiceStub implements TicketService{
  4. private final TicketService ticketService;
  5. public TicketServiceStub(TicketService ticketService) {
  6. super();
  7. this.ticketService = ticketService;
  8. }
  9. @Override
  10. public String getTicket(String name) {
  11. if (!StringUtils.isEmpty(name)){
  12. ticketService.getTicket(name);
  13. return name;
  14. }else{
  15. System.out.println("名字不能为空");
  16. return null;
  17. }
  18. }
  19. }

修改UserServiceImpl

  1. @DubboReference(stub = "com.zym.service.TicketServiceStub")

然后开始测试:

测试类:

  1. package com.zym;
  2. import com.zym.service.UserService;
  3. import org.junit.jupiter.api.Test;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.boot.test.context.SpringBootTest;
  6. @SpringBootTest
  7. class ConsumerServerApplicationTests {
  8. @Autowired
  9. UserService userService;
  10. @Test
  11. void contextLoads() {
  12. userService.buyTicket(null);
  13. }
  14. }

8.png

可以看到,在调用服务前,先进行了验证。