SpringCloud之Ribbon组件配置的读取

spring:
  application:
    name: eshop-microservice-gateway-app
server:
  port: 8080
eureka:
  client:
    defautlZone: http://127.0.0.1:8761/eureka,http://127.0.0.1:8762/eureka
#ribbon的全局配置,对所有ribbon客户端都生效 ribbon:
 MaxAutoRetries: 2 eshop-microservice-user: ribbon: ReadTimeout: 4000 zuul: ignored-services: '*' retryable: true stripPrefix: false routes: userservice: serviceId: eshop-microservice-user path: /api/v1.0/user/** stripPrefix: false testservice: url: http://www.baidu.com/ path: /test/** demoservice: /aaaa/**

 配置文件中Ribbon的配置如下

eshop-microservice-user:
  ribbon: 
    ReadTimeout: 4000

 在springcloud里是怎么读取的,我们先看ribbon的调用入口:

	public HttpClientRibbonCommand create(final RibbonCommandContext context) {
		FallbackProvider zuulFallbackProvider = getFallbackProvider(
				context.getServiceId());
		final String serviceId = context.getServiceId();
		final RibbonLoadBalancingHttpClient client = this.clientFactory
				.getClient(serviceId, RibbonLoadBalancingHttpClient.class);
		client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));

		return new HttpClientRibbonCommand(serviceId, client, context, zuulProperties,
				zuulFallbackProvider, clientFactory.getClientConfig(serviceId));
	}

  这是Zuul组件调用Ribbon的入口,由clientFactory创建RibbonLoadBalancingHttpClient,我们进而去看this.clientFactory.getClient方法,通过步步查看可以看出在创建RibbonLoadBalancingHttpClient的时候创建了Spring容器,然后从容器获取的,创建容器的逻辑如下:

protected AnnotationConfigApplicationContext createContext(String name) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		if (this.configurations.containsKey(name)) {
			for (Class<?> configuration : this.configurations.get(name)
					.getConfiguration()) {
				context.register(configuration);
			}
		}
		for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
			if (entry.getKey().startsWith("default.")) {
				for (Class<?> configuration : entry.getValue().getConfiguration()) {
					context.register(configuration);
				}
			}
		}
		context.register(PropertyPlaceholderAutoConfiguration.class,
				this.defaultConfigType);
		context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
				this.propertySourceName,
				Collections.<String, Object>singletonMap(this.propertyName, name)));
		if (this.parent != null) {
			// Uses Environment from parent as well as beans
			context.setParent(this.parent);
			// jdk11 issue
			// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
			context.setClassLoader(this.parent.getClassLoader());
		}
		context.setDisplayName(generateDisplayName(name));
		context.refresh();
		return context;
	}

  这里面我们主要关心的是红色的代码,里面是在properties集合里添加了一个配置,这个配置我们知道name就是前面的serviceId,这里也就是eshop-microservice-user,但是propertyName具体是啥呢,我们接下来看:

public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {

	static final String NAMESPACE = "ribbon";

	public SpringClientFactory() {
		super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
	}
}

  这是SpringClientFactory的无参构造函数,通过这里我们可以知道propertyName=ribbon.client.name,所以properties集合添加的配置是:ribbon.client.name=eshop-microservice-user,而这到底用在哪里呢?我们可以先看看新创建的这个spring容器到底注入了哪些Configuration,在前面的createContext方法我们看到context.register(PropertyPlaceholderAutoConfiguration.class,this.defaultConfigType);这样一句代码,defaultConfigType就是RibbonClientConfiguration,然后我们看看RibbonClientConfiguration的几个关键地方

@Import({ HttpClientConfiguration.class, OkHttpRibbonConfiguration.class,
		RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class })
public class RibbonClientConfiguration {


	@RibbonClientName
	private String name = "client";

	@Bean
	@ConditionalOnMissingBean
	public IClientConfig ribbonClientConfig() {
		DefaultClientConfigImpl config = new DefaultClientConfigImpl();
		config.loadProperties(this.name);
		config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
		config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
		config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
		return config;
	}

  上面的name属性有RibbonClientName注解,如下

@Value("${ribbon.client.name}")
public @interface RibbonClientName {

}

  这样name的值在容器刷新后被ribbon.client.name替换成eshop-microservice-user,然后注入IClientConfig,ribbonClientConfig里面的逻辑就是初始化ribbon的配置并且加载ribbon的自定义配置。

然后我们看加载配置主要的方法loadProperties:

	public void loadProperties(String restClientName){
        enableDynamicProperties = true;
        setClientName(restClientName);
//这里加载全局的ribbon配置 loadDefaultValues();
//这里加载对应客户端的配置 Configuration props = ConfigurationManager.getConfigInstance().subset(restClientName); 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); } setPropertyInternal(prop, getStringValue(props, key)); } catch (Exception ex) { throw new RuntimeException(String.format("Property %s is invalid", prop)); } } }

在DefaultClientConfigImpl这个实现内部有个地方需要注意:

    protected Object getProperty(String key) {
        if (enableDynamicProperties) {
            String dynamicValue = null;
            DynamicStringProperty dynamicProperty = dynamicProperties.get(key);
            if (dynamicProperty != null) {
                dynamicValue = dynamicProperty.get();
            }
            if (dynamicValue == null) {
                dynamicValue = DynamicProperty.getInstance(getConfigKey(key)).getString();
                if (dynamicValue == null) {
                    dynamicValue = DynamicProperty.getInstance(getDefaultPropName(key)).getString();
                }
            }
            if (dynamicValue != null) {
                return dynamicValue;
            }
        }
        return properties.get(key);
    }

 在DefaultClientConfigImpl的内部有properties和dynamicProperties来存储配置信息,所以读取配置时会先从dynamicProperties中读取,如果没读取到则从properties读,而用户自定义的配置在properties和dynamicProperties中都会存储,所以就有

		config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
		config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
		config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);

  这三个重置默认值的代码并不影响用户自定义的配置。

 

posted @ 2019-10-10 12:54  myTang  阅读(1492)  评论(0)    收藏  举报