Loading...
墨滴

lyq

2021/11/05  阅读:34  主题:默认主题

Spring Cloud Netflix Ribbon源码解析(三)

Spring Cloud Netflix Ribbon源码解析(三)

基于Spring Cloud Hoxton.SR9版本


前言

经过了前两篇对Ribbon的介绍,我们基本把整个脉络梳理清楚了。相信读者已经对ribbon的实现原理有个一个整体的了解,如果你还没有看懂,希望你参考前面两篇文章,自己动手亲自跟踪一遍代码,相信你会有不一样的体会。好了,今天我们来重点说一下NamedContextFactory这个类。为什么要介绍这个类呢?因为这个抽象类在spring cloud中是一个十分重要的类,不仅在ribbon中被使用,而且在feign中也有被用到。所以有必要单独写一篇文章来介绍它,以便于后面的学习。那么我们一起来看看吧!

源码分析

NamedContextFactory

public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
  implements DisposableBeanApplicationContextAware 
{
}

这个抽象类实现了ApplicationContextAware接口,那么必然要重写setApplicationContext方法:

@Override
public void setApplicationContext(ApplicationContext parent) throws BeansException {
 this.parent = parent;
}

那么在spring应用上下文启动的时候,必然会为我们注入ApplicationContext对象,有了它,我们就可以做很多事情,比如通过依赖查找获取容器中的某个bean。

我们先看一下这个抽象类中提供了哪些方法,见下图:

通过上图我们可以看到,这个抽象类为我们提供了:

  • createContext方法:用于创建应用上下文
  • getContext方法:用于获取应用上下文
  • getInstance方法:用于查找某个bean实例

其他方法就不一一介绍了。

那么这个抽象类到底有什么作用呢?好吧,直接上答案: 不管是在ribbon中还是在feign中,都是根据服务名,创建一个spring的子应用上下文,比如我们通过restTemplate.getForObject("http://nacos-discovery-provider-sample/echo/" + message, String.class);调用远程服务的时候,这里的nacos-discovery-provider-sample就是对应的服务名,然后存在内存中的map中,以服务名作为key,值为ApplicationContext对象:

private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();

到目前为止,我们知道这么多就够了。那么接下来我们具体看看它的子类,在Ribbon环境下,SpringClientFactory实现了它。

SpringClientFactory

创建客户端、负载均衡器和客户端配置实例的工厂,为每一个客户端创建一个Spring ApplicationContext。可以理解成相当于BeanFactory。在其中持有关于Ribbon的相关配置等信息。

public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification{
   // 获取客户端
   public <C extends IClient<?, ?>> getClient(String name, Class<C> clientClass) {
       ......
   }
   // 获取负载均衡器
   public ILoadBalancer getLoadBalancer(String name) {
       ......
   }
   // 获取客户端配置
   public IClientConfig getClientConfig(String name) {
       ......
   }
   // 获取上下文
   public RibbonLoadBalancerContext getLoadBalancerContext(String serviceId) {
       ......
   }
   
}

SpringClientFacotry的构造函数:

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

super调用父类NamedContextFactory的构造器,将RibbonClientConfiguration.class赋值给defaultConfigTypeNamedContextFactory会为Spring ApplicationContext创建一个子ApplicationContext

public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
  String propertyName)
 
{
 //defaultConfigType=RibbonClientConfiguration.class先记住这个
 this.defaultConfigType = defaultConfigType;
 this.propertySourceName = propertySourceName;
 this.propertyName = propertyName;
}

在调用createContext中有注入defaultConfigType类型的bean,即注入RibbonClientConfiguration

protected AnnotationConfigApplicationContext createContext(String name) {
    ......
    context.register(PropertyPlaceholderAutoConfiguration.class,
      this.defaultConfigType)
;
    ......  
}

RibbonClientConfiguration

Ribbon客户端配置,提供默认的负载均衡算法等配置。第一次调用接口的时候,才会触发该配置类。这里先有个印象,重点今天的重点不是它,但是要记住,如果你在什么都不配置的情况下,框架会为你提供以下这些默认配置。

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@Import({ HttpClientConfiguration.class, OkHttpRibbonConfiguration.class,
      RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class })
public class RibbonClientConfiguration {
    // 默认的IRule,默认为ZoneAvoidanceRule
    // 使用了ConditionalOnMissingBean,当自定义实现bean的时候,默认的配置就不会被加载了。
    @Bean
    @ConditionalOnMissingBean
    public IRule ribbonRule(IClientConfig config) {
        // propertiesFactory是通过配置设置IRule实现的,
        // 比如在application.yml文件中设置。
        if (this.propertiesFactory.isSet(IRule.class, name)) {
          return this.propertiesFactory.get(IRule.class, config, name);
       }
       ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
       rule.initWithNiwsConfig(config);
       return rule;
    }
     
    // 默认的IPing实现,默认为: DummyPing
    @Bean
    @ConditionalOnMissingBean
    public IPing ribbonPing(IClientConfig config) {
        if (this.propertiesFactory.isSet(IPing.class, name)) {
            return this.propertiesFactory.get(IPing.class, config, name);
        }
        return new DummyPing();
    }
    
    // 默认的ILoadBalancer实现,默认为:ZoneAwareLoadBalancer
    @Bean
    @ConditionalOnMissingBean
    public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
        ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
          IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
       if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
           return this.propertiesFactory.get(ILoadBalancer.class, config, name);
       }
       // 将IRule和IPing作为参数
       return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
         serverListFilter, serverListUpdater);
    }
    
    // Ribbon负载均衡上下文
    @Bean
    @ConditionalOnMissingBean
    public RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer,
        IClientConfig config, RetryHandler retryHandler) {
        return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler);
    }
}

我们继续回到SpringClientFactory,看看它的构造函数是在哪里被调用的,如果看过我写的ribbon系列第一篇的读者,就会知道它是在RibbonAutoConfiguration配置类中被创建并注册到spring上下文中:

RibbonAutoConfiguration

@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(
  name 
"org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
  AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties(
{ RibbonEagerLoadProperties.class,
  ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration 
{

 //这里很关键,后面我们会说到,留个印象
 @Autowired(required = false)
 private List<RibbonClientSpecification> configurations = new ArrayList<>();

 @Autowired
 private RibbonEagerLoadProperties ribbonEagerLoadProperties;

 @Bean
 public HasFeatures ribbonFeature() {
  return HasFeatures.namedFeature("Ribbon", Ribbon.class);
 }

 //传入configurations基金和,创建实例对象,并将其添加到spring应用上下文中
 @Bean
 @ConditionalOnMissingBean
 public SpringClientFactory springClientFactory() {
  SpringClientFactory factory = new SpringClientFactory();
  factory.setConfigurations(this.configurations);
  return factory;
 }
 //将SpringClientFactory作为构造器参数传入到RibbonLoadBalancerClient中,很重要。
 @Bean
 @ConditionalOnMissingBean(LoadBalancerClient.class)
 public LoadBalancerClient loadBalancerClient() 
{
  return new RibbonLoadBalancerClient(springClientFactory());
 }
 ......
}

好了,到这里,我们基本上把SpringClientFactory的来龙去脉已经分析清楚了。接下来,我们一起来看看,这个SpringClientFactory在哪里被使用到。在ribbon系列的第一篇中,我们在RibbonLoadBalancerClient中,看到在执行execute方法中:

RibbonLoadBalancerClient

public <T> execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
   throws IOException 
{
 //获取负载均衡器
 ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
 Server server = getServer(loadBalancer, hint);
 if (server == null) {
  throw new IllegalStateException("No instances available for " + serviceId);
 }
 RibbonServer ribbonServer = new RibbonServer(serviceId, server,
   isSecure(server, serviceId),
   serverIntrospector(serviceId).getMetadata(server));

 return execute(serviceId, ribbonServer, request);
}

忘记了同学,请自行回顾。我们在分析到ILoadBalancer loadBalancer = getLoadBalancer(serviceId);这行代码的时候,我们就没有继续深入了。那么今天我们重点 分析下,getLoadBalancer(serviceId)方法到底是怎么获取负载均衡器的以及相关的配置信息。调用getLoadBalancer方法会进入下面的代码:

protected ILoadBalancer getLoadBalancer(String serviceId) {
 return this.clientFactory.getLoadBalancer(serviceId);
}

其中clientFactory就是之前通过构造器传入的SpringClientFactory,然后调用它的getLoadBalancer方法:

再次回到SpringClientFactory

public ILoadBalancer getLoadBalancer(String name) {
 return getInstance(name, ILoadBalancer.class);
}

前面我们已经分析了SpringClientFactory类的作用,那么重点看一下getInstance方法:

@Override
public <C> getInstance(String name, Class<C> type) {
 //调用NamedContextFactory的getInstance方法
 C instance = super.getInstance(name, type);
 if (instance != null) {
  return instance;
 }
 IClientConfig config = getInstance(name, IClientConfig.class);
 return instantiateWithConfig(getContext(name), type, config);
}

在方法内部首先会调用父类的getInstance(name, type)方法:

再次回到NamedContextFactory

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

从上下文中获取ILoadBalancer类型的bean。接着看一下getContext方法:

private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
......
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);
}

获取服务名的上下文。首先判断map中是否包含key为服务名,如果不包含则以服务名作为key,并调用createContext方法创建AnnotationConfigApplicationContext作为值存入contexts中;如果包含,则根据服务名取出对应的上下文对象。重点看一下createContext,重点来喽,重点来喽,重点来喽,重要的事情说三遍:

protected AnnotationConfigApplicationContext createContext(String name) {
 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
 // @RibbonClient的value或name属性,指定了服务名的配置
 if (this.configurations.containsKey(name)) {
  for (Class<?> configuration : this.configurations.get(name)
    .getConfiguration()) {
   context.register(configuration);
  }
 }
 //通过@RibbonClients的defaultConfiguration指定了配置
 for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
  if (entry.getKey().startsWith("default.")) {
   for (Class<?> configuration : entry.getValue().getConfiguration()) {
    context.register(configuration);
   }
  }
 }
 // 使用默认的配置,即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
  // 将Spring ApplicationContext设置成该服务名的上下文的父级
  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;
}

见代码中的注释,看到这里,很多同学肯定会有疑问,你是怎么知道这些代码跟@RibbonClient@RibbonClients有关系的呢?好的,那么我们先看看this.configurations是什么,以及在哪里初始化的,请认真观看哦:

public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
  implements DisposableBeanApplicationContextAware 
{

 ......

 private Map<String, C> configurations = new ConcurrentHashMap<>();

 ......
 public void setConfigurations(List<C> configurations) {
  for (C client : configurations) {
   this.configurations.put(client.getName(), client);
  }
 }
 ......
}

关键就在上面的setConfigurations方法,那么看一下setConfigurations方法在哪里调用的。忘记了的同学请往上翻,这里为了方便大家理解,还是再放一下代码吧:

@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(
  name 
"org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
  AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties(
{ RibbonEagerLoadProperties.class,
  ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration 
{

 //关键就在这个RibbonClientSpecification类型的bean是在哪里被添加到spring的容器中的
 @Autowired(required = false)
 private List<RibbonClientSpecification> configurations = new ArrayList<>();
 ......
 //传入configurations基金和,创建实例对象,并将其添加到spring应用上下文中
 @Bean
 @ConditionalOnMissingBean
 public SpringClientFactory springClientFactory() {
  SpringClientFactory factory = new SpringClientFactory();
  factory.setConfigurations(this.configurations);
  return factory;
 }
 
 ......
}

见代码中的注释。这里我们直接说方法:在IDEA中,在MAC环境下可以通过cmd+shift+f全局搜索 RibbonClientSpecification在哪里被用到,可以很容易的被找到,自己动手试试吧。结果就在RibbonClientConfigurationRegistrar类中被注册到spring容器中。另外如果熟悉ribbon的同学,应该都知道,ribbon为我们提供了@RibbonClient、@RibbonClients注解,用于针对服务自定义或提供全局自定义配置。

@Configuration(proxyBeanMethods = false)
@Import(RibbonClientConfigurationRegistrar.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RibbonClient 
{
}
@Configuration(proxyBeanMethods = false)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Documented
@Import(RibbonClientConfigurationRegistrar.class)
public @interface RibbonClients 
{

 RibbonClient[] value() default {};

 Class<?>[] defaultConfiguration() default {};

}

在以上两个注解中,都为我们导入了RibbonClientConfigurationRegistrar配置类。

RibbonClientConfigurationRegistrar

public class RibbonClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

 @Override
 public void registerBeanDefinitions(AnnotationMetadata metadata,
   BeanDefinitionRegistry registry)
 
{
  Map<String, Object> attrs = metadata
    .getAnnotationAttributes(RibbonClients.class.getName(), true);
  if (attrs != null && attrs.containsKey("value")) {
   AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
   for (AnnotationAttributes client : clients) {
    //获取@RibbonClient注解属性configuration上的配置并注册为bean
    registerClientConfiguration(registry, getClientName(client),
      client.get("configuration"));
   }
  }
  if (attrs != null && attrs.containsKey("defaultConfiguration")) {
   String name;
   if (metadata.hasEnclosingClass()) {
    name = "default." + metadata.getEnclosingClassName();
   }
   else {
    name = "default." + metadata.getClassName();
   }
   //获取@RibbonClients注解属性defaultConfiguration上的配置并注册为bean, bean名称以default.开头
   registerClientConfiguration(registry, name,
     attrs.get("defaultConfiguration"));
  }
  Map<String, Object> client = metadata
    .getAnnotationAttributes(RibbonClient.class.getName(), true);
  String name = getClientName(client);
  if (name != null) {
   //获取@RibbonClient注解属性configuration上的配置并注册为bean
   registerClientConfiguration(registry, name, client.get("configuration"));
  }
 }

 ......

 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());
 }

}

见上面的代码注释,如果看不懂的同学,请自行补充基础知识。最终注册的就是RibbonClientSpecification类型的bean。好了,弄清楚了这些,我们继续回到NamedContextFactory中createContext方法

三次回到NamedContextFactory

protected AnnotationConfigApplicationContext createContext(String name) {
 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
 // @RibbonClient的value或name属性,指定了服务名的配置
 if (this.configurations.containsKey(name)) {
  for (Class<?> configuration : this.configurations.get(name)
    .getConfiguration()) {
   context.register(configuration);
  }
 }
 //通过@RibbonClients的defaultConfiguration指定了配置
 for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
  if (entry.getKey().startsWith("default.")) {
   for (Class<?> configuration : entry.getValue().getConfiguration()) {
    context.register(configuration);
   }
  }
 }
 // 使用默认的配置,即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
  // 将Spring ApplicationContext设置成该服务名的上下文的父级
  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;
}

解释下:根据服务名创建一个ApplicationContext,在ApplicationContext中会注册通过@RibbonClient或@RibbonClients配置的配置类,以及默认的配置类RibbonClientConfiureation。在各个Configuration中通过@Bean的方式注册IRule或ILoadBalancer等组件的时候,要使用@ConditionalOnMissingBean,如果不使用此注册,会存在多个Bean,后注册的会覆盖先注册的。

多说一句:createContext方法是在父类NamedContextFactory中提供的,而子类并未覆盖,所以不管是ribbon还是feign最终都是通过这个方法根据服务名创建属于自己的上下文配置。

至此我们已经清楚了,是怎么从spring容器中获取负载均衡器的,以及这些配置的来源有哪些。同样,其他关于ribbon的配置,比如IRule、IPing等都是通过这种方式查找和提供配置的。有了以上知识作为储备,那么接下来,看看怎么自定义Ribbon相关配置以及注意事项。

自定义配置

不使用默认实现的IRule,自定义实现IRule。(ILoadBalancer 或IPing使用方式一样)

通过注解@RibbonClient或@RibbonClients指定配置类。

  1. 创建IRule的实现类,一般通过继承AbstractLoadBalancerRule:
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.Server;
 
import java.util.List;
 
/**
 * 自定义负载均衡规则
 * 永远返回服务列表中的第一个服务
 */

public class MyLoadBalancerRule extends AbstractLoadBalancerRule {
 
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
 
    }
 
    @Override
    public Server choose(Object key) {
        System.out.println("=====================进入MyLoadBalancerRule=================");
 
        // 获取所有的服务
        List<Server> serverList = getLoadBalancer().getAllServers();
 
        if (serverList==null || serverList.size()==0) {
            return null;
        }
        // 返回服务列表中的第一个服务
        return serverList.get(0);
    }
}
  1. 创建configuration
import com.netflix.loadbalancer.IRule;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
/**
 * 自定义
 */
@Configuration
public class MyRibbonClientConfiguration {
 
    @Bean
    @ConditionalOnMissingBean
    public IRule myIRule() {
        return new MyLoadBalancerRule();
    }
 
 
}
  1. 在启动类上加上@RibbonClient注解
@SpringBootApplication
@RibbonClient(name = "服务名",configuration = MyRibbonClientConfiguration.class)
public class MyClientApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(MyClientApplication.class, args);
    }
 
}
  1. 启动客户端项目,并访问接口。

可以发现进入了自定义的MyLoadBalancerRule的choose来实现负载均衡。

通过配置实现自定义组件

使用PropertiesFactory提供的功能,通过配置完成自定义组件。

NFLoadBalancerClassName:自定义ILoadBalancer
NFLoadBalancerPingClassName:自定义IPing
NFLoadBalancerRuleClassName:自定义IRule
NIWSServerListClassName:自定义ServerList
NIWSServerListFilterClassName:自定义ServerListFilter

修改application.yml配置:

服务名:
  ribbon:
    NFLoadBalancerRuleClassName:
      client.config.MyLoadBalancerRule

两个方式都可以实现自定义组件,并且第二种通过配置的方式优先级比通过代码实现要高。

注意事项

这里需要注意一点:假设同一个客户端需要调用多个服务端的接口,比如一个接口需要调用服务A的接口:http://serverA/server/info,一个接口需要调用服务B的接口:http://http://serverB/server/info。如果此时你想调用服务A的接口使用自定义的负载均衡逻辑,调用服务B的接口还是使用默认的负载均衡逻辑。对于自定义的配置类,你把它放在了@ComponentScan的扫描范围内,则这个自定义的配置类将全局生效,这样会造成服务A和服务B都会使用自定义的配置。为了避免这样的情况,需要将服务A的自定义配置放在@ComponentScan无法扫描的类,或通过@ComponentScan过滤掉。

比如在启动类上通过@ComponentScan(excludeFilters=“”)过滤掉配置类,并且@RibbonClient中的name属性值一定要和url中的服务名相同:

// myServer一定要是调用接口http://myServer/server/info中的服务名
@RibbonClient(name = "myServer",configuration = MyRibbonClientConfiguration.class)
@ComponentScan(excludeFilters 
"配置类所在的包")

这里我给的建议是,如果只是对某个服务应用自定义配置,那么就不要在配置类上添加@Configuration注解,这样就不会被扫描到。

原因分析

在第一次调用客户端接口的时候,通过SpringClientFactory的getInstance获取负载均衡器,最终会调用其父类NamedContextFactory的createContext,来初始化服务名对应的上下文。

  1. 首先查看是否有服务名对应的配置,即通过@RibbonClient(name="服务名", configuration="")来配置,如果有注册到当前上下文中;
  2. 然后再查看是否有default开头的配置,即通过@RibbonClients(defaultConfiguration="")来配置,如果有注册到当前上下文中。
  3. 最后注册默认的配置。最后将当前服务名的上下文设置成Spring 上下文的子级。

当自定义的配置在@ComponentScan扫描范围内的时候(如果配置类上标注了@Configuration注解)即会被注册到Spring 上下文中。这样当加载默认的配置类RibbonClientConfiguration,因为使用了@ConditionalOnMissingBean,此时父级的上下文中已经存在了,条件不满足,就不会注册这里的bean。

当自定义的配置不在@ComponentScan扫描范围内的时候,就不会加进Spring 上下文,此时要想配置生效,就需要@RibbonClient配置的name值与服务名一致,这样配置会加入当前服务名所对应的上下文中。只会对当前服务名生效。

总结

当自定义的配置只需要应用到某个服务名上时,只需要将自定义配置注册到服务名的上下文中, 不要将配置类放在启动类能扫描到的包下或者不标注@Configuration注解。

当自定义的配置需要应用到所有的服务名上时,则可以将自定义配置注册到Spring 上下文中。

主要分成2种配置:

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

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

更多原创文章,请扫码关注我的微信公众号
更多原创文章,请扫码关注我的微信公众号

lyq

2021/11/05  阅读:34  主题:默认主题

作者介绍

lyq