Spring Cloud Open Feign系列【6】Ribbion配置类源码分析

文章目录

  • Ribbon 自动配置类
    • RibbonAutoConfiguration
  • RibbonClientConfiguration

Ribbon 自动配置类

基于Spring Boot 提供的自动配置功能,Ribbon 自动配置的类在org.springframework.cloud.netflix.ribbon包的RibbonClientConfigurationRibbonAutoConfiguration配置类中。
 

RibbonAutoConfiguration

RibbonAutoConfiguration相当于全局配置,主要是加载Ribbon 客户端工厂、配置类工厂、重试机制工厂等,这个配置类在启动的时候就会被加载。

首先看下RibbonAutoConfiguration配置类上的注解:

// 标记为配置类Bean
@Configuration
// 注入Bean条件,RibbonClassesConditions类去判断是否注入该Bean
@Conditional({
   
     RibbonAutoConfiguration.RibbonClassesConditions.class})
// @RibbonClients为所有的Ribbon客户端提供默认配置, @RibbonClients标记该类被注册为一个 RibbonClientSpecification
@RibbonClients
//  在加载配置的类之后再加载当前类
@AutoConfigureAfter(
    name = {
   
     "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration"}
)
// 在加载配置的类之前再加载当前类
@AutoConfigureBefore({
   
     LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
// 使RibbonEagerLoadProperties、ServerIntrospectorProperties配置类生效
@EnableConfigurationProperties({
   
     RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})

该类具有两个属性,使用@Autowired注入

    @Autowired(
        required = false
    )
    // List集合,类型为RibbonClientSpecification,注入使用了@RibbonClients 的配置类。
    private List<RibbonClientSpecification> configurations = new ArrayList();
    // Ribbon的饥饿加载(eager-load)模式配置类,Ribbon进行客户端负载均衡的Client并不是在服务启动的时候就初
    // 始化好的,而是在调用的时候才会去创建相应的Client,开启加载饥饿模式,可以提前创建。
    @Autowired
    private RibbonEagerLoadProperties ribbonEagerLoadProperties;

接下来看下这个配置类注入了那些Bean 对象:

	// HasFeatures 可以查看系统启动的一些features,进而了解系统特征。
    @Bean
    public HasFeatures ribbonFeature() {
   
     
        return HasFeatures.namedFeature("Ribbon", Ribbon.class);
    }
    //  Spring 创建 Ribbon 客户端、负载均衡器、客户端配置实例的工厂
    @Bean
    @ConditionalOnMissingBean
    public SpringClientFactory springClientFactory() {
   
     
        SpringClientFactory factory = new SpringClientFactory();
        factory.setConfigurations(this.configurations);
        return factory;
    }
    // LoadBalancerClient为springcloud提供的负载均衡器客户端,可以查询所有的服务并挑选一个
    @Bean
    @ConditionalOnMissingBean({
   
     LoadBalancerClient.class})
    public LoadBalancerClient loadBalancerClient() {
   
     
        return new RibbonLoadBalancerClient(this.springClientFactory());
    }
    // Ribbon重试工厂
    @Bean
    @ConditionalOnClass(
        name = {
   
     "org.springframework.retry.support.RetryTemplate"}
    )
    @ConditionalOnMissingBean
    public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(final SpringClientFactory clientFactory) {
   
     
        return new RibbonLoadBalancedRetryFactory(clientFactory);
    }
    // 用来进行客户端配置类的获取,比如interface com.netflix.loadbalancer.IRule 规则配置类,对应一个属性=》NFLoadBalancerRuleClassName
    @Bean
    @ConditionalOnMissingBean
    public PropertiesFactory propertiesFactory() {
   
     
        return new PropertiesFactory();
    }
    // 如果配置了eager-load饥饿加载模式,就注册RibbonApplicationContextInitializer上下文初始化对象
    @Bean
    @ConditionalOnProperty({
   
     "ribbon.eager-load.enabled"})
    public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
   
     
        return new RibbonApplicationContextInitializer(this.springClientFactory(), this.ribbonEagerLoadProperties.getClients());
    }

RibbonClientConfiguration

RibbonClientConfiguration 则是Ribbon 客户端相关配置,这个类再执行第一次Feign 客户端请求的时候才会被加载。

比如当前Feign 客户端为order-service,它是第一次进行请求,那么会进入获取客户端配置方法:
 
然后在NamedContextFactory中获取改客户端的上下文,没有的时候,会去创建。

    protected AnnotationConfigApplicationContext getContext(String name) {
   
     
    // 当前上下文是否包含了该客户端order-service
        if (!this.contexts.containsKey(name)) {
   
     
            synchronized(this.contexts) {
   
     
            	// 没有则创建并放入
                if (!this.contexts.containsKey(name)) {
   
     
                    this.contexts.put(name, this.createContext(name));
                }
            }
        }
		// 有直接返回
        return (AnnotationConfigApplicationContext)this.contexts.get(name);
    }

最终调用NamedContextFactory的createContext 方法,为每个客户端创建上下文。

通过NamedContextFactory机制,按不同的应用创建了不同的子ApplicationContext,从而隔离不互相影响,每一个Feign 客户端配置不同的应用上下文,这样可以让每个不同的客户端使用不同的配置、不同的Bean。

在createContext 方法创建了客户端的上下文后,进入刷新,则会开始加载直接当前 Context 需要的Bean 、前置后置处理器等流程。RibbonClientConfiguration就是在这个时候加载的。

	// 在获取一个@FeignClient接口对应的代理对象前,先为其创建Context对象
    protected AnnotationConfigApplicationContext createContext(String name) {
   
     
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 查询RibbonAutoConfiguration 中,使用了@RibbonClients 的配置类
        if (this.configurations.containsKey(name)) {
   
     
        	// 配置类中有 名称为当前客户端的配置,则添加配置并注册到上下文中
            Class[] var3 = ((NamedContextFactory.Specification)this.configurations.get(name)).getConfiguration();
            int var4 = var3.length;

            for(int var5 = 0; var5 < var4; ++var5) {
   
     
                Class<?> configuration = var3[var5];
                context.register(new Class[]{
   
     configuration});
            }
        }
		// 循环使用了@RibbonClients 的配置类
        Iterator var9 = this.configurations.entrySet().iterator();

        while(true) {
   
     
            Entry entry;
            do {
   
     
                if (!var9.hasNext()) {
   
     
                	// 注册
                    context.register(new Class[]{
   
     PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType});
                    context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName, Collections.singletonMap(this.propertyName, name)));
                    if (this.parent != null) {
   
     
                        context.setParent(this.parent);
                        context.setClassLoader(this.parent.getClassLoader());
                    }

                    context.setDisplayName(this.generateDisplayName(name));
                    // 刷新上下文
                    context.refresh();
                    return context;
                }

                entry = (Entry)var9.next();
             // 配置类不是以default. 开头,则进入do 循环,这里没有
            } while(!((String)entry.getKey()).startsWith("default."));
			
            Class[] var11 = ((NamedContextFactory.Specification)entry.getValue()).getConfiguration();
            int var12 = var11.length;
			// 循环默认配置,并注册
            for(int var7 = 0; var7 < var12; ++var7) {
   
     
                Class<?> configuration = var11[var7];
                context.register(new Class[]{
   
     configuration});
            }
        }
    }

可以看到默认有两个@RibbonClients 的配置类:
 
创建上下文后,可以看到,当前客户端的上下文信息。
 

RibbonClientConfiguration类使用@Import导入了其他配置类。

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

RibbonClientConfiguration类的属性如下:

	// 默认的连接超时时间,默认1秒
    public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
    // 默认的读取超时时间,默认1秒
    public static final int DEFAULT_READ_TIMEOUT = 1000;
    public static final boolean DEFAULT_GZIP_PAYLOAD = true;
    // @RibbonClientName Ribbon客户端名称
    @RibbonClientName
    private String name = "client";
    // 配置工厂,由RibbonAutoConfiguration 注入
    @Autowired
    private PropertiesFactory propertiesFactory;

最后看下RibbonClientConfiguration注入了哪些Bean :

	// 当前客户端配置 
    @Bean
    @ConditionalOnMissingBean
    public IClientConfig ribbonClientConfig() {
   
     
    	// 默认的配置类
        DefaultClientConfigImpl config = new DefaultClientConfigImpl();
        // 加载配置
        config.loadProperties(this.name);
        // 设置配置 
        config.set(CommonClientConfigKey.ConnectTimeout, 1000);
        config.set(CommonClientConfigKey.ReadTimeout, 1000);
        config.set(CommonClientConfigKey.GZipPayload, true);
        return config;
    }
	
	// 加载负载均衡算法,默认ZoneAvoidanceRule 轮询。
    @Bean
    @ConditionalOnMissingBean
    public IRule ribbonRule(IClientConfig config) {
   
     
        if (this.propertiesFactory.isSet(IRule.class, this.name)) {
   
     
            return (IRule)this.propertiesFactory.get(IRule.class, config, this.name);
        } else {
   
     
            ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
            rule.initWithNiwsConfig(config);
            return rule;
        }
    }
	// Ping 检测服务健康状态
    @Bean
    @ConditionalOnMissingBean
    public IPing ribbonPing(IClientConfig config) {
   
     
        return (IPing)(this.propertiesFactory.isSet(IPing.class, this.name) ? (IPing)this.propertiesFactory.get(IPing.class, config, this.name) : new DummyPing());
    }
	// Ribbon的服务列表 
    @Bean
    @ConditionalOnMissingBean
    public ServerList<Server> ribbonServerList(IClientConfig config) {
   
     
        if (this.propertiesFactory.isSet(ServerList.class, this.name)) {
   
     
            return (ServerList)this.propertiesFactory.get(ServerList.class, config, this.name);
        } else {
   
     
            ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
            serverList.initWithNiwsConfig(config);
            return serverList;
        }
    }
	// 服务列表更新器
    @Bean
    @ConditionalOnMissingBean
    public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
   
     
        return new PollingServerListUpdater(config);
    }
	// 负载均衡器ILoadBalancer
    @Bean
    @ConditionalOnMissingBean
    public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList<Server> serverList, ServerListFilter<Server> serverListFilter, IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
   
     
        return (ILoadBalancer)(this.propertiesFactory.isSet(ILoadBalancer.class, this.name) ? (ILoadBalancer)this.propertiesFactory.get(ILoadBalancer.class, config, this.name) : new ZoneAwareLoadBalancer(config, rule, ping, serverList, serverListFilter, serverListUpdater));
    }
	// Ribbon的LoadBalancer五大组件之ServerListFilter服务列表过滤器
    @Bean
    @ConditionalOnMissingBean
    public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
   
     
        if (this.propertiesFactory.isSet(ServerListFilter.class, this.name)) {
   
     
            return (ServerListFilter)this.propertiesFactory.get(ServerListFilter.class, config, this.name);
        } else {
   
     
            ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
            filter.initWithNiwsConfig(config);
            return filter;
        }
    }
	// 负载均衡器上下文
    @Bean
    @ConditionalOnMissingBean
    public RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer, IClientConfig config, RetryHandler retryHandler) {
   
     
        return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler);
    }
	// 重试处理器
    @Bean
    @ConditionalOnMissingBean
    public RetryHandler retryHandler(IClientConfig config) {
   
     
        return new DefaultLoadBalancerRetryHandler(config);
    }
	// 服务拦截器
    @Bean
    @ConditionalOnMissingBean
    public ServerIntrospector serverIntrospector() {
   
     
        return new DefaultServerIntrospector();
    }
	// 后置处理器
    @PostConstruct
    public void preprocess() {
   
     
        RibbonUtils.setRibbonProperty(this.name, CommonClientConfigKey.DeploymentContextBasedVipAddresses.key(), this.name);
    }

总结

项目启动=》RibbonAutoConfiguration(加载工厂类,默认配置)=》第一次访问=》代理客户端=》初始化该客户端上下文=》RibbonClientConfiguration(加载服务列表、过滤器、拦截器、客户端配置等)=》处理请求。

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: