目录

dubbo配置

dubbo配置

配置原则

映射规则

可以将 xml 的 tag 名和属性名组合起来,用 ‘.’ 分隔。每行一个属性。

  • 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 配置中有超过一个的 tag,那么你可以使用 ‘id’ 进行区分。如果你不指定 id,它将作用于所有 tag。

  • dubbo.protocol.rmi.port=1099 相当于 <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 配置样例。

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

重写与优先级

http://img.cana.space/picStore/20201120102838.png

  • JVM -D 参数:当你部署或者启动应用时,它可以轻易地重写配置,比如,改变 dubbo 协议端口;
  • XML:XML 中的当前配置会重写 dubbo.properties 中的;
  • Properties:默认配置,仅仅作用于以上两者没有配置时。
  1. 如果在 classpath 下有超过一个 dubbo.properties 文件,比如,两个 jar 包都各自包含了 dubbo.properties,dubbo 将随机选择一个加载,并且打印错误日志。
  2. 如果 id 没有在 protocol 中配置,将使用 name 作为默认属性。

启动检查

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

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

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

dubbo:consumer配置

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

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

dubbo.registry.check=false,注册中心不存在时,是否报错

超时时间&配置覆盖关系

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

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

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

http://img.cana.space/picStore/20201120105122.png

(建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置)。

理论上 ReferenceConfig 中除了interface这一项,其他所有配置项都可以缺省不配置,框架会自动使用ConsumerConfig,ServiceConfig, ProviderConfig等提供的缺省配置。

使用springboot,在指定service上添加只针对该类中的该service调用的方法起作用,可以在@Service和@Reference注解里面配置timeout属性

重试次数

重试次数一般配合超时时间使用,重试次数不包括第一次调用

演示:

消费端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Slf4j
@Service
public class OrderService {
    @Reference(timeout = 1000, retries = 3)
    private UserService userService;

    /**
     * 初始化订单,查询用户的所有地址并返回
     *
     * @param userId
     * @return
     */
    public List<UserAddress> initOrder(String userId) {
        return userService.getUserAddressList(userId);
    }
}

服务端:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Slf4j
@Component
@Service
public class UserServiceImpl implements UserService {
    @SneakyThrows
    @Override
    public List<UserAddress> getUserAddressList(String userId) {
        log.info("com.eh.mall.order.service.OrderService.initOrder:{}", userId);
        TimeUnit.SECONDS.sleep(3);
        UserAddress address1 = new UserAddress(1, "北京市昌平区宏福科技园综合楼3层", "1", "李老师", "010-56253825", "Y");
        UserAddress address2 = new UserAddress(2, "深圳市宝安区西部硅谷大厦B座3层(深圳分校)", "1", "王老师", "010-56253825", "N");
        return Lists.newArrayList(address1, address2);
    }
}

调用方法时可以看到控制台输出了4次com.eh.mall.order.service.OrderService.initOrder:1

注意:重试需要设置在幂等方法上,非幂等方法不能设置重试

多版本

准备两份服务提供者接口实现

old:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Slf4j
@Component
@Service(version = "1.0.0")
public class UserServiceImpl implements UserService {
    @SneakyThrows
    @Override
    public List<UserAddress> getUserAddressList(String userId) {
        log.info("old");
        TimeUnit.SECONDS.sleep(3);
        UserAddress address1 = new UserAddress(1, "北京市昌平区宏福科技园综合楼3层", "1", "李老师", "010-56253825", "Y");
        UserAddress address2 = new UserAddress(2, "深圳市宝安区西部硅谷大厦B座3层(深圳分校)", "1", "王老师", "010-56253825", "N");
        return Lists.newArrayList(address1, address2);
    }
}

new:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Slf4j
@Component
@Service(version = "1.0.1")
public class UserServiceImpl1 implements UserService {
    @SneakyThrows
    @Override
    public List<UserAddress> getUserAddressList(String userId) {
        log.info("new");
        UserAddress address1 = new UserAddress(1, "北京市昌平区宏福科技园综合楼3层", "1", "李老师", "010-56253825", "Y");
        UserAddress address2 = new UserAddress(2, "深圳市宝安区西部硅谷大厦B座3层(深圳分校)", "1", "王老师", "010-56253825", "N");
        return Lists.newArrayList(address1, address2);
    }
}

消费者决定选择哪一个版本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Slf4j
@Service
public class OrderService {
    @Reference(version = "1.0.1")
    private UserService userService;

    /**
     * 初始化订单,查询用户的所有地址并返回
     *
     * @param userId
     * @return
     */
    public List<UserAddress> initOrder(String userId) {
        return userService.getUserAddressList(userId);
    }
}

当服务调用时,服务端控制台会打印出“new”。

本地存根

在 Dubbo 中利用本地存根在客户端执行部分逻辑

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

http://img.cana.space/picStore/20201120120001.png

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

1
<dubbo:service interface="com.foo.BarService" stub="true" />

1
<dubbo:service interface="com.foo.BarService" stub="com.foo.BarServiceStub" />

提供Stub的实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.foo;
public class BarServiceStub implements BarService {
    private final BarService barService;
    
    // 构造函数传入真正的远程代理对象
    public BarServiceStub(BarService barService){
        this.barService = barService;
    }
 
    public String sayHello(String name) {
        // 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等
        try {
            return barService.sayHello(name);
        } catch (Exception e) {
            // 你可以容错,可以做任何AOP拦截事项
            return "容错数据";
        }
    }
}
  1. Stub 必须有可传入 Proxy 的构造函数。 ↩︎
  2. 在 interface 旁边放一个 Stub 实现,它实现 BarService 接口,并有一个传入远程 BarService 实例的构造函数 ↩︎

dubbo配置与Springboot整合的三种方式

SpringBoot与dubbo整合的三种方式:

  1. 导入dubbo-starter,在application.properties配置属性,使用@Service【暴露服务】使用@Reference【引用服务】

  2. 保留dubbo xml配置文件;

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
         http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
         http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
       
     <!-- 1、指定当前服务/应用的名字(同样的服务名字相同,不要和别的服务同名) -->
     <dubbo:application name="boot-user-service-provider"></dubbo:application>
       	
     <!-- 2、指定注册中心的位置 -->
     <!-- <dubbo:registry address="zookeeper://127.0.0.1:2181"></dubbo:registry> -->
     <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"></dubbo:registry>
       	
     <!-- 3、指定通信规则(通信协议?通信端口) -->
     <dubbo:protocol name="dubbo" port="20882"></dubbo:protocol>
       	
     <!-- 4、暴露服务   ref:指向服务的真正的实现对象 -->
     <dubbo:service interface="com.atguigu.gmall.service.UserService" 
         ref="userServiceImpl01" timeout="1000" version="1.0.0">
         <dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method>
     </dubbo:service>
       	
     <!--统一设置服务提供方的规则  -->
     <dubbo:provider timeout="1000"></dubbo:provider>
       	
       	
     <!-- 服务的实现 -->
     <bean id="userServiceImpl01" class="com.atguigu.gmall.service.impl.UserServiceImpl"></bean>
       	
       	
     <!-- 连接监控中心 -->
     <dubbo:monitor protocol="registry"></dubbo:monitor>
       	
    </beans>
    

    导入dubbo-starter,使用@ImportResource导入dubbo的配置文件即可

  3. 使用注解API的方式:

    将每一个组件手动创建到容器中,让dubbo来扫描其他的组件

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    
    @Configuration
    public class MyDubboConfig {
       	
     @Bean
     public ApplicationConfig applicationConfig() {
         ApplicationConfig applicationConfig = new ApplicationConfig();
         applicationConfig.setName("boot-user-service-provider");
         return applicationConfig;
     }
       	
     //<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"></dubbo:registry>
     @Bean
     public RegistryConfig registryConfig() {
         RegistryConfig registryConfig = new RegistryConfig();
         registryConfig.setProtocol("zookeeper");
         registryConfig.setAddress("127.0.0.1:2181");
         return registryConfig;
     }
       	
     //<dubbo:protocol name="dubbo" port="20882"></dubbo:protocol>
     @Bean
     public ProtocolConfig protocolConfig() {
         ProtocolConfig protocolConfig = new ProtocolConfig();
         protocolConfig.setName("dubbo");
         protocolConfig.setPort(20882);
         return protocolConfig;
     }
       	
     /**
      *<dubbo:service interface="com.atguigu.gmall.service.UserService" 
         ref="userServiceImpl01" timeout="1000" version="1.0.0">
         <dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method>
     </dubbo:service>
      */
     @Bean
     public ServiceConfig<UserService> userServiceConfig(UserService userService){
         ServiceConfig<UserService> serviceConfig = new ServiceConfig<>();
         serviceConfig.setInterface(UserService.class);
         serviceConfig.setRef(userService);
         serviceConfig.setVersion("1.0.0");
       		
         //配置每一个method的信息
         MethodConfig methodConfig = new MethodConfig();
         methodConfig.setName("getUserAddressList");
         methodConfig.setTimeout(1000);
       		
         //将method的设置关联到service配置中
         List<MethodConfig> methods = new ArrayList<>();
         methods.add(methodConfig);
         serviceConfig.setMethods(methods);
       		
         //ProviderConfig
         //MonitorConfig
       		
         return serviceConfig;
     }
    }
    

注意<dubbo:service> 对应@Service注解

区别是使用<dubbo:service>可以为每个方法进行配置,在老版本使用@Service注解是无法实现的。在新版本中可以这样设置:

1
2
3
4
5
6
7
@Service
@DubboService(methods = { // @DubboService暴露接口
        @Method(name="getUserById",timeout = 3000) // 指定对应method配置
}) // 暴露接口
public class UserServiceImpl implements UserService {
	// 略
}

其中,@DubboService注解是apache dubbo 2.7.0以后才有的。