【SpringCloud原理】Ribbon核心组件以及运行原理源码剖析(五)

pivoteic
发布于 2022-6-16 17:49
浏览
0收藏

 

二、SpringCloud中使用的核心组件的实现都有哪些

 

说完了Ribbon的一些核心组件和执行原理之后,我们再来看一下在SpringCloud环境下,这些组件到底是用的哪些实现,毕竟有写时接口,有的是抽象类。

 

Ribbon的自动装配类:RibbonAutoConfiguration,我拎出了核心的源码

@Configuration
@RibbonClients
public class 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配置类上有个@RibbonClients注解,接下来讲解一下这个注解的作用

@Import(RibbonClientConfigurationRegistrar.class)
public @interface RibbonClients {

  RibbonClient[] value() default {};

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

}

 
看过我写的OpenFeign的文章小伙伴肯定知道,要使用Feign,得需要使用@EnableFeignClients,@EnableFeignClients的作用可以扫描指定包路径下的@FeignClient注解,也可以声明配置类;同样RibbonClients的作用也是可以声明配置类,同样也使用了@Import注解注解来实现的,RibbonClientConfigurationRegistrar这个配置类的作用就是往spring容器中注入每个服务的Ribbon组件(@RibbonClient里面可以声明每个服务对应的配置)的配置类和默认配置类,将配置类封装为RibbonClientSpecification注入到spring容器中,其实就跟@FeignClient注解声明配置的作用是一样的。

 

RibbonAutoConfiguration的主要作用就是注入了一堆RibbonClientSpecification,就是每个服务对应的配置类,然后声明了SpringClientFactory这个bean,将配置类放入到里面。

 

SpringClientFactory是不是感觉跟OpenFeign中的FeignContext很像,其实两个的作用是一样的,SpringClientFactory也继承了NamedContextFactory,实现了配置隔离,同时也在构造方法中传入了每个容器默认的配置类RibbonClientConfiguration。至于什么是配置隔离,我在OpenFeign那篇文章说过,不清楚的小伙伴可以后台回复feign01即可获得文章链接。

 

配置优先级问题

 

这里我说一下在OpenFeign里没仔细说的配置优先级的事情,因为有这么多配置类,都可以在配置类中声明对象,那么到底使用哪个配置类声明的对象呢。

 

优先级最高的是springboot启动的时候的容器,因为这个容器是每个服务的容器的父容器,而在配置类声明bean的时候,都有@ConditionalOnMissingBean注解,一旦父容器有这个bean,那么子容器就不会初始化。

优先级第二高的是每个客户端声明的配置类,也就是通过@FeignClient和@RibbonClient的configuration属性声明的配置类

优先级第三高的是@EnableFeignClients和@RibbonClients注解中configuration属性声明的配置类

优先级最低的就是FeignContext和SpringClientFactory构造时传入的配置类

 

至于优先级怎么来的,其实是在NamedContextFactory中createContext方法中构建AnnotationConfigApplicationContext时按照配置的优先级一个一个传进去的。

 

RibbonClientConfiguration提供的默认的bean

 

接下来我们看一下RibbonClientConfiguration都提供了哪些默认的bean

  @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;
  }


配置类对应的bean,这里设置了ConnectTimeout和ReadTimeout都是1s中。

  @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;
  }


IRule,默认是ZoneAvoidanceRule,这个Rule带有过滤的功能,过滤哪些不可用的分区的服务(这个过滤可以不用care),过滤成功之后,继续采用线性轮询的方式从过滤结果中选择一个出来。至于这个propertiesFactory,可以不用管,这个是默认读配置文件的中的配置,一般不设置,后面看到都不用care。

  @Bean
  @ConditionalOnMissingBean
  @SuppressWarnings("unchecked")
  public ServerList<Server> ribbonServerList(IClientConfig config) {
    if (this.propertiesFactory.isSet(ServerList.class, name)) {
      return this.propertiesFactory.get(ServerList.class, config, name);
    }
    ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
    serverList.initWithNiwsConfig(config);
    return serverList;
  }


默认是ConfigurationBasedServerList,也就是基于配置来提供服务实例列表。但是在SpringCloud环境中,这是不可能的,因为服务信息是在注册中心,所以应该是服务注册中心对应实现的,比如Nacos的实现NacosServerList,这里我贴出NacosServerList的bean的声明,在配置类NacosRibbonClientConfiguration中

  @Bean
  @ConditionalOnMissingBean
  public ServerList<?> ribbonServerList(IClientConfig config,
      NacosDiscoveryProperties nacosDiscoveryProperties) {
    NacosServerList serverList = new NacosServerList(nacosDiscoveryProperties);
    serverList.initWithNiwsConfig(config);
    return serverList;
  }


至于为什么容器选择NacosServerList而不是ConfigurationBasedServerList,主要是因为NacosRibbonClientConfiguration这个配置类是通过@RibbonClients导入的,也就是比SpringClientFactory导入的RibbonClientConfiguration配置类优先级高。

  @Bean
  @ConditionalOnMissingBean
  public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
    return new PollingServerListUpdater(config);
  }


ServerListUpdater,就是我们剖析的PollingServerListUpdater,默认30s更新一次BaseLoadBalancer内部服务的缓存。

  @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);
    }
    return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
        serverListFilter, serverListUpdater);
  }


ILoadBalancer,默认是ZoneAwareLoadBalancer,构造的时候也传入了上面声明的的bean,ZoneAwareLoadBalancer这个类继承了DynamicServerListLoadBalancer,所以这个类功能也符合我们剖析的源码,至于ZoneAwareLoadBalancer多余的特性,也不用care。

 

到这里,Ribbon在SpringCloud的配置我们就讲完了,主要就是声明了很多核心组件的bean,最后都设置到ZoneAwareLoadBalancer中。但是,AbstractLoadBalancerAwareClient这个对象的声明我们并没有在配置类中找到,主要是因为这个对象是OpenFeign整合Ribbon的一个入口,至于是如何整合的,这个坑就留给下篇文章吧。

 

那么在springcloud中,上图就可以加上注册中心。

【SpringCloud原理】Ribbon核心组件以及运行原理源码剖析(五)-鸿蒙开发者社区

三、总结

 

本文剖析了Ribbon这个负载均衡组件中的一些核心组件的源码,并且将这些组件之间的关系一一描述清楚,同时也剖析了在发送请求的时候是如何通过ILoadBalancer获取到一个服务实例,重构URI的过程。希望本篇文章能够让你知道Ribbon是如何工作的。至于OpenFeign整合Ribbon,我会在下篇文章剖析,有喜欢的小伙伴可以点个关注。。

 

文章转自公众号:三友的java日记

已于2022-6-16 17:49:40修改
收藏
回复
举报
回复
    相关推荐