【你好Ribbon】四:Ribbon核心包-IClientConfig源码分析篇

每日一句

没有礁石,就没有美丽的浪花;没有挫折,就没有壮丽的人生


我们接着上一篇继续聊,上一篇讲的是应用 这一篇我们讲原理 讲如何实现。在阅读本文之前 希望你已对Archaius有所了解

属性加载

   IClientConfig coredy1 = DefaultClientConfigImpl.getClientConfigWithDefaultValues("coredy1");
   IClientConfig coredy = DefaultClientConfigImpl.getClientConfigWithDefaultValues("coredy");
   System.out.println(coredy.get(IClientConfigKey.Keys.ConnectTimeout));
   System.out.println(coredy1.get(IClientConfigKey.Keys.ConnectTimeout));

还是把上一篇的例子拿过来,我们跟进getClientConfigWithDefaultValues方法。

 public static DefaultClientConfigImpl getClientConfigWithDefaultValues(String clientName) {
 //这里使用的是默认的name_space
    return getClientConfigWithDefaultValues(clientName, DEFAULT_PROPERTY_NAME_SPACE);
  }

继续跟进getClientConfigWithDefaultValues

public static DefaultClientConfigImpl getClientConfigWithDefaultValues(String clientName, String nameSpace) {
    DefaultClientConfigImpl config = new DefaultClientConfigImpl(nameSpace);
    config.loadProperties(clientName);
    return config;
  }

上面方法做了两件事情 一:实例化一个DefaultClientConfigImpl对象 我们说过 DefaultClientConfigImpl是IClientConfig的唯一实现。第二:调用IClientConfig这个接口的loadProperties方法。重点看一下loadProperties方法

 public void loadProperties(String restClientName) {
    //启用动态属性 如果开启属性将具有动态 性  这里也是唯一一处将该值设置成true的地方
    //所以如果仅仅是默认值 不支持动态属性的 
    enableDynamicProperties = true;
    //设置clientName
    setClientName(restClientName);
    //加载默认属性
    loadDefaultValues();
    //这里可以看到底层的配置还是通过 Archaius来配置的 所以我们把配置写在classpath的config.properties中是生效的
    //对于subset这个方法 举个例子可能会更清楚 例如我们在config文件中的配置是 coredy.ribbon.ReadTimeout
    //我们调用subset(”coredy“) 那就会给我们返回所有以coredy开头的Configuration
    Configuration props = ConfigurationManager.getConfigInstance().subset(restClientName);
    //遍历得到的Configuration 将属性放到全局的配置里面properties
    for (Iterator<String> keys = props.getKeys(); keys.hasNext(); ) {
      String key = keys.next();
      String prop = key;
      try {
        if (prop.startsWith(getNameSpace())) {
          prop = prop.substring(getNameSpace().length() + 1);
        }
        //特别注意这里是使用getStringValue(props, key)来获取值得
        //意为着 你配置的属性是a,b,c 那么最终在全局配置properties里面的值也是a,b,c
        setPropertyInternal(prop, getStringValue(props, key));
      } catch (Exception ex) {
        throw new RuntimeException(String.format("Property %s is invalid", prop));
      }
    }
  }
  • 只有调用这个loadProperties方法 才会把enableDynamicProperties 这个属性设置成true 所以只有默认属性的时候 是不具有动态性的
  • loadDefaultValues() 所以在我们通过DefaultClientConfigImpl静态方法获取实例的时候 默认是要加载默认配置的
  • 加载客户端属性 (通过subset(restClientName)获取配置 然后放在全局配置) 的时候使用的是getStringValue() 这个方法 它保证了你在配置文件配置的是什么样子 那么加载出来的就是什么样子(注意和下面我们要说的加载默认配置的时候的区别)

继续跟进setPropertyInternal 设置客户端属性 (我们把非默认属性统一称为客户端属性)

String stringValue = (value == null) ? "" : String.valueOf(value);
//直接将客户端属性放入全局属性中
    properties.put(propName, stringValue);
    //判断是否开启了动态属性 这里是开启了的
    if (!enableDynamicProperties) {
      return;
    }
    /*这里得到一个key值  这个key就会根据我们设置的namespace和clientName来决定
     * coredy.ribbon.ReadTimeout or ribbon.ReadTimeout 客户端配置的key和默认key
	 * (clientName == null) ? getDefaultPropName(propName) : getInstancePropName(clientName, propName);	
     **/
    String configKey = getConfigKey(propName);
    //获取动态属性 注册回调函数
    final DynamicStringProperty prop = DynamicPropertyFactory.getInstance().getStringProperty(configKey, null);
    //属性更改会 执行回调 更改全局配置properties里面对应的值
    Runnable callback = new Runnable() {
      @Override
      public void run() {
        String value = prop.get();
        if (value != null) {
          properties.put(propName, value);
        } else {
          properties.remove(propName);
        }
      }
      @Override
      public boolean equals(Object other) {
        if (other == null) {
          return false;
        }
        if (getClass() == other.getClass()) {
          return toString().equals(other.toString());
        }
        return false;
      }
      @Override
      public String toString() {
        return propName;
      }
      @Override
      public int hashCode() {
        return propName.hashCode();
      }
    };
    //属性更改会回调 callback
    prop.addCallback(callback);
    dynamicProperties.put(propName, prop);

上面的setPropertyInternal 主要是在处理属性的动态性。如果enableDynamicProperties为false直接返回了。到这里我们的客户端属性就加载进了全局配置中 并且所有的属性都具有动态性。那我们看一下如何加载默认属性的,我们跟进loadDefaultValues方法

//loadDefaultValues方法就不贴出来了 主要是 穷举所有的key 调用下面这个方法
 putDefaultStringProperty(CommonClientConfigKey.ListOfServers, "");

跟进putDefaultStringProperty

 String value = ConfigurationManager.getConfigInstance().getString(
     getDefaultPropName(propName), defaultValue);
 setPropertyInternal(propName, value);

代码很简单 就是获取默认的key值 然后依赖Archaius获取对应key的配置值。但是 但是有一点需要特别注意。这里的 ConfigurationManager.getConfigInstance().getString()方法获取的配置和刚刚上面我们说的getStringValue() 是有区别的。例如我们配置的值是a,b,c 那么getStringValue获取的值是a,b,c 而 getString()获取的却是 a! 所以默认值字符串类型的 配置里面出现逗号那就有问题了。

获取属性

上面我们把属性加载的整个流程跟进了一遍,那下面我们来看一下ribbon是怎么获取属性的。以getPropertyAsString为例 其他的几个方法getPropertyAsBoolean 、getPropertyAsInteger甚至get方法都是一样的。都是依赖getProperty方法进行查找。

    IClientConfig clientConfig = DefaultClientConfigImpl.getClientConfigWithDefaultValues("coredy");
    String propertyAsString = clientConfig.getPropertyAsString(new CommonClientConfigKey<String>("spring") {
    }, null);
    Assert.assertThat(propertyAsString, is("121212"));

上面是一个获取spring属性的例子。我们在实例化一个key的时候 CommonClientConfigKey 泛型是必须要写的。否则会出现IllegalArgumentException异常。 在构造参数中有一个 checkArgument(superclass instanceof ParameterizedType,
“%s isn’t parameterized”, superclass)
; 这样的判断 如果父类不是参数化泛型的实例 就会出异常。

我们继续跟进getPropertyAsString


  //可以看到这个方法什么事情都么有做 只是做了一个类型的转换 获取的逻辑全部委托给了getProperty方法了
  //其他的几个方法也是类似
  public String getPropertyAsString(IClientConfigKey key, String defaultValue) {
       Object rawValue = getProperty(key);
       if (rawValue != null) {
           return String.valueOf(rawValue);
       }
       return defaultValue;
   }


//最终会执行该方法获取属性值
   protected Object getProperty(String key) {
   //如果开启动态属性值
        if (enableDynamicProperties) {
            String dynamicValue = null;
            //先从动态属性缓存里面去找  
            DynamicStringProperty dynamicProperty = dynamicProperties.get(key);
            if (dynamicProperty != null) {
                dynamicValue = dynamicProperty.get();
            }
            //这里有一个小小的细节  为什么要这样写? 还要判断一次 只要动态属性开启 正常情况下动态属性缓存里面基本是有所有的属性的(我们通过属性加载流程可以看到) 
            //那为什么还要从新load一次? 想来想去 有一种情况可能要考虑 我们要获取的key不存在全局配置里面 并且是在我们IClientConfig加载完了之后
            //我们加到配置文件中去的 这种情况下 单纯的 dynamicProperties.get(key);是获取不到 并且兜底的 properties.get(key);也获取不到
            if (dynamicValue == null) {
            //如果找不到 先去客户端配置
                dynamicValue = DynamicProperty.getInstance(getConfigKey(key)).getString();
                if (dynamicValue == null) {
               //如果找不到 找默认配置 
                    dynamicValue = DynamicProperty.getInstance(getDefaultPropName(key)).getString();
                }
            }
            if (dynamicValue != null) {
                return dynamicValue;
            }
        }
        //如果都找不到 或者没有开启动态配置 那么从全局配置properties去找
        return properties.get(key);
    }

在动态属性开启的情况下 有一点细节可能要注意 作者不仅dynamicProperties.get(key);获取属性 在获取不到的情况下 又使用Archaius
的 DynamicProperty.getInstance(getConfigKey(key))去获取了一次 Archaius中的配置。并且是先加载客户端配置 再加载默认配置。这样就避免漏了 后续我们往Archaius中加的配置加载不到的问题。

设置属性

设置属性就非常简单了.直接设置到全局属性里面。

 public <T> DefaultClientConfigImpl set(IClientConfigKey<T> key, T value) {
       properties.put(key.key(), value);
       return this;
   }

这里需要想一下为什么没有想属性加载那么麻烦 考虑属性动态改变的问题。这里是因为属性动态改变是Archauis提供的功能 我们这里手动设置 其实是脱离了Archauis的管理 所以没法做到动态性 也没必要。
这就是为什么在获取属性的时候 最后用return properties.get(key)来兜底,因为 在开启动态配置的情况下 确实有可能出现 properties里面有 dynamicProperties里面没有的情况

总结

IClientConfig 就到这了 ,相对比较简单 了解主要的几个方法 以及大致的属性加载流程就好了

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值