ribbon结构和初始化过程

第一步spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration

找到RibbonAutoConfiguration类

/**
 * Auto configuration for Ribbon (client side load balancing).
 *
 * @author Spencer Gibb
 * @author Dave Syer
 * @author Biju Kunjummen
 */
@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);
    }

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

    @Bean
    @ConditionalOnMissingBean(LoadBalancerClient.class)
    public LoadBalancerClient loadBalancerClient() {
        return new RibbonLoadBalancerClient(springClientFactory());
    }

    @Bean
    @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
    @ConditionalOnMissingBean
    public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(final SpringClientFactory clientFactory) {
        return new RibbonLoadBalancedRetryFactory(clientFactory);
    }

    @Bean
    @ConditionalOnMissingBean
    public PropertiesFactory propertiesFactory() {
        return new PropertiesFactory();
    }

    @Bean
    @ConditionalOnProperty(value = "ribbon.eager-load.enabled")
    public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
        return new RibbonApplicationContextInitializer(springClientFactory(),
                ribbonEagerLoadProperties.getClients());
    }

    @Configuration
    @ConditionalOnClass(HttpRequest.class)
    @ConditionalOnRibbonRestClient
    protected static class RibbonClientHttpRequestFactoryConfiguration {

        @Autowired
        private SpringClientFactory springClientFactory;

        @Bean
        public RestTemplateCustomizer restTemplateCustomizer(
                final RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory) {
            return restTemplate -> restTemplate.setRequestFactory(ribbonClientHttpRequestFactory);
        }

        @Bean
        public RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {
            return new RibbonClientHttpRequestFactory(this.springClientFactory);
        }
    }

    //TODO: support for autoconfiguring restemplate to use apache http client or okhttp

    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(OnRibbonRestClientCondition.class)
    @interface ConditionalOnRibbonRestClient { }

    private static class OnRibbonRestClientCondition extends AnyNestedCondition {
        public OnRibbonRestClientCondition() {
            super(ConfigurationPhase.REGISTER_BEAN);
        }

        @Deprecated //remove in Edgware"
        @ConditionalOnProperty("ribbon.http.client.enabled")
        static class ZuulProperty {}

        @ConditionalOnProperty("ribbon.restclient.enabled")
        static class RibbonProperty {}
    }

    /**
     * {@link AllNestedConditions} that checks that either multiple classes are present
     */
    static class RibbonClassesConditions extends AllNestedConditions {

        RibbonClassesConditions() {
            super(ConfigurationPhase.PARSE_CONFIGURATION);
        }

        @ConditionalOnClass(IClient.class)
        static class IClientPresent {

        }

        @ConditionalOnClass(RestTemplate.class)
        static class RestTemplatePresent {

        }

        @ConditionalOnClass(AsyncRestTemplate.class)
        static class AsyncRestTemplatePresent {

        }

        @ConditionalOnClass(Ribbon.class)
        static class RibbonPresent {

        }

    }
}

首先关注创建工厂方法bean:
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
SpringClientFactory构造方法:

public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {

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

查看父类:NamedContextFactory实现了ApplicationContextAware,可以拿到容器上下文

public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
       implements DisposableBean, ApplicationContextAware 
   private ApplicationContext parent;

@Override
   public void setApplicationContext(ApplicationContext parent) throws BeansException {
       this.parent = parent;
   }
....
   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);
       }
       context.setDisplayName(generateDisplayName(name));
       context.refresh();
       return context;
   }

看到createContext方法:new了一个AnnotationConfigApplicationContext,根据传入的name获取到this.configurations.containsKey是否已经有定义的service注册到context里,然后继续将其他的config注册到新创建的AnnotationConfigApplicationContext里,然后在讲AnnotationConfigApplicationContext设置到spring的context 里context.setParent(this.parent);类似springmvc和spring容器的关系。
回到RibbonAutoConfiguration,还定义了一个LoadBalancerClient值得关注:

@Bean
    @ConditionalOnMissingBean(LoadBalancerClient.class)
    public LoadBalancerClient loadBalancerClient() {
        return new RibbonLoadBalancerClient(springClientFactory());
    }

然后看下ribbon调用时做了些什么:

 ServiceInstance serviceInstance = loadBalancerClient.choose("hello-server");

进入ZoneAwareLoadBalancer类

@Override
    public Server chooseServer(Object key) {
        if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
            logger.debug("Zone aware logic disabled or there is only one zone");
            return super.chooseServer(key);
        }
        Server server = null;
        try {
            LoadBalancerStats lbStats = getLoadBalancerStats();
            Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
            logger.debug("Zone snapshots: {}", zoneSnapshot);
            if (triggeringLoad == null) {
                triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(
                        "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d);
            }

            if (triggeringBlackoutPercentage == null) {
                triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(
                        "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d);
            }
            Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
            logger.debug("Available zones: {}", availableZones);
            if (availableZones != null &&  availableZones.size() < zoneSnapshot.keySet().size()) {
                String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
                logger.debug("Zone chosen: {}", zone);
                if (zone != null) {
                    BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
                    server = zoneLoadBalancer.chooseServer(key);
                }
            }
        } catch (Exception e) {
            logger.error("Error choosing server using zone aware logic for load balancer={}", name, e);
        }
        if (server != null) {
            return server;
        } else {
            logger.debug("Zone avoidance logic is not invoked.");
            return super.chooseServer(key);
        }
    }

判断是否有配置zone

private static final DynamicBooleanProperty ENABLED = DynamicPropertyFactory.getInstance().getBooleanProperty("ZoneAwareNIWSDiscoveryLoadBalancer.enabled", true);
   

没有配置zone调用父类chooseServer

/*
     * Get the alive server dedicated to key
     * 
     * @return the dedicated server
     */
    public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }

调用rule.choose(key);
rule定义默认

    private final static IRule DEFAULT_RULE = new RoundRobinRule();

public interface IRule{
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     * 
     * @return choosen Server object. NULL is returned if none
     *  server is available 
     */

    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

屏幕快照 2019-08-25 上午12.14.07.png
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容