Dubbo |ˈdʌbəʊ| 分布式初体验 第二章 Dubbo 使用配置


Dubbo |ˈdʌbəʊ| 分布式初体验 —— 第二章 Dubbo 使用配置


1. Dubbo 配置方式及优先级

1.1. 配置方式

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

  • JVM System Properties,JVM -D 参数
  • System environment,JVM进程的环境变量
  • Externalized Configuration,外部化配置,从配置中心读取
  • Application Configuration,应用的属性配置,从 Spring 应用的 Environment 中提取 dubbo 打头的属性集
  • API / XML / 注解等编程接口采集的配置可以被理解成配置来源的一种,是直接面向用户编程的配置采集方式
  • 从 Classpath 读取配置文件 dubbo.properties

1.2. 覆盖关系:

下图展示了 Dubbo 配置覆盖关系的优先级,从上到下优先级依次降低:

DubboCoverageStrategy

1.3. 配置加载流程

配置加载流程

1.4. 一些常用的配置

XML 详细配置参考:https://dubbo.apache.org/zh/docs/references/xml/

标签 用途 解释
<dubbo:service/> 服务配置 用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心
<dubbo:reference/> 引用配置 用于创建一个远程服务代理,一个引用可以指向多个注册中心
<dubbo:protocol/> 协议配置 用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受
<dubbo:application/> 应用配置 用于配置当前应用信息,不管该应用是提供者还是消费者
<dubbo:module/> 模块配置 用于配置当前模块信息,可选
<dubbo:registry/> 注册中心配置 用于配置连接注册中心相关信息
<dubbo:monitor/> 监控中心配置 用于配置连接监控中心相关信息,可选
<dubbo:provider/> 提供方配置 当 ProtocolConfig 和 ServiceConfig 某属性没有配置时,采用此缺省值,可选
<dubbo:consumer/> 消费方配置 当 ReferenceConfig 某属性没有配置时,采用此缺省值,可选
<dubbo:method/> 方法配置 用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息
<dubbo:argument/> 参数配置 用于指定方法参数配置

1.5. 配置之间的关系

dubbo-config

1.6. 不同粒度配置的覆盖关系

以 timeout 为例,下图显示了配置的查找顺序,其它 retries, loadbalance, actives 等类似:

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

其中,服务提供方配置,通过 URL 经由注册中心传递给消费方。

DubboXMLCoverageRelation

2. dubbo.properties

Dubbo 属性配置有两个职责:

  1. 定义配置:根据属性创建配置组件实例,类似 SpringBoot 的 @ConfigurationProperties 的作用。
  2. 属性覆盖:覆盖已存在的配置组件实例的属性值,类似 Spring PropertyOverrideConfigurer 的作用。

关于 dubbo.properties 属性:

  1. 如果在 Classpath 下有超过一个 dubbo.properties 文件,比如,两个 Jar 包都各自包含了 dubbo.properties,dubbo 将随机选择一个加载,并且打印错误日志。
  2. Dubbo 可以自动加载 Classpath 根目录下的 dubbo.properties,但是你同样可以使用 JVM 参数来指定路径:-Ddubbo.properties.file=xxx.properties

2.1. 映射规则

将 XML 配置的标签名 + 属性名,用点分隔,多个拆成多行,例如:

  • dubbo.application.name=foo 等价于<dubbo:application name="foo" />
  • dubbo.registry.address=10.20.153.10:9090 等价于 <dubbo:registry address="10.20.153.10:9090" />

如果 XML 有多行同名标签配置,可以用 ID 号区分,如果没有 ID 号则将所有同名标签生效,例如

  • dubbo.protocol.rmi.port=1234 等价于 <dubbo:protocol id="rmi" name="rmi" port="1099" />
  • dubbo.registry.china.address=10.20.153.10:9090 等价于 <dubbo:registry id="china" address="10.20.153.10:9090" />

下面的 dubbo.properties 的一个典型配置:

dubbo.application.name=foo
dubbo.application.owner=bar
dubbo.registry.address=10.20.153.10:9090

2.2. 覆盖关系

下图展示了配置覆盖关系的优先级,从上到下优先级依次降低:

DubboCoverageRelation

2.3. 属性覆盖

属性覆盖是指用配置的属性值覆盖 Config Bean实例的属性,类似 Spring PropertyOverrideConfigurer 的作用。

Property resource configurer that overrides bean property values in an application context definition. It pushes values from a properties file into bean definitions. Configuration lines are expected to be of the following form:

beanName.property=value

但与 PropertyOverrideConfigurer 的不同之处是,Dubbo 的属性覆盖有多个匹配格式,优先级从高到低依次是:

#1. 指定id的实例级配置
dubbo.&#123;config-type&#125;s.&#123;config-id&#125;.&#123;config-item&#125;=&#123;config-item-value&#125;

#2. 指定name的实例级配置
dubbo.&#123;config-type&#125;s.&#123;config-name&#125;.&#123;config-item&#125;=&#123;config-item-value&#125;

#3. 应用级配置(单数配置)
dubbo.&#123;config-type&#125;.&#123;config-item&#125;=&#123;config-item-value&#125;

属性覆盖处理流程:

按照优先级从高到低依次查找,如果找到此前缀开头的属性,则选定使用这个前缀提取属性,忽略后面的配置。

属性覆盖流程

2.4. 单复数配置对照表

复数配置的命名与普通单词变复数的规则相同:

  1. 辅音字母 + y 结尾时,去掉 y,改为 ies
  2. 字母 sshch 结尾时,加 es
  3. 其它加 s
Config Type 单数配置 复数配置
application dubbo.application.xxx=xxx dubbo.applications.{id}.xxx=xxx
dubbo.applications.{name}.xxx=xxx
protocol dubbo.protocol.xxx=xxx dubbo.protocols.{id}.xxx=xxx
dubbo.protocols.{name}.xxx=xxx
module dubbo.module.xxx=xxx dubbo.modules.{id}.xxx=xxx
dubbo.modules.{name}.xxx=xxx
registry dubbo.registry.xxx=xxx dubbo.registries.{id}.xxx=xxx
monitor dubbo.monitor.xxx=xxx dubbo.monitors.{id}.xxx=xxx
config-center dubbo.config-center.xxx=xxx dubbo.config-centers.{id}.xxx=xxx
metadata-report dubbo.metadata-report.xxx=xxx dubbo.metadata-reports.{id}.xxx=xxx
ssl dubbo.ssl.xxx=xxx dubbo.ssls.{id}.xxx=xxx
metrics dubbo.metrics.xxx=xxx dubbo.metricses.{id}.xxx=xxx
provider dubbo.provider.xxx=xxx dubbo.providers.{id}.xxx=xxx
consumer dubbo.consumer.xxx=xxx dubbo.consumers.{id}.xxx=xxx
service dubbo.service.{interfaceName}.xxx=xxx
reference dubbo.reference.{interfaceName}.xxx=xxx
method dubbo.service.{interfaceName}.{methodName}.xxx=xxx
dubbo.reference.{interfaceName}.{methodName}.xxx=xxx
argument dubbo.service.{interfaceName}.{methodName}.{arg-index}.xxx=xxx

3. 启动时检查

启动时检查可以在启动时检查依赖的服务是否可用

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

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

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

示例:

  • 对于Spring 配置文件 dubbo.xml

    • 关闭某个服务的启动时检查(没有提供者时报错)

      <dubbo:reference interface="com.yourname.mall.service.UserService" id="userService" check="false" />
      
    • 关闭所有服务的启动时检查(没有提供者时报错)

      <dubbo:consumer check="false" />
      
    • 关闭注册中心启动时检查(注册订阅失败时报错)

      <dubbo:registry check="false" />
      
  • 通过 dubbo.properties

    dubbo.reference.com.yourname.mall.service.BaseService.check=false
    dubbo.reference.check=false
    dubbo.consumer.check=false
    dubbo.registry.check=false
    
  • 通过 -D 参数

    $ java -Ddubbo.reference.com.yourname.mall.service.BaseService.check=false
    $ java -Ddubbo.reference.check=false
    $ java -Ddubbo.consumer.check=false
    $ java -Ddubbo.registry.check=false
    
  • 对于 SpringBoot 配置文件 application.properties / application.yaml

    dubbo:
        reference:
            com:
                yourname:
                    mall:
                        service:
                            BaseService:
                                check: false
                                
            check: false
            
        consumer: 
            check: false
            
        registry:
            check: false
    

配置的含义:

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

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

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

4. 超时

当服务消费者引用服务提供者时,可能由于网络原因,需要执行很长一段时间,如果长时间都没有返回,可能会导致大量线程阻塞,从而引起性能下降,为了解决这个问题,我们可以指定一个超时属性(timeout

示例:

  • 设置某个服务的超时时间为 3000ms(可选属性,默认使用 <dubbo:consumer> 中的 timeout

    <dubbo:reference interface="com.yourname.mall.service.UserService" id="userService" timeout="3000" />
    
  • 设置某个服务中的某个方法的超时时间为 3000ms

    <dubbo:reference interface="com.yourname.mall.service.UserService" id="userService">
          <dubbo:method name="getUserAddressList" timeout="3000" />
    </dubbo:reference>
    
  • 设置所有服务的超时时间为 3000ms

    <dubbo:consumer timeout="3000" />
    

5. 重试次数

重试次数(retries)通常会结合超时属性一起使用。当某一个服务由于网络不佳或者是服务运行缓慢导致超时,远程调用失败时,可以通过设置重试次数来进行多次尝试,重试次数不包含第一次调用,0 代表不重试

通常在幂等方法下,建议设置重试次数以来提升系统性能,而在非幂等方法下,则不建议设置重试次数,以免出现结果不一致的情况

幂等方法:无论运行多少次,最终都是一个结果,如:查询、删除、修改方法

非幂等方法:每一次运行的最终结果都不一致,如:新增方法

示例:

  • 设置某个服务的超时时间为 3000ms,重试 3

    <dubbo:reference interface="com.yourname.mall.service.UserService" id="userService" timeout="3000" retries="3" />
    
  • 设置某个服务中的某个方法的超时时间为 3000ms,重试 3

    <dubbo:reference interface="com.yourname.mall.service.UserService" id="userService">
          <dubbo:method name="getUserAddressList" timeout="3000" retries="3" />
    </dubbo:reference>
    
  • 设置所有服务的超时时间为 3000ms,重试 3

    <dubbo:consumer timeout="3000" retries="3" />
    

6. 多版本

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

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

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

示例:

老版本服务提供者配置:

<dubbo:service interface="com.yourname.mall.service.UserService" version="1.0.0" />

新版本服务提供者配置:

<dubbo:service interface="com.yourname.mall.service.UserService" version="2.0.0" />

老版本服务消费者配置:

<dubbo:reference id="barService" interface="com.yourname.mall.service.UserService" version="1.0.0" />

新版本服务消费者配置:

<dubbo:reference id="userService" interface="com.yourname.mall.service.UserService" version="2.0.0" />

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

[^1]:提示:需要2.2.0 以上版本支持

<dubbo:reference id="userService" interface="com.yourname.mall.service.UserService" version="*" />

7. 本地存根

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

本地存根通常会放在 API 包中方便调用

DubboStub

示例:

在 Spring 配置文件中按以下方式配置:

<dubbo:service interface="com.yourname.service.BarService" stub="true" />
<dubbo:service interface="com.yourname.service.BarService" stub="com.yourname.service.BarServiceStub" />

提供 Stub 的实现[^3]:

public class UserServiceStub implements UserService &#123;
    private final UserService userService;
    
    // 构造函数传入真正的远程代理对象
    public UserServiceStub(UserService userService)&#123;
        this.userService = userService;
    &#125;
 
    public String getUserAddressList(String userId) &#123;
        // 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等
        try &#123;
            return userService.getUserAddressList(userId);
        &#125; catch (Exception e) &#123;
            // 你可以容错,可以做任何AOP拦截事项
            return "容错数据";
        &#125;
    &#125;
&#125;

[^2]:Stub 必须有可传入 Proxy 的构造函数。
[^3]:在 interface 旁边放一个 Stub 实现,它实现 BarService 接口,并有一个传入远程 BarService 实例的构造函数

8. Dubbo 与 SpringBoot 整合的三种方式

Dubbo 与 SpringBoot 整合的三种方式

  1. 导入 dubbo-spring-boot-starter,在 application.properties / application.yaml 中配置属性,使用 @DubboService 注解暴露服务,使用 @DubboReference 注解引用服务(需要使用 @EnableDubble 开启基于注解的 Dubbo 功能,如果想要对某一个方法进行详细配置的话可以在 @DubboService 或者是 @DubboReferencemethods 参数中设置:{@Method(name = "getUserAddressList", timeout = 10000, retries = 5)} 来实现)

  2. 保留 dubbo.xml 配置文件,使用 @ImportResource(locations = "classpath:dubbo.xml") 导入原始 XML 配置文件

  3. 使用注解 API 的方式,将每一个组件手动创建到容器中,让 Dubbo 来扫描其他的组件

    1. 创建一个配置类

      @Configuration
      public class MyDubboConfig &#123;
            @Bean
            public ApplicationConfig applicationConfig() &#123;
                ApplicationConfig applicationConfig = new ApplicationCOnfig();
                applicationConfig.setName("boot-user-service-provider");
              return applicationVonfig 
          &#125;
        
            @Bean
            public RegistryConfig registryConfig() &#123;
                RegistryConfig registryConfig = new RegistryConfig();
                registryConfig.setProtocol("zookeeper");
                registryConfig.setAddress("127.0.0.1:2181");
                return registryConfig
          &#125;
            
            @Bean
            public protocolConfig protocolConfig() &#123;
                ProtocolConfig protocolConfig = new protocolConfig();
                protocolConfig.setName("dubbo");
                protocolConfig.setPort(20882);
                return protocolConfig
          &#125;
        
          @Bean
            public ServiceCopnfig<UserService> serviceConfig(UserService userService) &#123;
                ServiceCopnfig<UserService> serviceConfig = new ServiceCopnfig<>();
                serviceConfig.setInterface(UserService.class);
                serviceConfig.setRef(userService);
                serviceConfig.setVersion("1.0.0");
            
                // 编写每一个 Method 信息
                MethodConfig methodConfig = new MethodConfig();
                methodConfig.setName("getUserAddressList");
                methodConfig.setTimeout(10000);
            
                // 将 Method 的设置关联到 Service 配置中
                serviceConfig.setMethods(Arrays.asList(methodConfig));
                
                // ProviderConfig
              // MonitorConfig
                // ...
                
                return serviceConfig
          &#125;
      &#125;
      
    2. 设置包扫描 @EnableDubbo(sacnBasePackages = "com.yourname.mall")

    3. 使用 @DubboService 暴露服务



文章作者: gregPerlinLi
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 gregPerlinLi !
  目录