[]リボンSpringCloudクライアントの設定とグローバル設定をカスタマイズする方法

起因

それはあなたが登録ユーレカへのメタデータのメタデータをマイクロビジネスサービスを設定する必要がありますので、ラインでのグレースケールのテストに基づいてグレーZuulゲートウェイルーティングを達成するために、企業内、このように始まった、とだけ達し自立ルール定義のリボン訪問グレースケールサービスの目的。これは、リボンのiRuleをカスタマイズする必要があります、グレースケール要求は唯一の良いルール現像後のグレーラベルメタデータ、カスタムのiRuleを有するマイクロビジネス・サービスでサポートされますが、質問はのiRuleにルールを設定する方法でありますリボンクライアントまたはグローバル効果。

使用春の雲Dalston.SR5バージョン

その中で、公式文書、実際に私たちは、クライアントを設定するか、デフォルトを変更する方法については、いくつかの方法が与えられているが、なぜの使用を説明していません

これらの行の次の分析:

  • 春クラウドリボンがそれを理解し始めたときに自動的に設定する方法の簡単な分析は、春の豆に取り付けられています
  • 春クラウドリボンクライアントの遅延読み込み
  • 春クラウドリボンクライアントの設定がロードされているが、それはグローバルコンフィギュレーションとクライアントの構成が含まれています
  • クライアントの設定、グローバル設定をカスタマイズする方法
  • 公式文書は、検討事項のいくつかを説明しました


春クラウドリボン自動設定

ネットフリックスの現在のバージョンはすべて自動的に設定されているspring-cloud-netflix-core-xxx.jar彼らによればMETA-INF/spring.factories、既知の構成では、春の雲リボン自動設定クラスRibbonAutoConfiguration


RibbonAutoConfiguration

@Configuration
@ConditionalOnClass({ IClient.class, RestTemplate.class, AsyncRestTemplate.class, Ribbon.class})
@RibbonClients
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties(RibbonEagerLoadProperties.class)
public class RibbonAutoConfiguration {

    // 所有针对某个RibbonClient指定的配置
    @Autowired(required = false)
    private List<RibbonClientSpecification> configurations = new ArrayList<>();
    
    // ribbon是否懒加载的配置文件
    @Autowired
    private RibbonEagerLoadProperties ribbonEagerLoadProperties;

    // Spring会给每个RibbonClient创建独立的ApplicationContext上下文
    // 并在其上下文中创建RibbonClient对应的Bean:如IClient、ILoadbalancer等
    @Bean
    public SpringClientFactory springClientFactory() {
        SpringClientFactory factory = new SpringClientFactory();
        factory.setConfigurations(this.configurations);
        return factory;
    }

    // Spring创建的带负载均衡功能的Client,会使用SpringClientFactory创建对应的Bean和配置
    @Bean
    @ConditionalOnMissingBean(LoadBalancerClient.class)
    public LoadBalancerClient loadBalancerClient() {
        return new RibbonLoadBalancerClient(springClientFactory());
    }

    // 到Spring environment中加载针对某个Client的Ribbon的核心接口实现类
    @Bean
    @ConditionalOnMissingBean
    public PropertiesFactory propertiesFactory() {
        return new PropertiesFactory();
    }
    
    // 如果不是懒加载,启动时就使用RibbonApplicationContextInitializer加载并初始化客户端配置
    @Bean
    @ConditionalOnProperty(value = "ribbon.eager-load.enabled", matchIfMissing = false)
    public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
        return new RibbonApplicationContextInitializer(springClientFactory(),
                ribbonEagerLoadProperties.getClients());
    }

    ......
}

上記のRibbonAutoConfigurationビーン主に次のカテゴリに分かれて作成しました:

  • リボンクライアントとアクセス設定のための環境を作成します。
    • SpringClientFactory:リボンクライアントはそれぞれ別個SpringアプリケーションコンテキストのApplicationContextを作成し、これに対応するコア構成および実装クラスインターフェースリボンをロードします
    • PropertiesFactory:リボンクライアント・インターフェース・クラスの春的環境の環境コア構成から取得し、インスタンス化するために使用
  • 作成RibbonLoadBalancerClientとspringClientFactory注射、対応する設定と実装クラスを派生しやすい、RibbonLoadBalancerClientそれは春であるLoadBalancerClientインタフェースの実装クラス、execute()メソッドは、クライアント・ロード・バランシングを提供します
  • レイジーロード関連
    • RibbonEagerLoadProperties:遅延ロードの設定項目のプロパティ、あなたは、遅延ロードするかどうかを遅延読み込みを指定することができますし、どのないクライアント
    • RibbonApplicationContextInitializer:RibbonClientは、スタートアップコンフィギュレーション(非遅延ロード)初期化子でロードします

あなたは、コンテキストおよび構成情報RibbonClientをロードしないデフォルトの起動プロセスを参照してください、しかし、ロードすることができた場合にのみ、その遅延読み込みの使用


春クラウドRibbonClient遅延ロード

使用される場合にのみロードされているので、Zuulへのゲートウェイは、例えば、その中にRibbonRoutingFilter作成RibbonCommandは、リボンロードバランシングを含みます

//## RibbonRoutingFilter  Zuul负责路由的Filter
public class RibbonRoutingFilter extends ZuulFilter {

    @Override
    public Object run() {
        RequestContext context = RequestContext.getCurrentContext();
        this.helper.addIgnoredHeaders();
        try {
            RibbonCommandContext commandContext = buildCommandContext(context);
            ClientHttpResponse response = forward(commandContext);
            setResponse(response);
            return response;
        }
        catch (ZuulException ex) {
            throw new ZuulRuntimeException(ex);
        }
        catch (Exception ex) {
            throw new ZuulRuntimeException(ex);
        }
    }

    protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception {
        Map<String, Object> info = this.helper.debug(context.getMethod(),
                context.getUri(), context.getHeaders(), context.getParams(),
                context.getRequestEntity());

        // 使用ribbonCommandFactory创建RibbonCommand
        RibbonCommand command = this.ribbonCommandFactory.create(context);
        try {
            ClientHttpResponse response = command.execute();
            this.helper.appendDebug(info, response.getStatusCode().value(),
                    response.getHeaders());
            return response;
        }
        catch (HystrixRuntimeException ex) {
            return handleException(info, ex);
        }
    }
}

実行ではRibbonRoutingFilter#run()、ルーティングが実行するforward()方法を、ここに原因であるHystrixCommand ribbonCommandFactoryの使用は、この方法では上、リボンクライアントの遅延ロードをRibbonCommandを作成して、内部コールのバランスをとるの実行リボン負荷、ここで我々は見てHttpClientRibbonCommandFactory実装クラス

//## org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory
public class HttpClientRibbonCommandFactory extends AbstractRibbonCommandFactory {
    @Override
    public HttpClientRibbonCommand create(final RibbonCommandContext context) {
        ZuulFallbackProvider zuulFallbackProvider = getFallbackProvider(context.getServiceId());
        final String serviceId = context.getServiceId();
        // 通过SpringClientFactory获取IClient接口实例
        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));
    }
}

作成RibbonLoadBalancingHttpClient中のロジックをSpringClientFactory#getClient(serviceId, RibbonLoadBalancingHttpClient.class)次のように、:

  • SpringClientFactory#のgetInstance(名前、clientClass)
    • NamedContextFactory#のgetInstance(名前、タイプ):
      • その後、提供されたデフォルトの設定は統一レジスタクラスRibbonClientConfigurationを含み、作成したのcreateContext()、またはRibbonClient @、RibbonClients @設定されたクラス・ロジック(defaultConfiguration = XXX)を呼び出さない場合のApplicationContextに対応して、クライアントを取得します
      • IClientConfigで作成するための反射のない使用がない場合、ApplicationContextの例のタイプを取得し、設定します

終了RibbonClient基本的に遅延読み込みが完了すると、RibbonClientは、コア・インタフェースの他の実装に対応するのApplicationContextを取得し続けることができ、これらのクラスは、に従って実装されているカスタムデフォルト/グローバル/クライアントの作成した構成

//## org.springframework.cloud.netflix.ribbon.SpringClientFactory
public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {
    static final String NAMESPACE = "ribbon";

    public SpringClientFactory() {
        super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
    }
    
    /**
     * Get the rest client associated with the name.
     * @throws RuntimeException if any error occurs
     */
    public <C extends IClient<?, ?>> C getClient(String name, Class<C> clientClass) {
        return getInstance(name, clientClass);
    }
    
    // name代表当前Ribbon客户端,type代表要获取的实例类型,如IClient、IRule
    @Override
    public <C> C getInstance(String name, Class<C> type) {
        // 先从父类NamedContextFactory中直接从客户端对应的ApplicationContext中获取实例
        // 如果没有就根据IClientConfig中的配置找到具体的实现类,并通过反射初始化后放到Client对应的ApplicationContext中
        C instance = super.getInstance(name, type);
        if (instance != null) {
            return instance;
        }
        IClientConfig config = getInstance(name, IClientConfig.class);
        return instantiateWithConfig(getContext(name), type, config);
    }
    
    // 使用IClientConfig实例化
    static <C> C instantiateWithConfig(AnnotationConfigApplicationContext context,
                                        Class<C> clazz, IClientConfig config) {
        C result = null;
        try {
            // 通过以IClientConfig为参数的构造创建clazz类实例
            Constructor<C> constructor = clazz.getConstructor(IClientConfig.class);
            result = constructor.newInstance(config);
        } catch (Throwable e) {
            // Ignored
        }
        
        // 如果没创建成功,使用无惨构造
        if (result == null) {
            result = BeanUtils.instantiate(clazz);
            
            // 调用初始化配置方法
            if (result instanceof IClientConfigAware) {
                ((IClientConfigAware) result).initWithNiwsConfig(config);
            }
            
            // 处理自动织入
            if (context != null) {
                context.getAutowireCapableBeanFactory().autowireBean(result);
            }
        }
        return result;
    }
    
}


//## 父类 org.springframework.cloud.context.named.NamedContextFactory
public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
        implements DisposableBean, ApplicationContextAware {
    // 维护Ribbon客户端对应的ApplicationContext上下文
    private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();

    // 维护Ribbon客户端的@Configuration配置类
    private Map<String, C> configurations = new ConcurrentHashMap<>();

    private ApplicationContext parent;

    private Class<?> defaultConfigType;  // 默认配置类为 RibbonClientConfiguration
    private final String propertySourceName;  // 默认为 ribbon
    private final String propertyName;  // 默认读取RibbonClient名的属性为ribbon.client.name

    public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
            String propertyName) {
        this.defaultConfigType = defaultConfigType;
        this.propertySourceName = propertySourceName;
        this.propertyName = propertyName;
    }

    // 如果包含Client上下文直接返回
    // 如果不包含,调用createContext(name),并放入contexts集合
    protected AnnotationConfigApplicationContext getContext(String name) {
        if (!this.contexts.containsKey(name)) {
            synchronized (this.contexts) {
                if (!this.contexts.containsKey(name)) {
                    this.contexts.put(name, createContext(name));
                }
            }
        }
        return this.contexts.get(name);
    }

    // 创建名为name的RibbonClient的ApplicationContext上下文
    protected AnnotationConfigApplicationContext createContext(String name) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        
        // configurations集合中是否包含当前Client相关配置类,包含即注入到ApplicationContext
        if (this.configurations.containsKey(name)) {
            for (Class<?> configuration : this.configurations.get(name)
                    .getConfiguration()) {
                context.register(configuration);
            }
        }
        
        //configurations集合中是否包含default.开头的通过@RibbonClients(defaultConfiguration=xxx)配置的默认配置类
        for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
            if (entry.getKey().startsWith("default.")) {
                for (Class<?> configuration : entry.getValue().getConfiguration()) {
                    context.register(configuration);
                }
            }
        }
        
        // 注册PropertyPlaceholderAutoConfiguration、RibbonClientConfiguration
        context.register(PropertyPlaceholderAutoConfiguration.class,
                this.defaultConfigType);
        // 添加 ribbon.client.name=具体RibbonClient name的enviroment配置       
        context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
                this.propertySourceName,
                Collections.<String, Object> singletonMap(this.propertyName, name)));
        
        // 设置父ApplicationContext,这样可以使得当前创建的子ApplicationContext可以使用父上下文中的Bean
        if (this.parent != null) {
            // Uses Environment from parent as well as beans
            context.setParent(this.parent);
        }
        context.refresh();  //刷新Context
        return context;
    }

    public <T> T getInstance(String name, Class<T> type) {
        AnnotationConfigApplicationContext context = getContext(name);
        if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
                type).length > 0) {
            return context.getBean(type);
        }
        return null;
    }   
}

上記のApplicationContext各RibbonClientの作成においてより重要であるcreateContext(name)@Configuration実装クラスは、それが分析の焦点を取るに従ってたリボンインターフェイスコンフィギュレーションクラスを作成するために、コアロジックを含む方法であって、(リボンインタフェースは、コアを説明する参照

そして、createContext(name)あなたは現在のコンテキストリボンクライアントに関連する方法、および注入の設定クラスを作成するときに、デフォルトの設定のクラスに加えてRibbonClientConfiguration、このようなリボンクライアントの設定クラスのデフォルトのグローバルコンフィギュレーションクラスとしてハードコーディングされた、およびその他の構成クラスは、ですが、また、どのように構成する方法それは?


春クラウドRibbonClientロードする設定は、グローバルコンフィギュレーションおよびクライアントの構成が含まれています

RibbonClient対応のApplicationContextを作成し、利用可能なすべてのコンフィギュレーションの設定クラスを登録

//## org.springframework.cloud.context.named.NamedContextFactory#createContext()
protected AnnotationConfigApplicationContext createContext(String name) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    
    // 1、注册专门为RibbonClient指定的configuration配置类,@RibbonClient注解
    if (this.configurations.containsKey(name)) {
        for (Class<?> configuration : this.configurations.get(name)
                .getConfiguration()) {
            context.register(configuration);
        }
    }
    
    // 2、将为所有RibbonClient的configuration配置类注册到ApplicationContext
    for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
        if (entry.getKey().startsWith("default.")) {
            for (Class<?> configuration : entry.getValue().getConfiguration()) {
                context.register(configuration);
            }
        }
    }
    
    // 3、注册defaultConfigType,即Spring的默认配置类 RibbonClientConfiguration
    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);
    }
    context.refresh();  // 刷新上下文
    return context;
}

上記ロジックは、ApplicationContextの設定、コンテキストのために特別に調製され、構成クラスに関連するレジスタ3リボン場所から見た構成RibbonClientの目的を達成するために、インタフェースリボンコア構成クラスに従って、すなわちを実装するクラスを作成することができます

  1. このような構成から得られたマップ構成が指定されたクラスのために特別に配置され、そしてその対応RibbonClient名を登録するのApplicationContextコンテキストに従って
  2. 構成からのこの地図見つかっ開始時。デフォルトは、クラスコンフィギュレーションのすべてのRibbonClientのデフォルトの設定で、それぞれのApplicationContextのコンテキストに登録
  3. 指定されていない場合は、個々の開発者は、最初の二つはデータではないが、また、春の雲デフォルトの設定クラスを登録しましたRibbonClientConfiguration

それからマップクラスコンフィギュレーションデータで構成それで?段階の分析によって、次のステップ

//## RibbonAutoConfiguration
@Autowired(required = false)
private List<RibbonClientSpecification> configurations = new ArrayList<>();

@Bean
public SpringClientFactory springClientFactory() {
    SpringClientFactory factory = new SpringClientFactory();
    factory.setConfigurations(this.configurations);
    return factory;
}

最初はRibbonAutoConfiguration自動設定でクラスを作成することでSpringClientFactory設定されている、@Autowired春のコンテナ内の構成のこのセットRibbonClientSpecificationコレクションは、その後、RibbonClientSpecificationそれが登録されたときにコレクションがありますか?

//## org.springframework.cloud.netflix.ribbon.RibbonClientConfigurationRegistrar
public class RibbonClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        // 1、@RibbonClients注解
        Map<String, Object> attrs = metadata.getAnnotationAttributes(
                RibbonClients.class.getName(), true);
        // 1.1 value是RibbonClient[],遍历针对具体的RibbonClient配置的configuration配置类,并注册
        if (attrs != null && attrs.containsKey("value")) {
            AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
            for (AnnotationAttributes client : clients) {
                registerClientConfiguration(registry, getClientName(client),
                        client.get("configuration"));
            }
        }
        // 1.2 找到@RibbonClients注解的defaultConfiguration,即默认配置
        //     注册成以default.Classname.RibbonClientSpecification为名的RibbonClientSpecification
        if (attrs != null && attrs.containsKey("defaultConfiguration")) {
            String name;
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            } else {
                name = "default." + metadata.getClassName();
            }
            registerClientConfiguration(registry, name,
                    attrs.get("defaultConfiguration"));
        }
        
        // 2、@RibbonClient注解
        // 注册某个具体Ribbon Client的configuration配置类
        Map<String, Object> client = metadata.getAnnotationAttributes(
                RibbonClient.class.getName(), true);
        String name = getClientName(client);
        if (name != null) {
            registerClientConfiguration(registry, name, client.get("configuration"));
        }
    }

    private String getClientName(Map<String, Object> client) {
        if (client == null) {
            return null;
        }
        String value = (String) client.get("value");
        if (!StringUtils.hasText(value)) {
            value = (String) client.get("name");
        }
        if (StringUtils.hasText(value)) {
            return value;
        }
        throw new IllegalStateException(
                "Either 'name' or 'value' must be provided in @RibbonClient");
    }

    private void registerClientConfiguration(BeanDefinitionRegistry registry,
            Object name, Object configuration) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(RibbonClientSpecification.class);
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        registry.registerBeanDefinition(name + ".RibbonClientSpecification",
                builder.getBeanDefinition());
    }
}

図から分かるように、クラスの構成設定のセットです@RibbonClient@RibbonClients、それぞれ、注釈の設定、特定のRibbonClient構成にし、デフォルトの設定のデフォルト

要約すると、リボン@Configuration関連の方法を設定クラスがロードされます

  1. 対応AnnotationConfigApplicationContext RibbonClientを作成した後、開始するために記載@RibbonClient及び@RibbonClientsもしあれば、クラスに対応する現在の設定を見つけるために、ロードされた構成注釈RibbonClient名を設定し、コンテキストレジスタに
  2. そしてに従って設定設定を見つける@RibbonClients注釈ロードデフォルト。以降、コンテキストレジスタに、もしあれば、デフォルトの設定のクラスを
  3. 最後に登録春の雲のデフォルト RibbonClientConfiguration


これは、ApplicationContextの論理的なコンテキストを作成する方法を言いますと、登録リボンクライアントの設定に関連するクラスは、リボンを使用しますコンフィギュレーションクラス、決定に関連するRibbonClient IClientConfigなタイムアウトとして、リボンのクライアント構成情報をロードするために、クライアントの設定に関連しますその他の構成、特にインタフェースの実装クラスを作成しているのコアは、春の雲からデフォルトで登録することができますRibbonClientConfiguration詳しく見てみ


RibbonClientConfiguration負荷および作成するために、コアリボンインターフェイスクラスを設定

//## org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration
@Import({OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {

    @Value("${ribbon.client.name}")
    private String name = "client";

    // TODO: maybe re-instate autowired load balancers: identified by name they could be
    // associated with ribbon clients

    @Autowired
    private PropertiesFactory propertiesFactory;

    @Bean
    @ConditionalOnMissingBean
    public IClientConfig ribbonClientConfig() {
        DefaultClientConfigImpl config = new DefaultClientConfigImpl();
        config.loadProperties(this.name);
        return config;
    }

    @Bean
    @ConditionalOnMissingBean
    public IRule ribbonRule(IClientConfig config) {
        if (this.propertiesFactory.isSet(IRule.class, name)) {
            return this.propertiesFactory.get(IRule.class, config, name);
        }
        ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
        rule.initWithNiwsConfig(config);
        return rule;
    }

上記のコード断片の唯一の傍受は、リボン関連する与えIClientConfig、クライアントの構成と1つのコア・インタフェースをIRuleクラスが設定をロードするために作成された方法を実現すると

IClientConfig

IClientConfigリボンは、クライアントの設定インターフェイスは、あなたが最初に見ることができます作成されDefaultClientConfigImpl、デフォルトの実装クラスを、その後、config.loadProperties(this.name)現在のクライアント関連の設定を読み込みます

//## com.netflix.client.config.DefaultClientConfigImpl#loadProperties()
/**
 * Load properties for a given client. It first loads the default values for all properties,
 * and any properties already defined with Archaius ConfigurationManager.
 */
@Override
public void loadProperties(String restClientName){
    enableDynamicProperties = true;
    setClientName(restClientName);
    
    // 1、使用Netflix Archaius的ConfigurationManager从Spring env中加载“ribbon.配置项”这类默认配置
    //   如没加载到有默认静态配置
    loadDefaultValues();
    
    // 2、使用Netflix Archaius的ConfigurationManager从Spring env中加载“client名.ribbon.配置项”这类针对某个Client的配置信息
    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春的環境が含まれている場合は、デフォルトの静的構成の「リボンを。CI」これらは来るが、すべてのクライアント構成のためにロードされます、「クライアントがあります.ribbonに名前を付けます。クライアントの構成情報の設定項目」タイプがロードされてきます

次のように静的な構成は以下のとおりです。


RibbonClientコアインタフェースの実装クラスがロードされ、作成するように構成されています

上にあるIClientCOnfigどのように設定項目すでにコアクラス現在RibbonClient利用設定インタフェースが含まれている理由に立って、ロードされたが、ここでは春クラウドロジックの独自の実装を定義しています

@Autowired
private PropertiesFactory propertiesFactory;

@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
    // 查看propertiesFactory是否有关于当前接口的配置,如有就使用,并创建实例返回
    if (this.propertiesFactory.isSet(IRule.class, name)) {
        return this.propertiesFactory.get(IRule.class, config, name);
    }
    
    // spring cloud 默认配置
    ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
    rule.initWithNiwsConfig(config);
    return rule;
}

のは、見てみましょうPropertiesFactoryロジック

public class PropertiesFactory {
    @Autowired
    private Environment environment;

    private Map<Class, String> classToProperty = new HashMap<>();

    public PropertiesFactory() {
        classToProperty.put(ILoadBalancer.class, "NFLoadBalancerClassName");
        classToProperty.put(IPing.class, "NFLoadBalancerPingClassName");
        classToProperty.put(IRule.class, "NFLoadBalancerRuleClassName");
        classToProperty.put(ServerList.class, "NIWSServerListClassName");
        classToProperty.put(ServerListFilter.class, "NIWSServerListFilterClassName");
    }

    // 查看当前clazz是否在classToProperty管理的几个核心接口之一
    // 如是,查看Spring environment中是否能找到 “clientName.ribbon.核心接口配置项”的配置信息
    public boolean isSet(Class clazz, String name) {
        return StringUtils.hasText(getClassName(clazz, name));
    }

    public String getClassName(Class clazz, String name) {
        if (this.classToProperty.containsKey(clazz)) {
            String classNameProperty = this.classToProperty.get(clazz);
            String className = environment.getProperty(name + "." + NAMESPACE + "." + classNameProperty);
            return className;
        }
        return null;
    }

    // 也是先调用getClassName()获取Spring enviroment中配置的核心接口实现类名
    // 再使用IClientConfig配置信息创建其实例
    @SuppressWarnings("unchecked")
    public <C> C get(Class<C> clazz, IClientConfig config, String name) {
        String className = getClassName(clazz, name);
        if (StringUtils.hasText(className)) {
            try {
                Class<?> toInstantiate = Class.forName(className);
                return (C) instantiateWithConfig(toInstantiate, config);
            } catch (ClassNotFoundException e) {
                throw new IllegalArgumentException("Unknown class to load "+className+" for class " + clazz + " named " + name);
            }
        }
        return null;
    }
}

したがって、上記作成するためにIRule論理インターフェイスクラスを

  • 最初によってpropertiesFactoryビュー春的環境が存在する場合、リターンのインスタンスを作成し、現在のiRuleコアリボンクライアントインタフェースの実装クラスの構成情報に設定されている(構成形式:clientName.ribbon.NFLoadBalancerRuleClassName =のiRule具象実装クラス)
  • そうでない場合には、その中には直接使用NetflixはありませんDefaultClientConfigImpl静的な設定が、デフォルトの実装クラス春クラウドカスタムは、取るIRuleルールはインターフェースですZoneAvoidanceRule

要約:

まず、ApplicationContextのコンテキストRibbonClientを作成し、コンフィギュレーションを使用しているコンフィギュレーションクラスを決定

RibbonClients登録グローバルデフォルトの設定クラス@ 1、

クラスのクライアント構成のRibbonClient登録@ 2、

3、春の雲のデフォルト設定のクラスRibbonClientConfiguration

确定配置类后就是加载Client相关的IClientConfig配置信息,并创建核心接口实现类

如果没有自定义全局/客户端配置类,那么就是使用RibbonClientConfiguration,而其规则是

对于超时等配置(除核心接口实现类以外):使用Netflix的配置逻辑,通过 ribbon.xxx 作为默认配置,以 clientName.ribbon.xxx 作为客户端定制配置

对于核心接口实现类配置:客户端定制配置仍然使用 clientName.ribbon.xxx,但默认配置是Spring Cloud在RibbonClientConfiguration方法中写死的默认实现类

已经知道大概的逻辑了,下面就看看具体如何自定义Client配置、全局配置


如何自定义RibbonClient配置、全局配置

这部分在Spring Cloud官方reference中有说明 16.2 Customizing the Ribbon Client

大致意思如下:

  • 一部分配置(非核心接口实现类的配置)可以使用Netflix原生API提供的方式,即使用如 .ribbon. * 的方式配置,具体有哪些配置项,可以参考 com.netflix.client.config.CommonClientConfigKey

  • 如果想比较全面的控制RibbonClient并添加一些额外配置,可以使用 @RibbonClient@RibbonClients 注解,并配置一个配置类,如上的 FooConfiguration

    • @RibbonClient(name = "foo", configuration = FooConfiguration.class) 是针对名为 foo 的RibbonClient的配置类,也可以使用@RibbonClients({@RibbonClient数组}) 的形式给某几个RibbonClient设置配置类

    • @RibbonClients( defaultConfiguration = { xxx.class } ) 是针对所有RIbbonClient的默认配置

      • 官方文档说 FooConfiguration配置类 必须是@Configuration的,这样就必须注意,SpringBoot主启动类不能扫描到FooConfiguration,否则针对某个RibbonClient的配置就会变成全局的,原因是在创建每个RibbonClient时会为其创建ApplicationContext上下文,其parent就是主启动类创建的ApplicationContext,子ApplicationContext中可以使用父ApplicationContext中的Bean,且创建Bean时都使用了@ConditionalOnMissingBean,所以FooConfiguration如果被主启动类的上下文加载,且创建了比如IRule的实现类,在某个RIbbonClient创建其子ApplicationContext并@Bean想创建其自定义IRule实现类时,会发现parent ApplicationContext已经存在,就不会创建了,配置就失效了

        但在我的实验中,即使FooConfiguration不加@Configuration注解也可以加载为RibbonClient的配置,且由于没有@Configuration了,也不会被主启动类扫描到

所以主要分成2种配置:

(1)超时时间等静态配置,使用 ribbon.* 配置所有Client,使用 .ribbon. * 配置某个Client

(2)使用哪种核心接口实现类配置,使用@RibbonClients注解做默认配置,使用@RibbonClient做针对Client的配置(注意@Configuration不要被SpringBoot主启动类扫描到的问题)

おすすめ

転載: www.cnblogs.com/trust-freedom/p/11216280.html