jannal(无名小宝)

没有失败,只有缓慢的成功

导航

Dubbo之启动时检查

dubbo版本

  1. dubbo版本2.6.7

启动时检查

  1. 在启动时检查依赖的服务是否可用,缺省会在启动时检查依赖的服务是否可用(默认check="true"),不可用时会抛出异常,阻止 Spring 初始化完成
  2. 以下情况需要关闭
    • 测试时,不关心部分服务。部分服务没有启动,但是不影响当前测试流程
    • 循环依赖,必须有一方先启动
  3. 如果Spring是懒加载或者延迟引用服务,必须关闭check,否则服务临时不可用时,会抛出异常。拿到 null 引用,如果 check="false",总是会返回引用,当服务恢复时,能自动连上

配置示例

  1. Spring方式配置

    1. 关闭某个服务的启动时检查
    <dubbo:reference interface="com.foo.BarService" check="false" />
    
    2. 关闭所有服务的启动时检查
    <dubbo:consumer check="false" />
    
    3. 关闭注册中心启动时检查
    <dubbo:registry check="false" />
    
  2. dubbo.properties方式配置

    # 单个服务配置
    dubbo.reference.com.foo.BarService.check=false
    #强制改变所有 reference 的 check 值,就算配置中有声明,也会被覆盖
    dubbo.reference.check=false
    #设置 check 的缺省值,如果配置中有显式的声明
    dubbo.consumer.check=false
    #订阅失败时允许启动
    dubbo.registry.check=false
    
  3. 通过启动参数

    java -Ddubbo.reference.com.foo.BarService.check=false
    java -Ddubbo.reference.check=false
    java -Ddubbo.consumer.check=false 
    java -Ddubbo.registry.check=false
    

源码分析

  1. 默认情况下(check=true),如果provider没有提供服务,consumer在启动时会报如下错误

    Failed to check the status of the service cn.jannal.dubbo.facade.DemoService. No provider available for the service group0/cn.jannal.dubbo.facade.DemoService:1.0.0 from the url zookeeper://dubbo-zookeeper:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&check=true&dubbo=2.0.2&group=group0&interface=cn.jannal.dubbo.facade.DemoService&methods=sayHello&pid=95169&qos.port=33333&register.ip=172.16.117.33&revision=1.0.0&side=consumer&timestamp=1640577310886&version=1.0.0 to the consumer 172.16.117.33 use dubbo version 2.6.7
    
  2. 报错的地方在ReferenceConfig#createProxy,可以看到,默认check设置为true,如果invoker不可用,在抛出异常。默认调用的是RegistryProtocol#refer

    private T createProxy(Map<String, String> map) {
      	...省略...
        invoker = refprotocol.refer(interfaceClass, urls.get(0));
        ...省略...  
    		Boolean c = check;
        if (c == null && consumer != null) {
            c = consumer.isCheck();
        }
        if (c == null) {
            c = true; // default true
        }
        if (c && !invoker.isAvailable()) {
            // make it possible for consumer to retry later if provider is temporarily unavailable
            initialized = false;
            throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
        }
    	...省略...
    }
    
    
  3. RegistryProtocol#doRefer里创建了RegistryDirectory,RegistryDirectory是一种动态目录服务,实现了NotifyListener,当注册中心服务配置发生变化后,RegistryDirectory 可收到与当前服务相关的变化。收到变更通知后,RegistryDirectory 可根据配置变更信息刷新 Invoker 列表

    MockClusterInvoker#isAvailable里调用的directory
    @Override
    public boolean isAvailable() {
        return directory.isAvailable();
    }  
    
    所以上面的invoker.isAvailable() 本质就是调用RegistryDirectory#isAvailable()
    public boolean isAvailable() {
        if (isDestroyed()) {
            return false;
        }
        Map<String, Invoker<T>> localUrlInvokerMap = urlInvokerMap;
        if (localUrlInvokerMap != null && localUrlInvokerMap.size() > 0) {
            for (Invoker<T> invoker : new ArrayList<Invoker<T>>(localUrlInvokerMap.values())) {
                if (invoker.isAvailable()) {
                    return true;
                }
            }
        }
        return false;
    }  
    

服务分组

  1. 当一个接口有多种实现时,可以用 group 区分。

  2. 配置

    服务
    <dubbo:service group="feedback" interface="com.xxx.IndexService" />
    <dubbo:service group="member" interface="com.xxx.IndexService" />
    引用
    <dubbo:reference id="feedbackIndexService" group="feedback" interface="com.xxx.IndexService" />
    <dubbo:reference id="memberIndexService" group="member" interface="com.xxx.IndexService" />
    任意组:
    <dubbo:reference id="barService" interface="com.foo.BarService" group="*" />
    

多版本

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

  2. 升级步骤

    • 先升级一半提供者为新版本
    • 再将所有消费者升级为新版本
    • 将剩下的一半提供者升级为新版本
  3. 新老版本配置

    <dubbo:service interface="com.foo.BarService" version="1.0.0" />
    <dubbo:service interface="com.foo.BarService" version="2.0.0" />
    
    <dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />
    <dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />
    
  4. 如果不需要区分版本,可以直接

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

posted on 2022-02-08 10:28  jannal  阅读(150)  评论(0编辑  收藏  举报