欢迎光临
我们一直在努力

微服务组件源码2——Spring Ribbon原理(基于RibbonLoadBalancerClient)

1、基本原理(LoadBalancerClient + 拦截器)

Ribbon 负载组件的内部就是集成了 LoadBalancerClient 负载均衡客户端,所以 Ribbon 负载均衡的原理本质也跟上面介绍的 LoadBalancerClient 原理一致,负载均衡器 Ribbon 默认会通过 Eureka Client 向 Eureka 服务端的服务注册列表中获取服务的信息,并缓存一份在本地 JVM 中,根据缓存的服务注册列表信息,可以通过 LoadBalancerClient 来选择不同的服务实例,从而实现负载均衡。主要做如下的增强处理:

基本用法就是注入一个 RestTemplate,并使用@LoadBalance注解标注 RestTemplate,从而使RestTemplate具备负载均衡的能力。当 Spring 容器启动时,使用@LoadBalanced 注解修饰的 RestTemplate 会被添加拦截器 LoadBalancerInterceptor,拦截器会拦截 RestTemplate 发送的请求,转而执行LoadBalancerInterceptor 中的 intercept() 方法,并在 intercept()方法中使用LoadBalancerClient 处理请求,从而达到负载均衡的目的。

那么 RestTemplate 添加 @LoadBalanced 注解后,为什么会被拦截呢?这是因为 LoadBalancerAutoConfiguration 类维护了一个被 @LoadBalanced 修饰的 RestTemplate 列表,在初始化过程中,通过调用 customizer.customize(restTemplate) 方法为 RestTemplate 添加了 LoadBalancerInterceptor 拦截器,该拦截器中的方法将远程服务调用的方法交给了 LoadBalancerClient 去处理,从而达到了负载均衡的目的

Ribbon会提供一个serviceId对应的服务实例,此外就不会做其他事了,基于此服务实例进行远程调用、返回结果处理等都是由RestTemplate去处理的

2、自动配置概述

2.1、RibbonAutoConfiguration

* 收集@RibbonClients注解信息封装为RibbonClientSpecification(功能和@LoadBalancerClients注解类似)

 会收集注册@RibbonClients、RibbonClient注解,name和指定的配置类(包括默认)为RibbonClientSpecification(String name, Class<?>[] configuration)类

      @Configuration
      @RibbonClient(name = "orderService",configuration = HelloRibbonConfiguration.class)

@RibbonClients(defaultConfiguration = MyRibbonConfiguration.class)
      public class RibbonConfiguration {}

* 创建SpringClientFactory extends NamedContextFactory<RibbonClientSpecification>命名上下文

  ①会基于容器中List<RibbonClientSpecification> configurations(上一步注册的配置集合),为每一个指定的name 创建一个上下文,父上下文为顶层Spring容器

    每个上下文里包含可独自指定的RibbonClientSpecification配置和默认配置类RibbonClientConfiguration,如IClientConfig客户端配置、IRule策略的配置、超时配置等

  ②其中在RibbonClientConfiguration里的IClientConfig客户端配置的默认配置为DefaultClientConfigImpl,它还会加载文件配置的信息

    从Spring env中加载“ribbon.配置项”这类全局默认配置、和加载“client名.ribbon.配置项”这类针对某个Client的配置信息

* 创建RibbonLoadBalancerClient(springClientFactory()) 用户使用的顶级LoadBalancerClient 接口对象

   ①List<RibbonClientSpecification>收集全局配置和单个服务的配置信息

   ②SpringClientFactory会为每一个服务创建独立的上下文

   ③new RibbonLoadBalancerClient(springClientFactory())

@Configuration
@Conditional(org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration.RibbonClassesConditions.class)
//其内RibbonClientConfigurationRegistrar会收集注册@RibbonClientsRibbonClient注解内的那么,name和指定的配置类(包括默认)为RibbonClientSpecification
@RibbonClients
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
//@LoadBalanced 注解修饰的 RestTemplate 会被添加拦截器LoadBalancerInterceptor(loadBalancerClient, requestFactory)
@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);
    }
    //客户端配置容器工厂:会为每一个ClientName和指定的配置类(包含了默认配置类RibbonClientConfiguration)创建一个独立的上下文
    @Bean
    public SpringClientFactory springClientFactory() {
        SpringClientFactory factory = new SpringClientFactory();
        factory.setConfigurations(this.configurations);
        return factory;
    }
    //使用客户端配置容器工厂创建顶层对象,RibbonLoadBalancerClient,实现了LoadBalancerClient接口
    @Bean
    @ConditionalOnMissingBean(LoadBalancerClient.class)
    public LoadBalancerClient loadBalancerClient() {
        return new RibbonLoadBalancerClient(springClientFactory());
    }

    //

}

//SpringClientFactory

public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {

    static final String NAMESPACE = "ribbon";

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

    public ILoadBalancer getLoadBalancer(String name) {
        return getInstance(name, ILoadBalancer.class);
    }
    
    //通过NamedContextFactory获取指定容器内的某个type实例,获取不到时,会通过IClientConfig进行创建,
    //调用容器的autowireBean进属性注入,但是最终实例不会放入容器中
    @Override
    public <C> C getInstance(String name, Class<C> type) {
        C instance = super.getInstance(name, type);
        if (instance != null) {
            return instance;
        }
        IClientConfig config = getInstance(name, IClientConfig.class);
        return instantiateWithConfig(getContext(name), type, config);
    }
    
    //
}

2.2、RibbonClientConfiguration默认配置(serviceId维度)

* 是默认的Ribbon客户端配置类,在创建SpringClientFactory是指定的默认配置类

* 每个独立的服务ID上下文中都会注入此类配置的信息

  – 指定了robbin会用到的所有组件的bean

  – 同时还import指定了具体的httpClient的自动配置

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

2.2.1、Name容器中6大可配置组件 

  • Spring Ribbon的6大可配置组件类及默认配置
  • 自动化配置接口

    描述

    默认实现

    说明

    IClientConfig

    Ribbon的客户端配置

    com.netflix.client.config.DefaultClientConfigImpl

     对如下组件及其他信息的配置项

    IRule

    Ribbon的负载均衡策略

    com.netflix.loadbalancer.ZoneAvoidanceRule

    该策略能在多区域环境下选出最佳区域的实例进行访问

    IPing

    Ribbon的实例检查策略                     

    com.netflix.loadbalancer.NoOpPing

    该检查策略是一个特殊的实现,实际上它并不会检查实例是否可用,而是始终返回true,默认所有的实例都是可用的

    ServerList<Server>

    服务实例清单维护机制

    com.netflix.loadbalancer.ConfigurationBasedServerList

    ServerListFilter<Server>

    服务实例清单过滤机制

    org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter

    该策略能够优先过滤出与请求调用方处于同一个区域的服务清单

    ILoadBalancer

    (负载均衡器主类)

    com.netflix.loadbalancer.ZoneAwareLoadBalancer

    该策略具备服务感知能力,封装了如上几个组件

  • 与Eureka、Ncaos集成
  • * 当在Spring Cloud中同时引入Spring Cloud Eureka 和 Spring Cloud Ribbon 时,会触发Eureka对于Ribbon的自动化配置,那么Ribbon的相关默认实现类就会有所变化。

    自动化配置接口

    描述

    默认实现

    说明

    IPing

    Ribbon的实例检查策略                 

    com.netflix.niws.loadbalancer.NIWSDiscoveryPing

    该实现将实例检查的任务交给服务治理框架来进行维护

    ServerList<Server>

    服务实例清单维护机制

    com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList

     该实现会将服务清单列表交给Eureka来维护

    在与Spring Cloud Eureka结合使用时,我们的配置会更简单,例如上一步中提到的客户端配置EUREKA-CLIENT.ribbon.listOfServers,就不需要再这么麻烦的进行配置,

    因为Eureka的自动配置类会为我们维护所有实例的清单。

    我们也可以通过参数配置来禁用Eureka对Ribbon服务实例的维护实现:ribbon.eureka.enabled=false

    * 同样的,当在Spring Cloud中同时引入Spring Cloud Nacos和 Spring Cloud Ribbon 时,且禁用了Eureka,此时也会只有Nacos自定义的实现类覆盖关默认实现类

      例如我们希望使用Nacos的IRule实现,那么在配置类中加上

    @Bean
      public IRule ribbonRule() {
          return new NacosRule();
      }

    // Spring Cloud Ribbon中对RibbonClient的默认配置类

    @Configuration(proxyBeanMethods = false)
    @EnableConfigurationProperties
    //具体通信工具配置HttpClient
    @Import({ HttpClientConfiguration.class, OkHttpRibbonConfiguration.class,
            RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class })
    public class RibbonClientConfiguration {

        public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
        public static final int DEFAULT_READ_TIMEOUT = 1000;
        public static final boolean DEFAULT_GZIP_PAYLOAD = true;

        @RibbonClientName
        private String name = "client";
        
        @Autowired
        private PropertiesFactory propertiesFactory;

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

        
        //各个组件的默认实现
        @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();
        }

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

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

        //

    }

    2.2.2、OkHttpRibbonConfiguration (例)

    配置具体的通信工具,例如当项目存在OkHttpClient类且存在配置属性ribbon.okhttp.enabled,会加载这个配置类

    * OkHttpLoadBalancingClient和RetryableOkHttpLoadBalancingClient实现类bean

      是实现负载均衡功能中顶级接口com.netflix.client.IClient

    * OkHttpClientConfiguration

      是存粹的OkHttpClient的配置,包括配置连接池、生成OkHttpClient对象

      注意这里OkHttpClient的生成,会从IClientConfig config配置中获取Ribbon相关的配置(超时配置),设置在OkHttpClient中

    注:基于RestTemplate使用ribbon的方法内,是没有用到此配置的bean。是直接借助RestTemplate的通信功能进行访问。Ribbon值提供serverInstan的选择

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnProperty("ribbon.okhttp.enabled")
    @ConditionalOnClass(name = "okhttp3.OkHttpClient")
    public class OkHttpRibbonConfiguration {

       @RibbonClientName
       private String name = "client";

       @Bean
       @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
       @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
       public RetryableOkHttpLoadBalancingClient retryableOkHttpLoadBalancingClient(
             IClientConfig config, ServerIntrospector serverIntrospector,
             ILoadBalancer loadBalancer, RetryHandler retryHandler,
             LoadBalancedRetryFactory loadBalancedRetryFactory, OkHttpClient delegate,
             RibbonLoadBalancerContext ribbonLoadBalancerContext) {
          RetryableOkHttpLoadBalancingClient client = new RetryableOkHttpLoadBalancingClient(
                delegate, config, serverIntrospector, loadBalancedRetryFactory);
          client.setLoadBalancer(loadBalancer);
          client.setRetryHandler(retryHandler);
          client.setRibbonLoadBalancerContext(ribbonLoadBalancerContext);
          Monitors.registerObject("Client_" + this.name, client);
          return client;
       }

       @Bean
       @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
       @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
       public OkHttpLoadBalancingClient okHttpLoadBalancingClient(IClientConfig config,
             ServerIntrospector serverIntrospector, ILoadBalancer loadBalancer,
             RetryHandler retryHandler, OkHttpClient delegate) {
          OkHttpLoadBalancingClient client = new OkHttpLoadBalancingClient(delegate, config,
                serverIntrospector);
          client.setLoadBalancer(loadBalancer);
          client.setRetryHandler(retryHandler);
          Monitors.registerObject("Client_" + this.name, client);
          return client;
       }

       @Configuration(proxyBeanMethods = false)
       protected static class OkHttpClientConfiguration {

          private OkHttpClient httpClient;

          @Bean
          @ConditionalOnMissingBean(ConnectionPool.class)
          public ConnectionPool httpClientConnectionPool(IClientConfig config,
                OkHttpClientConnectionPoolFactory connectionPoolFactory) {
             RibbonProperties ribbon = RibbonProperties.from(config);
             int maxTotalConnections = ribbon.maxTotalConnections();
             long timeToLive = ribbon.poolKeepAliveTime();
             TimeUnit ttlUnit = ribbon.getPoolKeepAliveTimeUnits();
             return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
          }

          @Bean
          @ConditionalOnMissingBean(OkHttpClient.class)
          public OkHttpClient client(OkHttpClientFactory httpClientFactory,
                ConnectionPool connectionPool, IClientConfig config) {
             RibbonProperties ribbon = RibbonProperties.from(config);
             this.httpClient = httpClientFactory.createBuilder(false)
                   .connectTimeout(ribbon.connectTimeout(), TimeUnit.MILLISECONDS)
                   .readTimeout(ribbon.readTimeout(), TimeUnit.MILLISECONDS)
                   .followRedirects(ribbon.isFollowRedirects())
                   .connectionPool(connectionPool).build();
             return this.httpClient;
          }

          @PreDestroy
          public void destroy() {
             if (httpClient != null) {
                httpClient.dispatcher().executorService().shutdown();
                httpClient.connectionPool().evictAll();
             }
          }

       }

    }

    2.3、LoadBalancerAutoConfiguration

    * 把标注了@LoadBalanced注解的所有RestTemplate实例,添加一个LoadBalancerInterceptor(loadBalancerClient, requestFactory)

      – loadBalancerClient是在RibbonAutoConfiguration中指定的RibbonLoadBalancerClient(springClientFactory()) 类型bean

      – requestFactory是本配置类中指定的LoadBalancerRequestFactory类型bean

      – @LoadBalanced注解就是@Qualifier类注解,用于匹配bean的标志

    @Qualifier
       public @interface LoadBalanced {}

    * 执行RestTemplates时,会被这个拦截器拦截,

      ①LoadBalancerRequestFactory会根据传递进来的ClientHttpRequestExecution创建具体通信工具封装后的LoadBalancerRequest

      ②RestTemplates可以设置ClientHttpRequestFactory,默认为SimpleClientHttpRequestFactory来创建ClientHttpRequest

      ③LoadBalancerClient负载均衡器会根据serviceName选择合适的路径,进行请求

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(RestTemplate.class)
    @ConditionalOnBean(LoadBalancerClient.class)
    @EnableConfigurationProperties(LoadBalancerRetryProperties.class)
    public class LoadBalancerAutoConfiguration {

        @LoadBalanced
        @Autowired(required = false)
        private List<RestTemplate> restTemplates = Collections.emptyList();

        @Autowired(required = false)
        private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

        //把标注的了@LoadBalanced所有RestTemplate,添加一个LoadBalancerInterceptor(loadBalancerClient, requestFactory)
        @Bean
        public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
                final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
            return () -> restTemplateCustomizers.ifAvailable(customizers -> {
                for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                    for (RestTemplateCustomizer customizer : customizers) {
                        customizer.customize(restTemplate);
                    }
                }
            });
        }

        @Bean
        @ConditionalOnMissingBean
        public LoadBalancerRequestFactory loadBalancerRequestFactory(
                LoadBalancerClient loadBalancerClient) {
            return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
        }

        @Configuration(proxyBeanMethods = false)
        @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
        static class LoadBalancerInterceptorConfig {

            @Bean
            public LoadBalancerInterceptor ribbonInterceptor(
                    LoadBalancerClient loadBalancerClient,
                    LoadBalancerRequestFactory requestFactory) {
                return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
            }

            @Bean
            @ConditionalOnMissingBean
            public RestTemplateCustomizer restTemplateCustomizer(
                    final LoadBalancerInterceptor loadBalancerInterceptor) {
                return restTemplate -> {
                    List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                            restTemplate.getInterceptors());
                    list.add(loadBalancerInterceptor);
                    restTemplate.setInterceptors(list);
                };
            }

        }

        //

    }

    public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

        private LoadBalancerClient loadBalancer;
        private LoadBalancerRequestFactory requestFactory;
        public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
                                       LoadBalancerRequestFactory requestFactory) {
            this.loadBalancer = loadBalancer;
            this.requestFactory = requestFactory;
        }

        public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
            // for backwards compatibility
            this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
        }

        //执行RestTemplates时,会被这个拦截器拦截,
        //LoadBalancerRequestFactory会根据传递进来的ClientHttpRequestExecution创建具体通信工具封装后的LoadBalancerRequest
        //   RestTemplates可以设置ClientHttpRequestFactory,默认为SimpleClientHttpRequestFactory来创建ClientHttpRequest
        //LoadBalancerClient负载均衡器会根据serviceName选择合适的路径,进行请求
        //
        @Override
        public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
                                            final ClientHttpRequestExecution execution) throws IOException {
            final URI originalUri = request.getURI();
            String serviceName = originalUri.getHost();
            return this.loadBalancer.execute(serviceName,
                    this.requestFactory.createRequest(request, body, execution));
        }

    3、Name容器中6大可配置组件 

    3.1、概述

  • Spring Ribbon的6大可配置组件类及默认配置
  • 自动化配置接口

    描述

    默认实现

    说明

    IClientConfig

    Ribbon的客户端配置

    com.netflix.client.config.DefaultClientConfigImpl

     对如下组件及其他信息的配置项

    IRule

    Ribbon的负载均衡策略

    com.netflix.loadbalancer.ZoneAvoidanceRule

    该策略能在多区域环境下选出最佳区域的实例进行访问

    IPing

    Ribbon的实例检查策略                     

    com.netflix.loadbalancer.NoOpPing

    该检查策略是一个特殊的实现,实际上它并不会检查实例是否可用,而是始终返回true,默认所有的实例都是可用的

    ServerList<Server>

    服务实例清单维护机制

    com.netflix.loadbalancer.ConfigurationBasedServerList

    ServerListFilter<Server>

    服务实例清单过滤机制

    org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter

    该策略能够优先过滤出与请求调用方处于同一个区域的服务清单

    ILoadBalancer

    (负载均衡器主类)

    com.netflix.loadbalancer.ZoneAwareLoadBalancer

    该策略具备服务感知能力,封装了如上几个组件

    3.2、IClientConfig配置项组件

    3.2.1、IClientConfig接口

    * ClientName和NameSpace的获取

      这是为了隔离不同的服务调用端的配置

    * 其他接口就是属性的增删改查

    public interface IClientConfig {
       
       public String getClientName();
       public String getNameSpace();

       public void loadProperties(String clientName);
       public void loadDefaultValues();

       public Map<String, Object> getProperties();
       public boolean containsProperty(IClientConfigKey key);
       
       public String resolveDeploymentContextbasedVipAddresses();
       public int getPropertyAsInteger(IClientConfigKey key, int defaultValue);
       public String getPropertyAsString(IClientConfigKey key, String defaultValue);
       public boolean getPropertyAsBoolean(IClientConfigKey key, boolean defaultValue);
       public <T> T get(IClientConfigKey<T> key);
       public <T> T get(IClientConfigKey<T> key, T defaultValue);
       public <T> IClientConfig set(IClientConfigKey<T> key, T value);

    }

    3.2.2、DefaultClientConfigImpl实现类

    * 作用是保存了所有的配置项,基于Archaius实现,支持动态的获取配置项的最新值

    * 默认实现是DefaultClientConfigImpl,此类作用:

      ①保存各个组件的默认实现类,比如

        public static final String DEFAULT_NFLOADBALANCER_RULE_CLASSNAME = "com.netflix.loadbalancer.AvailabilityFilteringRule";

    public static final String DEFAULT_NFLOADBALANCER_CLASSNAME = "com.netflix.loadbalancer.ZoneAwareLoadBalancer";

    注:此默认配置在DefaultClientConfigImpl和RibbonClientConfiguration 配置bean加入到独立的服务上下文中均有指定

      ②保存各种属性的默认配置,支持动态比如

        public static final int DEFAULT_READ_TIMEOUT = 5000;

        public static final int DEFAULT_CONNECTION_MANAGER_TIMEOUT = 2000;

    public static final int DEFAULT_CONNECT_TIMEOUT = 2000;

    * loadProperties会加载环境对象中如下属性,保存到本地缓存Map<String, Object> properties,或者支持动态刷新的Map<String, DynamicStringProperty> dynamicProperties

      ①从Spring env中加载“ribbon.配置项”这类,作为默认配置

    使用 ribbon.<key>=<value> 的形式配置,例如全局配置连接超时时间:

    ribbon:

       ConnectTimeout: 250

    ②和加载“client名.ribbon.配置项”这类针对某个Client的配置信息

    指定客户端配置方式采用 <client>.ribbon.<key>=<value>,使用样例如下所示,同时,如果同时配置了全局配置和指定客户端配置,那么以指定客户端的配置为准。

    EUREKA-CLIENT:

       ribbon:

         listOfServers: localhost:8001,localhost:8002

    * 基本原理(更多原理见Archaius文档)

      – 基于Archaius作为数据源根据,对于Springboot项目来说,由ArchaiusAutoConfiguration进行自动配置,这个数据源一般为ConfigurableEnvironmentConfiguration

    此AbstractConfiguration实现类,以Spring容器的环境Environmen对象作为数据源进行获取,而不会对其进行反向设置

    也会组合其他的数据源,一并配置到ConfigurationManager.install(config);

      – 同时也注入了一个监听器ApplicationListener<EnvironmentChangeEvent>

    当监听 到环境配置修改时,会获取ConfigurableEnvironmentConfiguration中设置好的自动的动态刷新监听器,执行

    listener.configurationChanged(new ConfigurationEvent(source, type,key, value, beforeUpdate));

    这样Map<String, Object> properties,或者支持动态刷新的Map<String, DynamicStringProperty> dynamicProperties里对应的值就会被刷新

      – config.loadProperties(clientname)方法会从ConfigurableEnvironmentConfiguration中在抽取出指定格式key的键值对作为新的AbstractConfiguration实现类

       即SubsetConfiguration以实现数据源的隔离,例如SubsetConfiguration对抽象方法的实现,parent就是原始ConfigurableEnvironmentConfiguration配置

       getParentKey(key)会拼接clientname前缀

       public void addPropertyDirect(String key, Object value) {

            parent.addProperty(getParentKey(key), value);

        }

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

    public class DefaultClientConfigImpl implements IClientConfig {

        public static final Boolean DEFAULT_PRIORITIZE_VIP_ADDRESS_BASED_SERVERS = Boolean.TRUE;
        public static final String DEFAULT_NFLOADBALANCER_PING_CLASSNAME = "com.netflix.loadbalancer.DummyPing"; // DummyPing.class.getName();
        public static final String DEFAULT_NFLOADBALANCER_RULE_CLASSNAME = "com.netflix.loadbalancer.AvailabilityFilteringRule";
        public static final String DEFAULT_NFLOADBALANCER_CLASSNAME = "com.netflix.loadbalancer.ZoneAwareLoadBalancer";
        public static final boolean DEFAULT_USEIPADDRESS_FOR_SERVER = Boolean.FALSE;
        public static final String DEFAULT_CLIENT_CLASSNAME = "com.netflix.niws.client.http.RestClient";
        public static final String DEFAULT_VIPADDRESS_RESOLVER_CLASSNAME = "com.netflix.client.SimpleVipAddressResolver";
        public static final int DEFAULT_MAX_TOTAL_TIME_TO_PRIME_CONNECTIONS = 30000;
        //

        public void loadProperties(String restClientName) {
            //启用动态属性 如果开启属性将具有动态 性  这里也是唯一一处将该值设置成true的地方
            //所以如果仅仅是默认值 不支持动态属性的
            enableDynamicProperties = true;
            //设置clientName
            setClientName(restClientName);
            //加载默认属性
            //依赖Archaius获取对应key的配置值。但是 但是有一点需要特别注意。这里的 ConfigurationManager.getConfigInstance().getString()方法获取的配置和
            //例如我们配置的值是a,b,c 那么getStringValue获取的值是a,b,c  getString()获取的却是 a! 所以默认值字符串类型的 配置里面出现逗号那就有问题了。
            //
            loadDefaultValues();
            //这里可以看到底层的配置还是通过 Archaius来配置的 所以我们把配置写在classpathconfig.properties中是生效的
            //对于subset这个方法 举个例子可能会更清楚 例如我们在config文件中的配置是 coredy.ribbon.ReadTimeout
            //我们调用subset(”coredy“) 那就会给我们返回所有以coredy开头的Configuration
            Configuration props = ConfigurationManager.getConfigInstance().subset(restClientName);
            //遍历得到的Configuration 将属性放到全局的配置里面properties
            for (Iterator<String> keys = props.getKeys(); keys.hasNext(); ) {
                String key = keys.next();
                String prop = key;
                try {

    //指定了NameSpace的情况,默认你为ribbon,需要截断这个前缀
                    if (prop.startsWith(getNameSpace())) {
                        prop = prop.substring(getNameSpace().length() + 1);
                    }
                    //特别注意这里是使用getStringValue(props, key)来获取值得
                    //意为着 你配置的属性是a,b,c 那么最终在全局配置properties里面的值也是a,b,c
                    setPropertyInternal(prop, getStringValue(props, key));
                } catch (Exception ex) {
                    throw new RuntimeException(String.format("Property %s is invalid", prop));
                }
            }
            
            //
    }

    3.3、ServerList<Server>获取指定serviceId服务列表组件

    3.3.1、ServerList<T extends Server>接口

    * 定义获取指定serviceId服务列表的方法

    public interface ServerList<T extends Server> {

    //初始可用的服务列表
        public List<T> getInitialListOfServers();
        //经过ping进行心跳正常过滤后的最新可用的服务列表
        public List<T> getUpdatedListOfServers();   

    }

    * 一个Server对象表示一个服务实例的信息

    public class Server {

        public static final String UNKNOWN_ZONE = "UNKNOWN";
        private String host;
        private int port = 80;
        private String scheme;
        private volatile String id;//通常为 http域名+端口
        private volatile boolean isAliveFlag;
        private String zone = UNKNOWN_ZONE;
        private volatile boolean readyToServe = true;

        private MetaInfo simpleMetaInfo = new MetaInfo() {
            /**
             * 服务实例对应服务器的名称和服务组
             */

    @Override
            public String getAppName() {
                return null;
            }
            @Override
            public String getServerGroup() {
                return null;
            }
            /**
             * 服务实例的别名
             */
            @Override
            public String getServiceIdForDiscovery() {
                return null;
            }

            @Override
            public String getInstanceId() {
                return id;
            }
        };

        public Server(String host, int port) {
            this(null, host, port);
        }
        
        public Server(String scheme, String host, int port) {
            this.scheme = scheme;
            this.host = host;
            this.port = port;
            this.id = host + ":" + port;
            isAliveFlag = false;
        }

       //..

    }

    3.3.2、NacosServerList

    @Bean
    @ConditionalOnMissingBean
    @SuppressWarnings("unchecked")
    public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
       if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {
          return this.propertiesFactory.get(ServerListFilter.class, config, name);
       }
       ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
       filter.initWithNiwsConfig(config);
       return filter;
    }

    * 继承AbstractServerList,这里值初始化了过滤器实现类

      – 优先取NIWSServerListFilterClassName配置的,

      – 如果为null,取NIWSServerListFilterClassName类

      – 在RibbonClientConfiguration自动配置类中,默认配置为ZonePreferenceServerListFilter

    * NacosServerList实现类

      – 逻辑简单,直接借助Nacos提供的客户端NacosDiscoveryProperties 获取服务列表

    List<Instance> instances = discoveryProperties.namingServiceInstance().selectInstances(serviceId, true);

      – 再适配为ribbon需要的Server类型即可

      – 一个iClientConfig.getClientName(),即一个ServiceId对应一个NacosServerList

    public class NacosServerList extends AbstractServerList<NacosServer> {

       private NacosDiscoveryProperties discoveryProperties;

       private String serviceId;

       public NacosServerList(NacosDiscoveryProperties discoveryProperties) {
          this.discoveryProperties = discoveryProperties;
       }

       @Override
       public List<NacosServer> getInitialListOfServers() {
          return getServers();
       }

       @Override
       public List<NacosServer> getUpdatedListOfServers() {
          return getServers();
       }

       private List<NacosServer> getServers() {
          try {
             List<Instance> instances = discoveryProperties.namingServiceInstance()
                   .selectInstances(serviceId, true);
             return instancesToServerList(instances);
          }
          catch (Exception e) {
             throw new IllegalStateException(
                   "Can not get service instances from nacos, serviceId=" + serviceId,
                   e);
          }
       }

       private List<NacosServer> instancesToServerList(List<Instance> instances) {
          List<NacosServer> result = new ArrayList<>();
          if (null == instances) {
             return result;
          }
          for (Instance instance : instances) {
             result.add(new NacosServer(instance));
          }

          return result;
       }

       public String getServiceId() {
          return serviceId;
       }

       @Override
       public void initWithNiwsConfig(IClientConfig iClientConfig) {
          this.serviceId = iClientConfig.getClientName();
       }
    }

    3.4、ServerListFilter<T extends Server>服务列表过滤组件

    3.4.1、ServerListFilter接口

    public interface ServerListFilter<T extends Server> {

        public List<T> getFilteredListOfServers(List<T> servers);

    }

    3.4.2、ZoneAffinityServerListFilter实现类

    * getFilteredListOfServers的实现逻辑,分两步过滤

     – 初步过滤:

       对指定serverId下的所有实例列表默认使用ZoneAffinityPredicate使用过滤:过滤配置指定zone的服务实例

       一般的服务发现客户端会配置好这个zone值

     – 对初步过滤的结果再次判断

    ①配置zoneAffinity(默认flase)和zoneExclusive(默认flase)均没有true时,不过滤zone,选择所有实例列表

        ②当zoneExclusive为true,使用初步过滤的(指定zone过滤)

        ③当zoneAffinity为true,表示需要再次过滤判断:基于LoadBalancerStats判断这些初步过滤的服务实例的状态是否符合配置的阈值

          符合就使用过滤的,否则不过滤

          ((double) circuitBreakerTrippedCount) / instanceCount >= blackOutServerPercentageThreshold.get()

          || loadPerServer >= activeReqeustsPerServerThreshold.get()

          || (instanceCount – circuitBreakerTrippedCount) < availableServersThreshold.get())

    public class ZoneAffinityServerListFilter<T extends Server> extends
            AbstractServerListFilter<T> implements IClientConfigAware {

        private volatile boolean zoneAffinity = DefaultClientConfigImpl.DEFAULT_ENABLE_ZONE_AFFINITY;
        private volatile boolean zoneExclusive = DefaultClientConfigImpl.DEFAULT_ENABLE_ZONE_EXCLUSIVITY;
        private DynamicDoubleProperty activeReqeustsPerServerThreshold;
        private DynamicDoubleProperty blackOutServerPercentageThreshold;
        private DynamicIntProperty availableServersThreshold;
        private Counter overrideCounter;
        private ZoneAffinityPredicate zoneAffinityPredicate = new ZoneAffinityPredicate();
        
        private static Logger logger = LoggerFactory.getLogger(ZoneAffinityServerListFilter.class);
        
        String zone;
            
        public ZoneAffinityServerListFilter() {      
        }
        
        public ZoneAffinityServerListFilter(IClientConfig niwsClientConfig) {
            initWithNiwsConfig(niwsClientConfig);
        }
        
        //从配置组件中获取属性
        @Override
        public void initWithNiwsConfig(IClientConfig niwsClientConfig) {
            String sZoneAffinity = "" + niwsClientConfig.getProperty(CommonClientConfigKey.EnableZoneAffinity, false);
            if (sZoneAffinity != null){
                zoneAffinity = Boolean.parseBoolean(sZoneAffinity);
            }
            String sZoneExclusive = "" + niwsClientConfig.getProperty(CommonClientConfigKey.EnableZoneExclusivity, false);
            if (sZoneExclusive != null){
                zoneExclusive = Boolean.parseBoolean(sZoneExclusive);
            }
            if (ConfigurationManager.getDeploymentContext() != null) {
                zone = ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone);
            }
            activeReqeustsPerServerThreshold = DynamicPropertyFactory.getInstance().getDoubleProperty(niwsClientConfig.getClientName() + "." + niwsClientConfig.getNameSpace() + ".zoneAffinity.maxLoadPerServer", 0.6d);
            blackOutServerPercentageThreshold = DynamicPropertyFactory.getInstance().getDoubleProperty(niwsClientConfig.getClientName() + "." + niwsClientConfig.getNameSpace() + ".zoneAffinity.maxBlackOutServesrPercentage", 0.8d);
            availableServersThreshold = DynamicPropertyFactory.getInstance().getIntProperty(niwsClientConfig.getClientName() + "." + niwsClientConfig.getNameSpace() + ".zoneAffinity.minAvailableServers", 2);
            overrideCounter = Monitors.newCounter("ZoneAffinity_OverrideCounter");
            Monitors.registerObject("NIWSServerListFilter_" + niwsClientConfig.getClientName());
        }

        @Override
        public List<T> getFilteredListOfServers(List<T> servers) {
            if (zone != null && (zoneAffinity || zoneExclusive) && servers !=null && servers.size() > 0){
                //初步过滤:使用指定zone过滤的
                List<T> filteredServers = Lists.newArrayList(Iterables.filter(
                        servers, this.zoneAffinityPredicate.getServerOnlyPredicate()));
                //再次过滤判断,符合才使用初步过滤的,否则放行所有
                if (shouldEnableZoneAffinity(filteredServers)) {
                    return filteredServers;
                } else if (zoneAffinity) {
                    overrideCounter.increment();
                }
            }
            return servers;
        }
        
        //zoneAffinityzoneExclusive均没有true时,过滤
        //zoneExclusivetrue,使用初步过滤的(指定zone过滤)
        //zoneAffinitytrue,表示需要再次过滤:基于LoadBalancerStats判断这些初步过滤的服务实例的状态是否符合配置的阈值
        //                       符合就使用过滤的,否则不过滤
        private boolean shouldEnableZoneAffinity(List<T> filtered) {    
            if (!zoneAffinity && !zoneExclusive) {
                return false;
            }
            if (zoneExclusive) {
                return true;
            }
            LoadBalancerStats stats = getLoadBalancerStats();
            if (stats == null) {
                return zoneAffinity;
            } else {
                ZoneSnapshot snapshot = stats.getZoneSnapshot(filtered);
                double loadPerServer = snapshot.getLoadPerServer();
                int instanceCount = snapshot.getInstanceCount();            
                int circuitBreakerTrippedCount = snapshot.getCircuitTrippedCount();
                if (((double) circuitBreakerTrippedCount) / instanceCount >= blackOutServerPercentageThreshold.get()
                        || loadPerServer >= activeReqeustsPerServerThreshold.get()
                        || (instanceCount – circuitBreakerTrippedCount) < availableServersThreshold.get()) {
                    return false;
                } else {
                    return true;
                }
                
            }
        }
        
    }

    3.4.3、ZonePreferenceServerListFilter实现类

    @Bean
    @ConditionalOnMissingBean
    @SuppressWarnings("unchecked")
    public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
       if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {
          return this.propertiesFactory.get(ServerListFilter.class, config, name);
       }
       ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
       filter.initWithNiwsConfig(config);
       return filter;
    }

    * 在RibbonClientConfiguration自动配置中,默认实现为ZonePreferenceServerListFilter

    * ZonePreferenceServerListFilter继承ZoneAffinityServerListFilter

      当ZoneAffinityServerListFilter过滤失败(即调用父类过滤后,的实例数不变))时,使用指定zone(如有)进行过滤

    public class ZonePreferenceServerListFilter extends ZoneAffinityServerListFilter<Server> {

       private String zone;

       @Override
       public void initWithNiwsConfig(IClientConfig niwsClientConfig) {
          super.initWithNiwsConfig(niwsClientConfig);
          if (ConfigurationManager.getDeploymentContext() != null) {
             this.zone = ConfigurationManager.getDeploymentContext()
                   .getValue(ContextKey.zone);
          }
       }

       @Override
       public List<Server> getFilteredListOfServers(List<Server> servers) {
          List<Server> output = super.getFilteredListOfServers(servers);
          if (this.zone != null && output.size() == servers.size()) {
             List<Server> local = new ArrayList<>();
             for (Server server : output) {
                if (this.zone.equalsIgnoreCase(server.getZone())) {
                   local.add(server);
                }
             }
             if (!local.isEmpty()) {
                return local;
             }
          }
          return output;
       }

    }

    3.5、IPing连通性检测组件

    //各个组件的默认实现
        @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();
        }

    * 默认的逻辑始终为true,即不会进行ping处理

      这时为了性能考虑,不需要主动的去ping每一个实例的连通性,因为在ZoneAwareLoadBalancer中会进行过滤,

      如果实例有问题,那么必定存在socket相关的异常,那么就会触发此实例的断路,

    public interface IPing {
        public boolean isAlive(Server server);
    }

    public class DummyPing extends AbstractLoadBalancerPing {

        public DummyPing() {
        }

        public boolean isAlive(Server server) {
            return true;
        }

        @Override
        public void initWithNiwsConfig(IClientConfig clientConfig) {
        }
    }

    3.6、IRule选取规则组件

    3.6.1、IRule接口

    由接口方法即可知道:核心方法为choose选择一个serverId下的一个服务实例,而且逻辑或基于ILoadBalancer实现类

    public interface IRule{

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

    3.6.2、ZoneAvoidanceRule实现类

    3.6.2.1、主要流程

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

    * 是RibbonAutoConfiguration指定的默认规则

    * 其核心逻辑是:当前serviceId下的每一个服务实例经过其内配置的compositePredicate进行过滤后的候选服务实例集合,再进行轮询即可

      注:每一服务实例都需要走一遍下面的流程,比如ZoneAvoidanceRule.getAvailableZones方法会调用多此(其实没必要)

      – ZoneAvoidancePredicate校验

        ①基于LoadBalancerStats获取当前服务实例对应的zoneSnapshot

    使用ZoneAvoidanceRule.getAvailableZones计算出可用有效的分区availableZones,再进行下一步AvailabilityPredicate的校验

    getAvailableZones具体算法见下

        ②如果此服务实例对应的zone或者zoneSnapshot 不存在,那么会直接放行,进行下一步AvailabilityPredicate的校验

      – AvailabilityPredicate校验

         ①所有实例不处于断路状态

         ②所有实例的活跃请求数小于niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit配置,默认为int最大值

    – 两个Predicate校验都满足后,最为候选加入List<Server> eligible

       如果最终候选为0,那么再遍历一次,符合AvailabilityPredicate这个校验即可

       如果二次校验AvailabilityPredicate还是失败,那么默认全部实例放行

      – 对最终结果List<Server> eligible进行轮询

       

    public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule {

        public abstract AbstractServerPredicate getPredicate();

        @Override
        public Server choose(Object key) {
            ILoadBalancer lb = getLoadBalancer();
            //getPredicate()就是子类ZoneAvoidanceRule CompositePredicate compositePredicate
            Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
            if (server.isPresent()) {
                return server.get();
            } else {
                return null;
            }
        }
    }

    public class ZoneAvoidanceRule extends PredicateBasedRule {

        private static final Random random = new Random();
        private CompositePredicate compositePredicate;
        
        public ZoneAvoidanceRule() {
            super();
            ZoneAvoidancePredicate zonePredicate = new ZoneAvoidancePredicate(this);
            AvailabilityPredicate availabilityPredicate = new AvailabilityPredicate(this);
            compositePredicate = createCompositePredicate(zonePredicate, availabilityPredicate);
        }
        
        private CompositePredicate createCompositePredicate(ZoneAvoidancePredicate p1, AvailabilityPredicate p2) {
            return CompositePredicate.withPredicates(p1, p2)
                                 .addFallbackPredicate(p2)
                                 .addFallbackPredicate(AbstractServerPredicate.alwaysTrue())
                                 .build();
            
        }

    //..

    }

    //CompositePredicate的父类,其中chooseRoundRobinAfterFiltering如下
    public abstract class AbstractServerPredicate implements Predicate<PredicateKey> {
        
        protected IRule rule;
        private volatile LoadBalancerStats lbStats;
        
        private final Random random = new Random();
        
        private final AtomicInteger nextIndex = new AtomicInteger();
                
        private final Predicate<Server> serverOnlyPredicate =  new Predicate<Server>() {
            @Override
            public boolean apply(@Nullable Server input) {                    
                return AbstractServerPredicate.this.apply(new PredicateKey(input));
            }
        };

        public static AbstractServerPredicate alwaysTrue() {
            return new AbstractServerPredicate() {        
                @Override
                public boolean apply(@Nullable PredicateKey input) {
                    return true;
                }
            };
        }

        //同理:在过滤之后进行随机
        public Optional<Server> chooseRandomlyAfterFiltering(List<Server> servers) {
            List<Server> eligible = getEligibleServers(servers);
            if (eligible.size() == 0) {
                return Optional.absent();
            }
            return Optional.of(eligible.get(random.nextInt(eligible.size())));
        }
        
        //在过滤之后进行随机轮询
        public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers) {
            List<Server> eligible = getEligibleServers(servers);
            if (eligible.size() == 0) {
                return Optional.absent();
            }
            return Optional.of(eligible.get(incrementAndGetModulo(eligible.size())));
        }

         public List<Server> getEligibleServers(List<Server> servers) {
            return getEligibleServers(servers, null);
        }
     
        //遍历每一服务实例,使用子类的apply进行校验过滤,返回符合的候选集合
        public List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) {
            if (loadBalancerKey == null) {
                return ImmutableList.copyOf(Iterables.filter(servers, this.getServerOnlyPredicate()));            
            } else {
                List<Server> results = Lists.newArrayList();
                for (Server server: servers) {
                    if (this.apply(new PredicateKey(loadBalancerKey, server))) {
                        results.add(server);
                    }
                }
                return results;            
            }
        }

        //轮询
        private int incrementAndGetModulo(int modulo) {
            for (;;) {
                int current = nextIndex.get();
                int next = (current + 1) % modulo;
                if (nextIndex.compareAndSet(current, next) && current < modulo)
                    return current;
            }
        }
        //
    }

    public class ZoneAvoidancePredicate extends  AbstractServerPredicate {

        private volatile DynamicDoubleProperty triggeringLoad = new DynamicDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer.triggeringLoadPerServerThreshold", 0.2d);
        private volatile DynamicDoubleProperty triggeringBlackoutPercentage = new DynamicDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer.avoidZoneWithBlackoutPercetage", 0.99999d);
        private static final DynamicBooleanProperty ENABLED = DynamicPropertyFactory
                .getInstance().getBooleanProperty(
                        "niws.loadbalancer.zoneAvoidanceRule.enabled", true);

        public ZoneAvoidancePredicate(LoadBalancerStats lbStats,
                IClientConfig clientConfig) {
            super(lbStats, clientConfig);
            initDynamicProperties(clientConfig);
        }
        private void initDynamicProperties(IClientConfig clientConfig) {
            if (clientConfig != null) {
                triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(
                        "ZoneAwareNIWSDiscoveryLoadBalancer." + clientConfig.getClientName() + ".triggeringLoadPerServerThreshold", 0.2d);

                triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(
                        "ZoneAwareNIWSDiscoveryLoadBalancer." + clientConfig.getClientName() + ".avoidZoneWithBlackoutPercetage", 0.99999d);
            }
            
        }

        @Override
        public boolean apply(@Nullable PredicateKey input) {
            if (!ENABLED.get()) {
                return true;
            }
            String serverZone = input.getServer().getZone();
            if (serverZone == null) {
                return true;
            }
            LoadBalancerStats lbStats = getLBStats();
            if (lbStats == null) {
                return true;
            }
            if (lbStats.getAvailableZones().size() <= 1) {
                return true;
            }
            Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
            if (!zoneSnapshot.keySet().contains(serverZone)) {
                return true;
            }
            Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
            if (availableZones != null) {
                return availableZones.contains(input.getServer().getZone());
            } else {
                return false;
            }
        }    
    }

    public class AvailabilityPredicate extends  AbstractServerPredicate {
            
        private static final DynamicBooleanProperty CIRCUIT_BREAKER_FILTERING =
                DynamicPropertyFactory.getInstance().getBooleanProperty("niws.loadbalancer.availabilityFilteringRule.filterCircuitTripped", true);
        private static final DynamicIntProperty ACTIVE_CONNECTIONS_LIMIT =
                DynamicPropertyFactory.getInstance().getIntProperty("niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit", Integer.MAX_VALUE);
        private ChainedDynamicProperty.IntProperty activeConnectionsLimit = new ChainedDynamicProperty.IntProperty(ACTIVE_CONNECTIONS_LIMIT);
            
        public AvailabilityPredicate(LoadBalancerStats lbStats, IClientConfig clientConfig) {
            super(lbStats, clientConfig);
            initDynamicProperty(clientConfig);
        }
        private void initDynamicProperty(IClientConfig clientConfig) {
            String id = "default";
            if (clientConfig != null) {
                id = clientConfig.getClientName();
                activeConnectionsLimit = new ChainedDynamicProperty.IntProperty(id + "." + clientConfig.getNameSpace() + ".ActiveConnectionsLimit", ACTIVE_CONNECTIONS_LIMIT);
            }               
        }
        
        @Override
        public boolean apply(@Nullable PredicateKey input) {
            LoadBalancerStats stats = getLBStats();
            if (stats == null) {
                return true;
            }
            return !shouldSkipServer(stats.getSingleServerStat(input.getServer()));
        }
        
        private boolean shouldSkipServer(ServerStats stats) {        
            if ((CIRCUIT_BREAKER_FILTERING.get() && stats.isCircuitBreakerTripped())
                    || stats.getActiveRequestsCount() >= activeConnectionsLimit.get()) {
                return true;
            }
            return false;
        }

    }

    3.6.2.2、getAvailableZones获取有效分区

     getAvailableZones(Map<String, ZoneSnapshot> snapshot, double triggeringLoad, double triggeringBlackoutPercentage)

    * 参数:

      ①Map<String, ZoneSnapshot> snapshot每一个分区的快照,是基于LoadBalancerStats获取的

    对一个分区中的所有服务实例进行计算后封装为new ZoneSnapshot(instanceCount, circuitBreakerTrippedCount, activeConnectionsCount, loadPerServer)

    – instanceCount:当前服务数

    – circuitBreakerTrippedCount:当前滑动窗口内断路的服务数

    – activeConnectionsCount:当前滑动窗口内此分区所有服务实例的有效连接总数数

    – LoadPerServer =  activeConnectionsCount / (instanceCount – circuitBreakerTrippedCount)–此值越大表示,此分区剩余可用连接越少,负载就越大

      ②triggeringLoad和triggeringBlackoutPercentage都是配置的阈值

    triggeringLoadPerServerThreshold,默认为0.2

    avoidZoneWithBlackoutPercetage,默认为0.99999d

    * getAvailableZones流程

    ①获取Set<String> availableZones当前可用的候选的Zones集合,取Map<String, ZoneSnapshot> snapshot的keys

      ②获取配置值

    triggeringLoadPerServerThreshold,默认为0.2

    avoidZoneWithBlackoutPercetage,默认为0.99999d

      ③去除服务实例数为0的分区

      ④去除 loadPerServer小于0 或者 断路数/服务实例数>= avoidZoneWithBlackoutPercetage的分区

      ⑤计算LoadPerServer 最大的几个分区到worstZones集合,这个worstZones集合最大LoadPerServer 和最下LoadPerServer 相差必须小于0.000001d,

    如果最大的LoadPerServer 小于triggeringLoadPerServerThreshold且上面步骤没有排除过任何一个分区不存在,那么就直接availableZones,而不用过滤worstZones

    ⑥对worstZones进行过滤,

    获取在worstZones中所有分区的实例数总数,在此数范围内随机选择一个数,遍历worstZones,第一个实例数小于此数的分区为选中服务

    从availableZones中去除此分区

    ⑦最终得到Set<String> availableZones

    static Map<String, ZoneSnapshot> createSnapshot(LoadBalancerStats lbStats) {
        Map<String, ZoneSnapshot> map = new HashMap<String, ZoneSnapshot>();
        for (String zone : lbStats.getAvailableZones()) {
            ZoneSnapshot snapshot = lbStats.getZoneSnapshot(zone);
            map.put(zone, snapshot);
        }
        return map;
    }

     //LoadBalancerStats
    public ZoneSnapshot getZoneSnapshot(List<? extends Server> servers) {
        if (servers == null || servers.size() == 0) {
            return new ZoneSnapshot();
        }
        int instanceCount = servers.size();
        int activeConnectionsCount = 0;
        int activeConnectionsCountOnAvailableServer = 0;
        int circuitBreakerTrippedCount = 0;
        double loadPerServer = 0;
        long currentTime = System.currentTimeMillis();
        for (Server server: servers) {
            ServerStats stat = getSingleServerStat(server);   
            if (stat.isCircuitBreakerTripped(currentTime)) {
                circuitBreakerTrippedCount++;
            } else {
                activeConnectionsCountOnAvailableServer += stat.getActiveRequestsCount(currentTime);
            }
            activeConnectionsCount += stat.getActiveRequestsCount(currentTime);
        }
        if (circuitBreakerTrippedCount == instanceCount) {
            if (instanceCount > 0) {
                // should be NaN, but may not be displayable on Epic
                loadPerServer = –1;
            }
        } else {
            loadPerServer = ((double) activeConnectionsCountOnAvailableServer) / (instanceCount – circuitBreakerTrippedCount);
        }
        return new ZoneSnapshot(instanceCount, circuitBreakerTrippedCount, activeConnectionsCount, loadPerServer);
    }

    public static Set<String> getAvailableZones(
            Map<String, ZoneSnapshot> snapshot, double triggeringLoad,
            double triggeringBlackoutPercentage) {
        if (snapshot.isEmpty()) {
            return null;
        }
        Set<String> availableZones = new HashSet<String>(snapshot.keySet());
        if (availableZones.size() == 1) {
            return availableZones;
        }
        Set<String> worstZones = new HashSet<String>();
        double maxLoadPerServer = 0;
        boolean limitedZoneAvailability = false;

        for (Map.Entry<String, ZoneSnapshot> zoneEntry : snapshot.entrySet()) {
            String zone = zoneEntry.getKey();
            ZoneSnapshot zoneSnapshot = zoneEntry.getValue();
            int instanceCount = zoneSnapshot.getInstanceCount();
            if (instanceCount == 0) {
                availableZones.remove(zone);
                limitedZoneAvailability = true;
            } else {
                double loadPerServer = zoneSnapshot.getLoadPerServer();
                if (((double) zoneSnapshot.getCircuitTrippedCount())
                        / instanceCount >= triggeringBlackoutPercentage
                        || loadPerServer < 0) {
                    availableZones.remove(zone);
                    limitedZoneAvailability = true;
                } else {
                    if (Math.abs(loadPerServer – maxLoadPerServer) < 0.000001d) {
                        // they are the same considering double calculation
                        // round error
                        worstZones.add(zone);
                    } else if (loadPerServer > maxLoadPerServer) {
                        maxLoadPerServer = loadPerServer;
                        worstZones.clear();
                        worstZones.add(zone);
                    }
                }
            }
        }

        if (maxLoadPerServer < triggeringLoad && !limitedZoneAvailability) {
            // zone override is not needed here
            return availableZones;
        }
        String zoneToAvoid = randomChooseZone(snapshot, worstZones);
        if (zoneToAvoid != null) {
            availableZones.remove(zoneToAvoid);
        }
        return availableZones;

    }

    static String randomChooseZone(Map<String, ZoneSnapshot> snapshot,
            Set<String> chooseFrom) {
        if (chooseFrom == null || chooseFrom.size() == 0) {
            return null;
        }
        String selectedZone = chooseFrom.iterator().next();
        if (chooseFrom.size() == 1) {
            return selectedZone;
        }
        int totalServerCount = 0;
        for (String zone : chooseFrom) {
            totalServerCount += snapshot.get(zone).getInstanceCount();
        }
        int index = random.nextInt(totalServerCount) + 1;
        int sum = 0;
        for (String zone : chooseFrom) {
            sum += snapshot.get(zone).getInstanceCount();
            if (index <= sum) {
                selectedZone = zone;
                break;
            }
        }
        return selectedZone;
    }

    3.7、ILoadBalancer总控组件

    3.7.1、ILoadBalancer接口

    接口作用是服务实例的增改查

    public interface ILoadBalancer {

       //添加服务实例
       public void addServers(List<Server> newServers);
       //选择一个服务实例
       public Server chooseServer(Object key);
       //LB这边把一个指定的服务标为下线
       public void markServerDown(Server server);
       @Deprecated
       public List<Server> getServerList(boolean availableOnly);
       //只返回可用的服务实例
        public List<Server> getReachableServers();
        //所有服务实例
        public List<Server> getAllServers();
    }

    3.7.1.1、接口作用

    * 是负载均衡器主类ILoadBalancer接口的一个实现,ILoadBalancer是面向客户调用的接口

    该策略具备复制均衡的所有能力,因为封装了必要的几个组件

    * 一个serviceId维度有一个自己的ILoadBalancer实现类

    * 在RibbonClientConfiguration 中的默认配置为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);
       }
       return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList, serverListFilter, serverListUpdater);
    }

    ①IClientConfig:Ribbon的客户端配置,对如下组件及其他信息的配置,默认为DefaultClientConfigImpl

    ②IRule:Ribbon的负载均衡策略,该策略能在多区域环境下选出最佳区域的实例进行访问,默认为ZoneAvoidanceRule

    ③IPing Ribbon:实例连通性检查策略,默认NoOpPing该检查策略是一个特殊的实现,实际上它并不会检查实例是否可用,而是始终返回true,默认所有的实例都是可用的

    ④ServerList<Server> :服务实例清单维护机制,默认为.ConfigurationBasedServerList  

    ⑤ServerListFilter<Server>:服务实例清单过滤机制,默认为ZonePreferenceServerListFilter 该策略能够优先过滤出与请求调用方处于同一个区域的服务清单

      ⑥ServerListUpdater:进行动态服务列表的更新,默认为PollingServerListUpdater,这时一个定时拉取原始服务列表的策略

    3.7.1.2、接口体系

     ①ILoadBalancer接口的,定义了service实例的增删查改

    如addServers(List<Server> newServers)、chooseServer(Object key)等

     ②BaseLoadBlance,实现了整体的功能,如下

       – 抽象父类AbstractLoadBalancer,仅仅是定义了枚举ServerGroup:ALL,STATUS_UP,STATUS_NOT_UP

       – 两个List保存此服务的可用的所有的服务实例列表,以读写锁来处理线程安全

         此serviceId的所有的服务实例:List<Server> allServerList = Collections .synchronizedList(new ArrayList<Server>());

         此serviceId的上线的服务实例:List<Server> upServerList = Collections.synchronizedList(new ArrayList<Server>());

       – 通过IPing定时的对服务实例列表的可用性进行检查

       – 通过IRule实现ILoadBalancer接口chooseServer方法以选择指定策略的服务实例

       – 通过LoadBalancerStats记录了每个服务实例的状态ServerStats和每个区域的状态zoneStatsMap,

    包括访问次数、响应时间、熔断(时间)控制等等,通过zone划分了服务实例

    ③LoadBalancerContext

    – 此类的作用类似一个记录工具类,所有方法都需要指定LoadBalancerStats的ServerStats参数

    – 穿插在请调用的整个流程,提供记录和更新服务状态ServerStats的功能

    比如在打开请求时,会调用noteOpenConnectio(ServerStats serverStats)n方法,此方法会把对应的ServerStats的活跃请求指标activeRequestsCount + 1

     ④DynamicServerListLoadBalancer

        – 从指定的ServerList<T>实现类,如Eruka的DiscoveryEnabledNIWSServerList实现类,会获取所有服务列表,执行BaseLoadBlance父类的setServersList

          就会写入BaseLoadBlance的两个List保存此服务的可用的所有的服务实例列表

    – 基于服务列表构建好LoadBalancerStats的两个以Zone划分的Map

      Map<String, ZoneStats> zoneStatsMap ;

      Map<String, List<? extends Server>> upServerListZoneMap ;

    – 会基于关联的ServerListUpdater进行动态服务列表的更新,默认为PollingServerListUpdater,这时一个定时拉取默认1秒)原始服务列表的策略

      会从serverListImpl.getUpdatedListOfServers()获取最新的数据,使用ServerListFilter.getFilteredListOfServers(servers)过滤以下,调用setServersList会更新各种地表

      BaseLoadBlance的allServerList 和upServerList

      DynamicServerListLoadBalancer的Map<String, List<? extends Server>> upServerListZoneMap

      ZoneAwareLoadBalancer的每个zone对应的BaseLoadBalancer的allServerList 和upServerList

      拉取到最新的服务列表后,重写走上面④的流程

     ⑤ZoneAwareLoadBalancer

       – 为每一个分区创建一个BaseLoadBlance,即ConcurrentHashMap<String, BaseLoadBalancer> balancers

         主要是BaseLoadBalancer类型,其内的

       – 重写chooseServer方法

    如果只有一个分区,使用super.chooseServer(key);如果有多个分区,先通过LoadBalancerStats获取所有分区的状态快照,

    使用ZoneAvoidanceRule.getAvailableZones计算出可用有效的分区

        再使用ZoneAvoidanceRule.randomChooseZone随机选择一个分区,使用balancers 表对应BaseLoadBalancer的chooseServer选择最终的服务实例

       – 注:此分区过滤的功能最终基于ZoneAvoidanceRule进行选择的逻辑是部分重复的,那么这里为什么还需要进行重写

         – 用户这可以自定义的ZoneAwareLoadBalancer的rule,这样ZoneAwareLoadBalancer会帮我们先过滤好分区,然后选择自己设置的rule进行处理

        这样就复用了分区的功能

    例如可以使用WeightedResponseTimeRule以平均响应时间作为权重进行随机选择

    3.7.2、BaseLoadBlance

    3.7.2.1、提供整体的功能

    实现了整体的功能,如下

    * 在构造方法在对主要的成员属性进行初始化

      – config = clientConfig;

      – setRule(rule);

      – setPing(ping);

      – setLoadBalancerStats(stats); //一般会new LoadBalancerStats(clientConfig.getClientName());

      – 设置pingIntervalTime默认30s、maxTotalPingTime默认2

        其中一个间隔pingIntervalTime的PingTask

    * 维护存此服务的可用的所有的服务实例列表synchronizedList(new ArrayList<Server>()) List<Server> allServerList和upServerList,

      – 其中一个重要的方法setServersList(List<Server> lsrv)用于新增或者初始化服务实例列表,流程如下

    ①写锁加锁writeLock.lock

    ②遍历参数List<Server> lsr,添加到临时List allServers

    ③和原allServerList比较,如果个数不同,通知配置List<ServerListChangeListener> changeListeners监听器,触发serverListChanged(oldList, newList)方法

    ④是否与实例提前创建连接,在isEnablePrimingConnections为true时(默认为false),使用PrimeConnections对象

      会为每一个服务实例创建执行一个异步Future<Boolean>会去访问实例服务器,如果成功值为true,否则为false

    ⑤复制到最新的服务列表allServerList

      如果ping == null || ping.getClass().getName().equals(DummyPing.class.getName()),也直接赋值到upServerList

      注:这里直接覆盖没有关系,因为server重写了equal方法: svc.getId().equals(this.getId());

    ⑥写锁解锁writeLock.unlock();

       – 线程安全保障

         底表allServerList和upServerList是synchronizedList

         获取allServerList和upServerList信息时,一般就是直接返回两个集合成员,并使用 Collections.unmodifiableList

    setServersList以读写锁来处理线程安全,是为了与Pinger时获取待检查列表,进行安全控制

    进一步,在DynamicServerListLoadBalancer使用的Map<String, List<? extends Server>> upServerListZoneMap在setServersList里会同步更新,获取是是直接访问upServerListZoneMap

    * 通过IPing实现类定时的对服务实例列表的可用性进行检查

      – 默认始终返回true,对于Eureka的实现类为NIWSDiscoveryPing

      – 定时或者快速执行的PingTask任务为 new Pinger(pingStrategy).runPinger();,其中pingStrategy为SerialPingStrategy,流程如下

    注:ping == null || ping.getClass().getName().equals(DummyPing.class.getName())时此任务不执行

    ①加锁的获取allServerList的快照集合

    ②使用pingStrategy的pingServers(快照集合),获取每一个实例的ping结果,封装为result[]

    ③用result[]最新的isAlive结果覆盖原allServerList的isAlive状态,其中刷选为true的还赋值到upServerList

       
    * 通过IRule实现chooseServer方法以选择指定策略的服务实例

      rule.choose(key);

    * 保存对应的LoadBalancerStats

      – 此值可以是构造方法传递,如果没有传递使用IClientConfig进行默认创建。一般会new LoadBalancerStats(clientConfig.getClientName());

    – 记录了每个服务实例的状态ServerStats和每个区域的状态zoneStatsMap,包括访问次数、响应时间、熔断(时间)控制等等,

    通过zone划分了服务实例

    public class BaseLoadBalancer extends AbstractLoadBalancer implements PrimeConnections.PrimeConnectionListener, IClientConfigAware {

        private final static IRule DEFAULT_RULE = new RoundRobinRule();
        private final static SerialPingStrategy DEFAULT_PING_STRATEGY = new SerialPingStrategy();
        private static final String DEFAULT_NAME = "default";
        private static final String PREFIX = "LoadBalancer_";
        protected IRule rule = DEFAULT_RULE;
        protected IPingStrategy pingStrategy = DEFAULT_PING_STRATEGY;
        protected IPing ping = null;
        protected String name = DEFAULT_NAME;

        protected volatile List<Server> allServerList = Collections.synchronizedList(new ArrayList<Server>());
        protected volatile List<Server> upServerList = Collections.synchronizedList(new ArrayList<Server>());
        protected ReadWriteLock allServerLock = new ReentrantReadWriteLock();
        protected ReadWriteLock upServerLock = new ReentrantReadWriteLock();

        protected Timer lbTimer = null;
        protected int pingIntervalSeconds = 10;
        protected int maxTotalPingTimeSeconds = 5;
        protected Comparator<Server> serverComparator = new ServerComparator();
        protected AtomicBoolean pingInProgress = new AtomicBoolean(false);

        protected LoadBalancerStats lbStats;
        private IClientConfig config;

        private volatile Counter counter = Monitors.newCounter("LoadBalancer_ChooseServer");

        private PrimeConnections primeConnections;
        private volatile boolean enablePrimingConnections = false;
        

        
        private List<ServerListChangeListener> changeListeners = new CopyOnWriteArrayList<ServerListChangeListener>();
        private List<ServerStatusChangeListener> serverStatusListeners = new CopyOnWriteArrayList<ServerStatusChangeListener>();

        //配置默认组件,进行ping都是任务的启动
        public BaseLoadBalancer() {
            this.name = DEFAULT_NAME;
            this.ping = null;
            setRule(DEFAULT_RULE);
            setupPingTask();
            lbStats = new LoadBalancerStats(DEFAULT_NAME);
        }
        //其他构造器,略
        public BaseLoadBalancer(IClientConfig config) {
            initWithNiwsConfig(config);
        }
        public BaseLoadBalancer(IClientConfig config, IRule rule, IPing ping) {
            initWithConfig(config, rule, ping, createLoadBalancerStatsFromConfig(config));
        }
        //根据配置IClientConfig类进行构造
        void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping, LoadBalancerStats stats) {
            this.config = clientConfig;
            String clientName = clientConfig.getClientName();
            this.name = clientName;

            int pingIntervalTime = Integer.parseInt(""+ clientConfig.getProperty(CommonClientConfigKey.NFLoadBalancerPingInterval, Integer.parseInt("30")));
            int maxTotalPingTime = Integer.parseInt(""+ clientConfig.getProperty(CommonClientConfigKey.NFLoadBalancerMaxTotalPingTime,Integer.parseInt("2")));
            setPingInterval(pingIntervalTime);
            setMaxTotalPingTime(maxTotalPingTime);
            setRule(rule);
            setPing(ping);
            setLoadBalancerStats(stats);
            rule.setLoadBalancer(this);
            if (ping instanceof AbstractLoadBalancerPing) {
                ((AbstractLoadBalancerPing) ping).setLoadBalancer(this);
            }
            boolean enablePrimeConnections = clientConfig.get(CommonClientConfigKey.EnablePrimeConnections, DefaultClientConfigImpl.DEFAULT_ENABLE_PRIME_CONNECTIONS);
            if (enablePrimeConnections) {
                this.setEnablePrimingConnections(true);
                PrimeConnections primeConnections = new PrimeConnections(
                        this.getName(), clientConfig);
                this.setPrimeConnections(primeConnections);
            }
            init();

        }

        //setter/setter
        public void addServerListChangeListener(ServerListChangeListener listener) {
            changeListeners.add(listener);
        }
        public void removeServerListChangeListener(ServerListChangeListener listener) {
            changeListeners.remove(listener);
        }
        public void addServerStatusChangeListener(ServerStatusChangeListener listener) {serverStatusListeners.add(listener);}
        public void removeServerStatusChangeListener(ServerStatusChangeListener listener) {serverStatusListeners.remove(listener); }
        public IClientConfig getClientConfig() {
           return config;
        }

        void setupPingTask() {
            //Iping为空或者为DummyPing实现类,可跳过
            if (canSkipPing()) {return;}
            if (lbTimer != null) {
                lbTimer.cancel();
            }
            lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + name, true);
            lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000);
            //手动触发一下PingTask
            forceQuickPing();
        }

        //添加服务
        @Override
        public void addServers(List<Server> newServers) {
            if (newServers != null && newServers.size() > 0) {
                try {
                    ArrayList<Server> newList = new ArrayList<Server>();
                    newList.addAll(allServerList);
                    newList.addAll(newServers);
                    setServersList(newList);
                } catch (Exception e) { }
            }
        }
        //使用写锁,锁住allServerLock
        public void setServersList(List lsrv) {
            Lock writeLock = allServerLock.writeLock();
            ArrayList<Server> newServers = new ArrayList<Server>();
            //添加写锁
            writeLock.lock();
            try {
                ArrayList<Server> allServers = new ArrayList<Server>();
                for (Object server : lsrv) {
                    if (server == null) {continue;}
                    if (server instanceof String) {server = new Server((String) server);}
                    if (server instanceof Server) {
                        allServers.add((Server) server);
                    }
                    else {throw new IllegalArgumentException();
                    }
                }

                //触发器处理
                boolean listChanged = false;
                if (!allServerList.equals(allServers)) {
                    listChanged = true;
                    if (changeListeners != null && changeListeners.size() > 0) {
                       List<Server> oldList = ImmutableList.copyOf(allServerList);
                       List<Server> newList = ImmutableList.copyOf(allServers);                   
                       for (ServerListChangeListener l: changeListeners) {
                           try {
                               l.serverListChanged(oldList, newList);
                           } catch (Exception e) {}
                       }
                    }
                }
                //是否提前建立连接
                if (isEnablePrimingConnections()) {
                    for (Server server : allServers) {
                        if (!allServerList.contains(server)) {
                            server.setReadyToServe(false);
                            newServers.add((Server) server);
                        }
                    }
                    if (primeConnections != null) {
                        primeConnections.primeConnectionsAsync(newServers, this);
                    }
                }
                //复制最新的服务列表
                allServerList = allServers;
                if (canSkipPing()) {
                    for (Server s : allServerList) {
                        s.setAlive(true);
                    }
                    upServerList = allServerList;
                } else if (listChanged) {
                    forceQuickPing();
                }
            } finally {
                writeLock.unlock();
            }
        }

        //PingTask任务
        class PingTask extends TimerTask {
            public void run() {
                try {
                   new Pinger(pingStrategy).runPinger();
                } catch (Exception e) { }
            }
        }
        //
        class Pinger {
            private final IPingStrategy pingerStrategy;
            public Pinger(IPingStrategy pingerStrategy) {this.pingerStrategy = pingerStrategy;}
            public void runPinger() throws Exception {
                //幂等处理
                if (!pingInProgress.compareAndSet(false, true)) { return; }
                // we are "in" – we get to Ping
                Server[] allServers = null;
                boolean[] results = null;
                Lock allLock = null;
                Lock upLock = null;
                try {
                    //加把读锁,获取当前的服务列表的数组快照allServers,接着就释放锁了
                    allLock = allServerLock.readLock();
                    allLock.lock();
                    allServers = allServerList.toArray(new Server[allServerList.size()]);
                    allLock.unlock();

                    //使用指定的ping策略对allServers里的每一个服务进行ping
                    int numCandidates = allServers.length;
                    //默认为SerialPingStrategy,会遍历服务,调用IPing实现类ping Results[i] = ping.isAlive(servers[i]);
                    //默认的IPing实现类为NoOpPing,始终返回true
                    results = pingerStrategy.pingServers(ping, allServers);

                    final List<Server> newUpList = new ArrayList<Server>();
                    final List<Server> changedServers = new ArrayList<Server>();

                    //根据ping的结果形成新的newUpList,使用读写多进行upServerList复制
                    for (int i = 0; i < numCandidates; i++) {
                        boolean isAlive = results[i];
                        Server svr = allServers[i];
                        boolean oldIsAlive = svr.isAlive();
                        svr.setAlive(isAlive);
                        if (oldIsAlive != isAlive) {changedServers.add(svr); }
                        if (isAlive) {newUpList.add(svr);}
                    }
                    upLock = upServerLock.writeLock();
                    upLock.lock();
                    upServerList = newUpList;
                    upLock.unlock();
                    notifyServerStatusChangeListener(changedServers);
                } finally {
                    pingInProgress.set(false);
                }
            }
        }

        private final Counter createCounter() {
            return Monitors.newCounter("LoadBalancer_ChooseServer");
        }

        //基于rule实现chooseServer
        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) {return null;}
            }
        }

        
        public void markServerDown(Server server) {
            if (server == null || !server.isAlive()) {return;}
            server.setAlive(false);
            notifyServerStatusChangeListener(singleton(server));
        }

      
    }

    3.7.2.2、LoadBalancerStats

    负载均衡LB需要依赖这些统计信息做为判断的策略,负载均衡器的统计类主要是LoadBalancerStats,一个serverId下所有服务实例对应一个LoadBalancerStats。

    其内部持有:

    * LoadingCache<Server, ServerStats> serverStatsCache

    ①ServerStats对每个Server的运行情况做了相关统计如:平均响应时间、累计失败数、熔断(时间)控制等

    ②ServerStats的增删改查的方法

    ③基于ServerStats实现的方法,如noteResponseTime方法实现为ServerStatsnoteResponseTime(msecs),记录响应时间

    * Map<String, ZoneStats> zoneStatsMap和Map<String, List<? extends Server>> upServerListZoneMap

    ①zoneStatsMap是在在每一个Servrt基础上又进行区域zone的划分,ZoneStats保存着每一个zone的状态(主要用于记录zone名称)

    ②upServerListZoneMap直接就是以zone的划分Server

      注:子类DynamicServerListLoadBalancer重写setServersList会更新这个两个map,即在实例列表初始化或者定时拉取的时候会更新

    * 基于ServerStats获取一些报表:

    ①获取一个区域快照getZoneSnapshot(List<? extends Server> servers)

    – 对一个分区中的所有服务实例进行计算后封装为new ZoneSnapshot(instanceCount, circuitBreakerTrippedCount, activeConnectionsCount, loadPerServer)

    – instanceCount:当前服务数

    – circuitBreakerTrippedCount:当前滑动窗口内断路的服务实例数

    – activeConnectionsCount:当前滑动窗口内此分区所有服务实例的有效连接总数数

    – LoadPerServer =  activeConnectionsCount / (instanceCount – circuitBreakerTrippedCount)

     new ZoneSnapshot(instanceCount, circuitBreakerTrippedCount, activeConnectionsCount, loadPerServer)

       ②略

    public class LoadBalancerStats implements IClientConfigAware {
        
        private static final String PREFIX = "LBStats_";
        
        String name;
        
        //区域状态表,ZoneStats里面封装了zone名称和一个计数器
        volatile Map<String, ZoneStats> zoneStatsMap = new ConcurrentHashMap<String, ZoneStats>();
        //区域名称和服务列表的映射
        volatile Map<String, List<? extends Server>> upServerListZoneMap = new ConcurrentHashMap<String, List<? extends Server>>();

        //serverStats断路器相关
        private volatile CachedDynamicIntProperty connectionFailureThreshold;
        private volatile CachedDynamicIntProperty circuitTrippedTimeoutFactor;
        private volatile CachedDynamicIntProperty maxCircuitTrippedTimeout;

        //serverStats
        private static final DynamicIntProperty SERVERSTATS_EXPIRE_MINUTES =
            DynamicPropertyFactory.getInstance().getIntProperty("niws.loadbalancer.serverStats.expire.minutes", 30);
        private final LoadingCache<Server, ServerStats> serverStatsCache =
            CacheBuilder.newBuilder()
                .expireAfterAccess(SERVERSTATS_EXPIRE_MINUTES.get(), TimeUnit.MINUTES)
                .removalListener(new RemovalListener<Server, ServerStats>() {
                    @Override
                    public void onRemoval(RemovalNotification<Server, ServerStats> notification) {
                        notification.getValue().close();
                    }
                })
                .build(
                    new CacheLoader<Server, ServerStats>() {
                        public ServerStats load(Server server) {
                            return createServerStats(server);
                        }
                    });

        protected ServerStats createServerStats(Server server) {
            ServerStats ss = new ServerStats(this);
            ss.setBufferSize(1000);
            ss.setPublishInterval(1000);                    
            ss.initialize(server);
            return ss;        
        }
        public LoadBalancerStats(){
            zoneStatsMap = new ConcurrentHashMap<String, ZoneStats>();  
            upServerListZoneMap = new ConcurrentHashMap<String, List<? extends Server>>();        
        }
        public LoadBalancerStats(String name){
            this();
            this.name = name;
            Monitors.registerObject(name, this);
        }

        //getter/setter

        @Override
        public void initWithNiwsConfig(IClientConfig clientConfig)
        {
            this.name = clientConfig.getClientName();
            Monitors.registerObject(name, this);
        }

        
        //更新serverStats
        public void updateServerList(List<Server> servers){
            for (Server s: servers){
                addServer(s);
            }
        }
        public void addServer(Server server) {
            try {
                serverStatsCache.get(server);
            } catch (ExecutionException e) {
                ServerStats stats = createServerStats(server);
                serverStatsCache.asMap().putIfAbsent(server, stats);
            }
        }
        protected ServerStats getServerStats(Server server) {
            try {
                return serverStatsCache.get(server);
            } catch (ExecutionException e) {
                ServerStats stats = createServerStats(server);
                serverStatsCache.asMap().putIfAbsent(server, stats);
                return serverStatsCache.asMap().get(server);
            }
        }

        //基于ServerStats内暴露的方法
        public void noteResponseTime(Server server, double msecs){
            ServerStats ss = getServerStats(server);
            ss.noteResponseTime(msecs);
        }
        public void incrementActiveRequestsCount(Server server) {
            ServerStats ss = getServerStats(server);
            ss.incrementActiveRequestsCount();
        }
        public boolean isCircuitBreakerTripped(Server server) {
            ServerStats ss = getServerStats(server);
            return ss.isCircuitBreakerTripped();
        }
        //

        //zoneStatsMap表相关方法
        private ZoneStats getZoneStats(String zone) {
            zone = zone.toLowerCase();
            ZoneStats zs = zoneStatsMap.get(zone);
            if (zs == null){
                zoneStatsMap.put(zone, new ZoneStats(this.getName(), zone, this));
                zs = zoneStatsMap.get(zone);
            }
            return zs;
        }
        public void updateZoneServerMapping(Map<String, List<Server>> map) {
            upServerListZoneMap = new ConcurrentHashMap<String, List<? extends Server>>(map);
            for (String zone: map.keySet()) {
                getZoneStats(zone);
            }
        }
        public int getActiveRequestsCount(String zone) {
            return getZoneSnapshot(zone).getActiveRequestsCount();
        }
        public double getActiveRequestsPerServer(String zone) {
            return getZoneSnapshot(zone).getLoadPerServer();
        }
        //获取某个地区的zoneStatsMap快照
        public ZoneSnapshot getZoneSnapshot(String zone) {
            if (zone == null) {return new ZoneSnapshot();}
            zone = zone.toLowerCase();
            List<? extends Server> currentList = upServerListZoneMap.get(zone);
            return getZoneSnapshot(currentList);        
        }
        //根据每个服务的断路、可用状态,计算了loadPerServer = 当前滑动窗口内有效连接数(包括了当前时刻状态为断路的) / (当前服务数 – 当前滑动窗口内有效连接数且服务没有断路)
        public ZoneSnapshot getZoneSnapshot(List<? extends Server> servers) {
            if (servers == null || servers.size() == 0) {
                return new ZoneSnapshot();
            }
            int instanceCount = servers.size();
            int activeConnectionsCount = 0;
            int activeConnectionsCountOnAvailableServer = 0;
            int circuitBreakerTrippedCount = 0;
            double loadPerServer = 0;
            long currentTime = System.currentTimeMillis();
            for (Server server: servers) {
                ServerStats stat = getSingleServerStat(server);   
                if (stat.isCircuitBreakerTripped(currentTime)) {
                    circuitBreakerTrippedCount++;
                } else {
                    activeConnectionsCountOnAvailableServer += stat.getActiveRequestsCount(currentTime);
                }
                activeConnectionsCount += stat.getActiveRequestsCount(currentTime);
            }
            if (circuitBreakerTrippedCount == instanceCount) {
                if (instanceCount > 0) {
                    // should be NaN, but may not be displayable on Epic
                    loadPerServer = –1;
                }
            } else {
                loadPerServer = ((double) activeConnectionsCountOnAvailableServer) / (instanceCount – circuitBreakerTrippedCount);
            }
            return new ZoneSnapshot(instanceCount, circuitBreakerTrippedCount, activeConnectionsCount, loadPerServer);
        }
        //获取指定的zone内的累计请求数
        public long getMeasuredZoneHits(String zone) {
            if (zone == null) {
                return 0;
            }
            zone = zone.toLowerCase();
            long count = 0;
            List<? extends Server> currentList = upServerListZoneMap.get(zone);
            if (currentList == null) {
                return 0;
            }
            for (Server server: currentList) {
                ServerStats stat = getSingleServerStat(server);
                count += stat.getMeasuredRequestsCount();
            }
            return count;
        }
        //计算指定zone (活跃请求数量(周期) + 断路数 ) / 服务数
        public int getCongestionRatePercentage(String zone) {
            if (zone == null) {
                return 0;
            }
            zone = zone.toLowerCase();
            List<? extends Server> currentList = upServerListZoneMap.get(zone);
            if (currentList == null || currentList.size() == 0) {
                return 0;            
            }
            int serverCount = currentList.size();
            int activeConnectionsCount = 0;
            int circuitBreakerTrippedCount = 0;
            for (Server server: currentList) {
                ServerStats stat = getSingleServerStat(server);   
                activeConnectionsCount += stat.getActiveRequestsCount();
                if (stat.isCircuitBreakerTripped()) {
                    circuitBreakerTrippedCount++;
                }
            }
            return (int) ((activeConnectionsCount + circuitBreakerTrippedCount) * 100L / serverCount);
        }

    }

    3.7.2.3、ServerStats

  • 用于记录一个Server实例的统计数据/属性
  • CircuitBreaker断路器相关属性

    *connectionFailureThreshold:连接失败阈值,默认值3(超过就熔断)

      默认值配置:niws.loadbalancer.default.connectionFailureCountThreshold此key指定;个性化配置:"niws.loadbalancer." + name + ".connectionFailureCountThreshold"

    * circuitTrippedTimeoutFactor:断路器超时因子,默认值10s。

      默认值配置: niws.loadbalancer.default.circuitTripTimeoutFactorSeconds;个性化配置:"niws.loadbalancer." + name + ".circuitTripTimeoutFactorSeconds"

    * maxCircuitTrippedTimeout:断路器最大超时秒数(默认使用超时因子计算出来),默认值是30s。

      默认值配置:niws.loadbalancer.default.circuitTripMaxTimeoutSeconds;个性化配置:"niws.loadbalancer." + name + ".circuitTripMaxTimeoutSeconds"

    * successiveConnectionFailureCount:连续(successive)请求异常数量(这个连续发生在Retry重试期间)。

      在重试期间,但凡有一次成功了,就会把此参数置为0(失败的话此参数就一直加)

      说明:只有通常callErrorHandler.isCircuitTrippingException(e)方法判定才会算作失败,才会+1,

    默认情况下只有SocketException/SocketTimeoutException这两种异常才算失败哦~

    * totalCircuitBreakerBlackOutPeriod:断路器断电总时长(连续失败>=3次,增加20~30秒。具体增加多少秒,后面有计算逻辑)。   

    * totalRequests:总请求数量。每次请求结束/错误时就会+1。

        

    * activeRequestsCount:活跃请求数量(正在请求的数量,它能反应该Server的负载、压力)。

      但凡只要开始执行Sever了,就+1;但凡只要请求完成了/出错了,就-1;注意:它有时间窗口的概念,后面讲具体逻辑

    * lastActiveRequestsCountChangeTimestamp:简单的说就是activeRequestsCount的值最后变化的时间戳

    * activeRequestsCountTimeout:活跃请求数量的统计周期,如果lastActiveRequestsCountChangeTimestamp距离now超过了此周期,activeRequestsCount会重新计算

    * openConnectionsCount:暂无任何使用处,可忽略。

         

    * lastConnectionFailedTimestamp:最后一次失败的时间戳。至于什么叫失败,参考successiveConnectionFailureCount对失败的判断逻辑

    * lastAccessedTimestamp:最后访问时间戳。和lastActiveRequestsCountChangeTimestamp的区别是,它增/减都update一下,

    而lastAccessedTimestamp只有在增的时候才会update一下。

    * firstConnectionTimestamp:首次连接时间戳,只会记录首次请求进来时的时间。

         

    * failureCountSlidingWindowInterval:失败次数统计时间窗。默认值1000ms

    * serverFailureCounts:上一秒失败次数(上一秒是因为failureCountSlidingWindowInterval默认1000ms),

    successiveConnectionFailureCount增它就增,只不过它有时间窗口(1s)

    * requestCountInWindow:一个窗口期内的请求总数,窗口期默认为5分钟(300秒),activeRequestsCount增它就增,只不过它有时间窗口(300s)

    响应时间的累计预计

    * dataDist:它是一个DataAccumulator,数据累加器。内部维护这一个样本区,是一个数值类型的数组,每次调用noteValue都会在数组依次填充记录,数组下标++,

    如果满了会从头填充,之前的数据不会清空

    * publisher:定时publish发布数据,默认1分钟发布一次,会计算数组中数据10,20…,90…,99.5百分比数据的统计值。统计之后会清空数据

               比如数组buf大小为10,数为 [ 1,2,3,4,5, null,null,null,null,null],开始统计,表示在最近的1分钟内,只记录了5个数据 , 90%的统计值为:5 * 90% = 4.5 ,取low = 4 , high = 5

               返回 buf[iLow] + (index – iLow) * (buf[iHigh] – buf[iLow]) = 2.5

    * responseTimeDist:它是个Distribution类型,因为它仅仅只需要持续累加数据,然后提供最大最小值、平均值的访问而已

    dataDist和responseTimeDist统一通过noteResponseTime(double msecs)来记录每个请求的响应时间,dataDist按照时间窗口统计,responseTimeDist一直累加。

    (2)CircuitBreaker断路器的原理

    * 本处的断路器解释

    ①当有某个服务存在多个实例时,在请求的过程中,负载均衡器会统计每次请求的情况(请求响应时间,是否发生网络异常等),当出现了请求出现累计重试时,负载均衡器会标识当前服务实例,

    设置当前服务实例的断 路的时间区间,在此区间内,当请求过来时,负载均衡器会将此服务实例从可用服务实例列表中暂时剔除(其实就是暂时忽略此Server),优先选择其他服务实例。

    ②该断路器和Hystrix无任何关系,无任何关系,无任何关系。它是ServerStats内部维护的一套熔断机制,体现在如下getCircuitBreakerTimeout方法上

    该断路器规则非常简单,开启与否完全由连续失败来决定,而是否算失败由RetryHandler#isCircuitTrippingException来决定,默认它只认为SocketException/SocketTimeoutException(或者其子类异常)属于该种类型的异常

    任何业务异常(如NPE)和此断路器没有半毛钱关系。本断路器断的是Server,也就是远程服务器, Hystrix断路器断的是Client,也就是客户端的调用

    * 熔断结束时间的计算

    目前断路器统计失败是靠连续失败次数去判断断路逻辑的。此方法逻辑可总结如下:

    ①若连续失败次数还小于阈值(默认3次),那么就不用断路。返回0即可,否则执行计算要断开多久的逻辑

      连续(successive)请求异常数量(这个连续发生在Retry重试期间),在重试期间,但凡有一次成功了,就会把此参数置为0(失败的话此参数就一直加)

      说明:只有通常callErrorHandler.isCircuitTrippingException(e)方法判定才会算作失败,才会+1,默认情况下只有SocketException/SocketTimeoutException这两种异常

        ②计算失败基数,最大不能超过16(就算你连续失败100次,此基数也是16)

    ③根据超时因子circuitTrippedTimeoutFactor(默认是10)计算出时间值blackOutSeconds,该值不能大于上限connectionFailureCircuitTimeout(默认30s),

    也就是说保证了断路器最长不能打开超过30s

        ④断路结束计算公式:(连续失败次数 – 失败基数) * 2 * 超时因子

     
    // 看看该断路器到哪个时间点(关闭)的时刻时间戳在这个时间段内,该服务是断路的,在Server被熔断期间,负载均衡器都将忽略此Server。
    // 比如断路器要从0点开30s,那么返回值就是00:00:30s这个时间戳呗= 最后一次连接失败时间 + 断路周期
    private long getCircuitBreakerTimeout() {
        long blackOutPeriod = getCircuitBreakerBlackoutPeriod();
        if (blackOutPeriod <= 0) {
            return 0;
        }
        return lastConnectionFailedTimestamp + blackOutPeriod;
    }

    // 返回需要中断的持续时间(毫秒值)如果大于0表示需要断路
    private long getCircuitBreakerBlackoutPeriod() {
        int failureCount = successiveConnectionFailureCount.get();
        int threshold = connectionFailureThreshold.get();
        if (failureCount < threshold) {
            return 0;
        }
        int diff = (failureCount – threshold) > 16 ? 16 : (failureCount – threshold);
        int blackOutSeconds = (1 << diff) * circuitTrippedTimeoutFactor.get();
        if (blackOutSeconds > maxCircuitTrippedTimeout.get()) {
            blackOutSeconds = maxCircuitTrippedTimeout.get();
        }
        return blackOutSeconds * 1000L;
    }

    * 断路器如何闭合?

    倘若断路器打开了,它如何恢复呢?有如下3种情形它会恢复到正常状态:

      ①不是连续失败了,也就是成功了一次,那么successiveConnectionFailureCount就会立马归0,所以熔断器就闭合了

      ②即使请求失败了,但是并非是断路器类异常,即不是RetryHandler#isCircuitTrippingException这种类型的异常时(比如RuntimeException就不是这种类型的异常),那就也不算连续失败,所以也就闭合了

      ③到时间了,断路器自然就自动闭合了

    (3)有窗口性质的统计

    * serverFailureCounts:上一秒失败次数(上一秒是因为failureCountSlidingWindowInterval默认1000ms),

    successiveConnectionFailureCount增它就增,只不过它有时间窗口(1s)

    * requestCountInWindow:一个窗口期内的请求总数,窗口期默认为5分钟(300秒),activeRequestsCount增它就增,只不过它有时间窗口(300s)

    * 他们都是MeasuredRate类型的

      内部使用一个_threshold表示当前窗口的结尾边界,= System.currentTimeMillis() + sampleInterval;

      在操作统计值时,如果此时还处于窗口内就累加,否就重置

    public class MeasuredRate {
        private final AtomicLong _lastBucket = new AtomicLong(0);
        private final AtomicLong _currentBucket = new AtomicLong(0);
        private final long _sampleInterval;
        private volatile long _threshold;
        
        public MeasuredRate(long sampleInterval){
           _sampleInterval = sampleInterval;
           _threshold = System.currentTimeMillis() + sampleInterval;
        }
        
        public long getCount() {
            checkAndResetWindow();
            return _lastBucket.get();
        }
        
        public long getCurrentCount() {
            checkAndResetWindow();
            return _currentBucket.get();
        }
        
        public void increment() {
            checkAndResetWindow();
            _currentBucket.incrementAndGet();
        }

        private void checkAndResetWindow() {
            long now = System.currentTimeMillis();
            if(_threshold < now) {
                _lastBucket.set(_currentBucket.get());
                _currentBucket.set(0);
                _threshold = now + _sampleInterval;
            }
        }
        

    }

    3.7.3、DynamicServerListLoadBalancer

    主要是对服务列表的处理,基于几个组件,重写父类的方法

    * 在构造器后新增restOfInit处理,以初始化当前ServerId的服务实例

      ①调用serverListUpdater.start,为服务列表的更新器,默认为PollingServerListUpdater会定时(默认30s)调用updateListOfServers方法,

        而Eureka对应的为EurekaNotificationServerListUpdater,为在eurekaClient.registerEventListener(updateListener),

    只有监听到CacheRefreshedEvent事件才会调用updateListOfServers方法

      ②调用serverListImpl.getUpdatedListOfServers(),再使用filter过滤下,

    ③再调用setServersList方法,更新好父类的服务列表allServerList,再处理LoadBalancerStats的serverStatsCache和upServerListZoneMap

      – 每一个Server都封装为ServerStats在存于父类BaseLoadBlance中的LoadingCache<Server, ServerStats> serverStatsCache

      – 把所有Server按照zone进行划分存于父类BaseLoadBlance中的 Map<String, List<? extends Server> upServerListZoneMap

    – 把每一个zone封装为ZoneStats在存于父类BaseLoadBlance中的Map<String, ZoneStats> zoneStatsMap

    ZoneStats只是对zone的简单封装,存有LoadBalancerStats loadBalancerStats和 String zone;

        

    public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {
        private static final Logger LOGGER = LoggerFactory.getLogger(DynamicServerListLoadBalancer.class);

        boolean isSecure = false;
        boolean useTunnel = false;
        protected AtomicBoolean serverListUpdateInProgress = new AtomicBoolean(false);

        //基于serverListImpl获取服务列表
        volatile ServerList<T> serverListImpl;
        volatile ServerListFilter<T> filter;
        //处理ServerList的个更新器,默认为PollingServerListUpdater会都是调用updateListOfServers方法,
        //Eureka对应的为EurekaNotificationServerListUpdater,为在eurekaClient.registerEventListener(updateListener)
        //只有监听到CacheRefreshedEvent事件才会调用updateListOfServers方法
        protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {
            @Override
            public void doUpdate() {
                updateListOfServers();
            }
        };
        protected volatile ServerListUpdater serverListUpdater;

        public DynamicServerListLoadBalancer() {
            super();
        }
        
        //构造器,初始化组件之后,调用restOfInit
        public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
                                             ServerList<T> serverList, ServerListFilter<T> filter,
                                             ServerListUpdater serverListUpdater) {
            super(clientConfig, rule, ping);
            this.serverListImpl = serverList;
            this.filter = filter;
            this.serverListUpdater = serverListUpdater;
            if (filter instanceof AbstractServerListFilter) {
                ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
            }
            restOfInit(clientConfig);
        }
        public DynamicServerListLoadBalancer(IClientConfig clientConfig) {
            initWithNiwsConfig(clientConfig);
        }
        //基于配置类初始化默认的组件实现
        @Override
        public void initWithNiwsConfig(IClientConfig clientConfig) {
            try {
                super.initWithNiwsConfig(clientConfig);
                ServerList<T> niwsServerListImpl = (ServerList<T>) ClientFactory.instantiateInstanceWithClientConfig(niwsServerListClassName, clientConfig);
                this.serverListImpl = niwsServerListImpl;
                //
                restOfInit(clientConfig);
            } catch (Exception e) {throw new RuntimeException();}
        }

        void restOfInit(IClientConfig clientConfig) {
            boolean primeConnection = this.isEnablePrimingConnections();
            this.setEnablePrimingConnections(false);
            //调用serverListUpdater.start
            enableAndInitLearnNewServersFeature();
            //调用serverListImpl.getUpdatedListOfServers(),再使用filter过滤下,再调用setServersList
            updateListOfServers();
            if (primeConnection && this.getPrimeConnections() != null) {
                this.getPrimeConnections().primeConnections(getReachableServers());
            }
            this.setEnablePrimingConnections(primeConnection);
        }
        
        @Override
        public void setServersList(List lsrv) {
            //更新好父类的服务列表allServerList,再处理LoadBalancerStatsserverStatsCacheupServerListZoneMap
            super.setServersList(lsrv);
            List<T> serverList = (List<T>) lsrv;
            Map<String, List<Server>> serversInZones = new HashMap<String, List<Server>>();
            for (Server server : serverList) {
                // make sure ServerStats is created to avoid creating them on hot
                // path
                getLoadBalancerStats().getSingleServerStat(server);
                String zone = server.getZone();
                if (zone != null) {
                    zone = zone.toLowerCase();
                    List<Server> servers = serversInZones.get(zone);
                    if (servers == null) {
                        servers = new ArrayList<Server>();
                        serversInZones.put(zone, servers);
                    }
                    servers.add(server);
                }
            }
            //getLoadBalancerStats().updateZoneServerMapping(zoneServersMap)
            setServerListForZones(serversInZones);
        }

    protected void setServerListForZones(Map<String, List<Server>> zoneServersMap) {
            getLoadBalancerStats().updateZoneServerMapping(zoneServersMap);
        }

        public void stopServerListRefreshing() {
            if (serverListUpdater != null) {
                serverListUpdater.stop();
            }
        }
        
        @Override
        public void shutdown() {
            super.shutdown();
            stopServerListRefreshing();
        }

        //
    }

    3.7.4、ZoneAwareLoadBalancer重写chooseServer

    * 为每一个分区创建一个BaseLoadBlance,即ConcurrentHashMap<String, BaseLoadBalancer> balancers

    * 重写chooseServer,如果只有一个分区,使用super.chooseServer(key);如果有多个分区,先通过LoadBalancerStats获取所有分区的状态快照,

    使用ZoneAvoidanceRule.getAvailableZones计算出可用有效的分区集合availableZones(具体逻辑见上ZoneAvoidanceRule实现类)

    * 再使用ZoneAvoidanceRule.randomChooseZone随机选择一个分区,使用其ZoneAvoidanceRule的chooseServer选择最终的服务实例

      即最终使用ZoneAwareLoadBalancer配置的rule的choose方法进行选定,默认就是ZoneAvoidanceRule的choose方法

    注:默认ZoneAvoidanceRule也会进行分区处理,当时一个BaseLoadBlance里没有upServerListZoneMap分区概念,其内的upServerList都是同一个分区的

    所以会第二个AvailabilityPredicate校验,会筛选不处于断路状态且活跃请求数小于niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit配置,默认为int最大值

    对最终结果List<Server> eligible进行轮询即可

    * 为什么给每一个每一个分区创建一个BaseLoadBlance

      – 用户这可以自定义的ZoneAwareLoadBalancer的rule,这样ZoneAwareLoadBalancer会帮我们先过滤好分区,然后选择自己设置的rule进行处理

        这样就复用了分区的功能

    例如可以使用WeightedResponseTimeRule以平均响应时间作为权重进行随机选择

      –

    public class ZoneAwareLoadBalancer<T extends Server> extends DynamicServerListLoadBalancer<T> {

        private ConcurrentHashMap<String, com.netflix.loadbalancer.BaseLoadBalancer> balancers = new ConcurrentHashMap<String, com.netflix.loadbalancer.BaseLoadBalancer>();
        
        private static final Logger logger = LoggerFactory.getLogger(ZoneAwareLoadBalancer.class);
                
        private volatile DynamicDoubleProperty triggeringLoad;

        private volatile DynamicDoubleProperty triggeringBlackoutPercentage;

        private static final DynamicBooleanProperty ENABLED = DynamicPropertyFactory.getInstance().getBooleanProperty("ZoneAwareNIWSDiscoveryLoadBalancer.enabled", true);
                
        void setUpServerList(List<Server> upServerList) {
            this.upServerList = upServerList;
        }
        
        public ZoneAwareLoadBalancer() {
            super();
        }

        @Deprecated
        public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,
                                     IPing ping, ServerList<T> serverList, ServerListFilter<T> filter) {
            super(clientConfig, rule, ping, serverList, filter);
        }

        @Override
        public Server chooseServer(Object key) {
            if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
                return super.chooseServer(key);
            }
            Server server = null;
            try {
                LoadBalancerStats lbStats = getLoadBalancerStats();
                //调用LoadBalancerStats每一个分区的状态快照ZoneSnapshotZoneSnapshot包括了此分区的可用连接,断路数的相关数据
                //loadPerServer = ((double) activeConnectionsCountOnAvailableServer) / (instanceCount – circuitBreakerTrippedCount);
                //              = 当前滑动窗口内有效连接数(包括了当前时刻状态为断路的) / (当前服务数 – 当前滑动窗口内有效连接数且服务没有断路)
                Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
                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);
                }
                //选择有效的分区,再随机选择一个分区zone,使用zoneLoadBalancer.chooseServer(key)选择一个服务实例
                Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
                if (availableZones != null &&  availableZones.size() < zoneSnapshot.keySet().size()) {
                    String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
                    if (zone != null) {
                        //balancers找到分区对应的BaseLoadBalancer,没有会创建,在基于默认的ZoneAvoidanceRule寻找实例
                        com.netflix.loadbalancer.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);
            }
        }
         
        @VisibleForTesting
        com.netflix.loadbalancer.BaseLoadBalancer getLoadBalancer(String zone) {
            zone = zone.toLowerCase();
            com.netflix.loadbalancer.BaseLoadBalancer loadBalancer = balancers.get(zone);
            if (loadBalancer == null) {
               IRule rule = cloneRule(this.getRule());
                loadBalancer = new com.netflix.loadbalancer.BaseLoadBalancer(this.getName() + "_" + zone, rule, this.getLoadBalancerStats());
                BaseLoadBalancer prev = balancers.putIfAbsent(zone, loadBalancer);
                if (prev != null) {
                   loadBalancer = prev;
                }
            }
            return loadBalancer;        
        }

        private IRule cloneRule(IRule toClone) {
           IRule rule;
           if (toClone == null) {
              rule = new AvailabilityFilteringRule();
           } else {
              String ruleClass = toClone.getClass().getName();              
              try {
                rule = (IRule) ClientFactory.instantiateInstanceWithClientConfig(ruleClass, this.getClientConfig());
             } catch (Exception e) {
                throw new RuntimeException("Unexpected exception creating rule for ZoneAwareLoadBalancer", e);
             }
           }
           return rule;
        }
        
           
        @Override
        public void setRule(IRule rule) {
            super.setRule(rule);
            if (balancers != null) {
                for (String zone: balancers.keySet()) {
                    balancers.get(zone).setRule(cloneRule(rule));
                }
            }
        }
    }

    //ZoneAvoidanceRule
    public class ZoneAvoidanceRule extends PredicateBasedRule {

        private static final Random random = new Random();
        
        private CompositePredicate compositePredicate;
        
        public ZoneAvoidanceRule() {
            super();
            ZoneAvoidancePredicate zonePredicate = new ZoneAvoidancePredicate(this);
            AvailabilityPredicate availabilityPredicate = new AvailabilityPredicate(this);
            compositePredicate = createCompositePredicate(zonePredicate, availabilityPredicate);
        }
        
        private CompositePredicate createCompositePredicate(ZoneAvoidancePredicate p1, AvailabilityPredicate p2) {
            return CompositePredicate.withPredicates(p1, p2)
                                 .addFallbackPredicate(p2)
                                 .addFallbackPredicate(AbstractServerPredicate.alwaysTrue())
                                 .build();
            
        }
        
        
        @Override
        public void initWithNiwsConfig(IClientConfig clientConfig) {
            ZoneAvoidancePredicate zonePredicate = new ZoneAvoidancePredicate(this, clientConfig);
            AvailabilityPredicate availabilityPredicate = new AvailabilityPredicate(this, clientConfig);
            compositePredicate = createCompositePredicate(zonePredicate, availabilityPredicate);
        }

        static Map<String, ZoneSnapshot> createSnapshot(LoadBalancerStats lbStats) {
            Map<String, ZoneSnapshot> map = new HashMap<String, ZoneSnapshot>();
            for (String zone : lbStats.getAvailableZones()) {
                ZoneSnapshot snapshot = lbStats.getZoneSnapshot(zone);
                map.put(zone, snapshot);
            }
            return map;
        }

        static String randomChooseZone(Map<String, ZoneSnapshot> snapshot,
                Set<String> chooseFrom) {
            if (chooseFrom == null || chooseFrom.size() == 0) {
                return null;
            }
            String selectedZone = chooseFrom.iterator().next();
            if (chooseFrom.size() == 1) {
                return selectedZone;
            }
            int totalServerCount = 0;
            for (String zone : chooseFrom) {
                totalServerCount += snapshot.get(zone).getInstanceCount();
            }
            int index = random.nextInt(totalServerCount) + 1;
            int sum = 0;
            for (String zone : chooseFrom) {
                sum += snapshot.get(zone).getInstanceCount();
                if (index <= sum) {
                    selectedZone = zone;
                    break;
                }
            }
            return selectedZone;
        }

        public static Set<String> getAvailableZones(
                Map<String, ZoneSnapshot> snapshot, double triggeringLoad,
                double triggeringBlackoutPercentage) {
            if (snapshot.isEmpty()) {
                return null;
            }
            Set<String> availableZones = new HashSet<String>(snapshot.keySet());
            if (availableZones.size() == 1) {
                return availableZones;
            }
            Set<String> worstZones = new HashSet<String>();
            double maxLoadPerServer = 0;
            boolean limitedZoneAvailability = false;

            for (Map.Entry<String, ZoneSnapshot> zoneEntry : snapshot.entrySet()) {
                String zone = zoneEntry.getKey();
                ZoneSnapshot zoneSnapshot = zoneEntry.getValue();
                int instanceCount = zoneSnapshot.getInstanceCount();
                if (instanceCount == 0) {
                    availableZones.remove(zone);
                    limitedZoneAvailability = true;
                } else {
                    double loadPerServer = zoneSnapshot.getLoadPerServer();
                    if (((double) zoneSnapshot.getCircuitTrippedCount())
                            / instanceCount >= triggeringBlackoutPercentage
                            || loadPerServer < 0) {
                        availableZones.remove(zone);
                        limitedZoneAvailability = true;
                    } else {
                        if (Math.abs(loadPerServer – maxLoadPerServer) < 0.000001d) {
                            // they are the same considering double calculation
                            // round error
                            worstZones.add(zone);
                        } else if (loadPerServer > maxLoadPerServer) {
                            maxLoadPerServer = loadPerServer;
                            worstZones.clear();
                            worstZones.add(zone);
                        }
                    }
                }
            }

            if (maxLoadPerServer < triggeringLoad && !limitedZoneAvailability) {
                // zone override is not needed here
                return availableZones;
            }
            String zoneToAvoid = randomChooseZone(snapshot, worstZones);
            if (zoneToAvoid != null) {
                availableZones.remove(zoneToAvoid);
            }
            return availableZones;

        }

        public static Set<String> getAvailableZones(LoadBalancerStats lbStats,
                double triggeringLoad, double triggeringBlackoutPercentage) {
            if (lbStats == null) {
                return null;
            }
            Map<String, ZoneSnapshot> snapshot = createSnapshot(lbStats);
            return getAvailableZones(snapshot, triggeringLoad,
                    triggeringBlackoutPercentage);
        }

        @Override
        public AbstractServerPredicate getPredicate() {
            return compositePredicate;
        }    
    }

    3.8、RibbonLoadBalancerContext统计根据

    * RibbonLoadBalancerContext和LoadBalancerContext一致

    * 此类的作用类似一个记录工具类,所有方法都需要指定LoadBalancerStats的ServerStats参数

    穿插在请调用的整个流程,提供记录和更新服务状态ServerStats的功能,由cliect客户端自己决定何时调用

    比如在打开请求时,会调用noteOpenConnectio(ServerStats serverStats)n方法,此方法会把对应的ServerStats的活跃请求指标activeRequestsCount + 1

    * 核心方法:

      ① noteRequestCompletion:请求正常响应或者异常时都会调用

    – 活跃请求-1 ; 总请求数+1 ;记录响应时间

      stats.decrementActiveRequestsCount();

      stats.incrementNumRequests();

      stats.noteResponseTime(responseTime);

        – 正常响应,清空连续失败记录:stats.clearSuccessiveConnectionFailureCount();

    – 如果时符合断路器异常(由RetryHandler默认SocketException.class, SocketTimeoutException.class):

    连续失败次数+1 ;stats.incrementSuccessiveConnectionFailureCount();   

    服务总失败次数+1:stats.addToFailureCount();

        – 其他不符合的异常清空连续失败记录:stats.clearSuccessiveConnectionFailureCount();

      ②noteError:抛出error的时候调用(同noteRequestCompletion)

        protected void

    ③noteResponse:正常响应的时候调用(同noteRequestCompletion)

        protected void

    ④noteOpenConnection:client开始执行请求的是调用:活跃请求+1

    public class LoadBalancerContext implements IClientConfigAware {
        private static final Logger logger = LoggerFactory.getLogger(LoadBalancerContext.class);

        protected String clientName = "default";
        protected String vipAddresses;

        //用于判断一个异常类型可否重试或断路
        protected int maxAutoRetriesNextServer = DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER;
        protected int maxAutoRetries = DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES;
        protected RetryHandler defaultRetryHandler = new DefaultLoadBalancerRetryHandler();

        protected boolean okToRetryOnAllOperations = DefaultClientConfigImpl.DEFAULT_OK_TO_RETRY_ON_ALL_OPERATIONS.booleanValue();
        private ILoadBalancer lb;
        private volatile Timer tracer;

        public LoadBalancerContext(ILoadBalancer lb) {
            this.lb = lb;
        }

        //活跃请求-1 ; 总请求数+1 ; 记录响应时间
        private void recordStats(ServerStats stats, long responseTime) {
           if (stats == null) {
              return;
           }
            stats.decrementActiveRequestsCount();
            stats.incrementNumRequests();
            stats.noteResponseTime(responseTime);
        }

        //请求正常响应或者异常时都会调用
        protected void noteRequestCompletion(ServerStats stats, Object response, Throwable e, long responseTime) {
           if (stats == null) {
              return;
           }
            noteRequestCompletion(stats, response, e, responseTime, null);
        }
        public void noteRequestCompletion(ServerStats stats, Object response, Throwable e, long responseTime, RetryHandler errorHandler) {
           if (stats == null) {
              return;
           }
            try {
                // 活跃请求-1 ; 总请求数+1 ; 记录响应时间
                recordStats(stats, responseTime);
                RetryHandler callErrorHandler = errorHandler == null ? getRetryHandler() : errorHandler;
                if (callErrorHandler != null && response != null) {
                    //正常响应,清空连续失败记录
                    stats.clearSuccessiveConnectionFailureCount();
                } else if (callErrorHandler != null && e != null) {
                    if (callErrorHandler.isCircuitTrippingException(e)) {
                        //如果时符合断路器异常(默认SocketException.class, SocketTimeoutException.class):连续失败次数+1 ; 服务总失败次数+1
                        stats.incrementSuccessiveConnectionFailureCount();                    
                        stats.addToFailureCount();
                    } else {
                        //其他不符合的异常清空连续失败记录
                        stats.clearSuccessiveConnectionFailureCount();
                    }
                }
            } catch (Exception ex) {}
        }

        //抛出error的时候调用(同noteRequestCompletion
        protected void noteError(ServerStats stats, ClientRequest request, Throwable e, long responseTime) {
           if (stats == null) {
              return;
           }
            try {
                // 活跃请求-1 ; 总请求数+1 ; 记录响应时间
                recordStats(stats, responseTime);
                RetryHandler errorHandler = getRetryHandler();
                if (errorHandler != null && e != null) {
                    if (errorHandler.isCircuitTrippingException(e)) {
                        stats.incrementSuccessiveConnectionFailureCount();                    
                        stats.addToFailureCount();
                    } else {
                        stats.clearSuccessiveConnectionFailureCount();
                    }
                }
            } catch (Exception ex) {}
        }

        //正常响应的时候调用(同noteRequestCompletion
        protected void noteResponse(ServerStats stats, ClientRequest request, Object response, long responseTime) {
           if (stats == null) {
              return;
           }
            try {
                recordStats(stats, responseTime);
                RetryHandler errorHandler = getRetryHandler();
                if (errorHandler != null && response != null) {
                    stats.clearSuccessiveConnectionFailureCount();
                }
            } catch (Exception ex) {
                logger.error("Error noting stats for client {}", clientName, ex);
            }            
        }

        //client开始执行请求的是调用:活跃请求+1
        public void noteOpenConnection(ServerStats serverStats) {
            if (serverStats == null) {
                return;
            }
            try {
                serverStats.incrementActiveRequestsCount();
            } catch (Exception ex) {
                logger.error("Error noting stats for client {}", clientName, ex);
            }            
        }

        //….
    }

    4、通过SpringClientFactory获取bean(以serviceId容器隔离)

    * 在RibbonAutoConfiguration 自动配置类中会创建SpringClientFactory 、configurations为配置表

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

    * SpringClientFactory继承NamedContextFactory,在创建时,构造方法指定了默认的配置信息和名称

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

    * NamedContextFactory提供以name为key的上下文表(父容器为顶层的Spring容器)

    – 可以为每一个服务创建一个容器,内部为各个服务指定配置的实例和默认的配置实例

    * 核心作用:获取bean

    – SpringClientFactory.getInstance(String name, Class<C> type)

    – 通过NamedContextFactory获取指定容器内的某个type实例

    获取不到时,会通过IClientConfig进行创建,调用容器的autowireBean进属性注入,但是最终实例不会放入容器中

    //SpringClientFactory

    public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {

        static final String NAMESPACE = "ribbon";

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

        public ILoadBalancer getLoadBalancer(String name) {
            return getInstance(name, ILoadBalancer.class);
        }
        
        //通过NamedContextFactory获取指定容器内的某个type实例,获取不到时,会通过IClientConfig进行创建,
        //调用容器的autowireBean进属性注入,但是最终实例不会放入容器中
        @Override
        public <C> C getInstance(String name, Class<C> type) {
            C instance = super.getInstance(name, type);
            if (instance != null) {
                return instance;
            }
            IClientConfig config = getInstance(name, IClientConfig.class);
            return instantiateWithConfig(getContext(name), type, config);
        }
        
        //
    }

    //NamedContextFactory
    public abstract class NamedContextFactory<C extends org.springframework.cloud.context.named.NamedContextFactory.Specification>
            implements DisposableBean, ApplicationContextAware {

        //默认的配置类信息
        private final String propertySourceName;
        private final String propertyName;
        private Class<?> defaultConfigType;
        //容器表
        private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
        //@RibbonClientsRibbonClient注解,name和指定的配置类
        private Map<String, C> configurations = new ConcurrentHashMap<>();
        private ApplicationContext parent;
        
        public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
                                   String propertyName) {
            this.defaultConfigType = defaultConfigType;
            this.propertySourceName = propertySourceName;
            this.propertyName = propertyName;
        }

        //获取指定name上下文中,指定类型的实例
        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;
        }

        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的上下文,并注入指定和默认的配置实例
        protected AnnotationConfigApplicationContext createContext(String name) {
            //创建容器
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
            //在配置表中找到对应name作用配置类,进行解析注入
            if (this.configurations.containsKey(name)) {
                for (Class<?> configuration : this.configurations.get(name)
                        .getConfiguration()) {
                    context.register(configuration);
                }
            }
            //在配置表中找到默认的全局配置类(defaultConfiguratiuon指定的),进行解析注入
            for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
                if (entry.getKey().startsWith("default.")) {
                    for (Class<?> configuration : entry.getValue().getConfiguration()) {
                        context.register(configuration);
                    }
                }
            }
            //在配置表中找到默认的全局配置类(构造器defaultConfigType指定的),进行解析注入
            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) {
                context.setParent(this.parent);
                context.setClassLoader(this.parent.getClassLoader());
            }
            context.setDisplayName(generateDisplayName(name));
            context.refresh();
            return context;
        }

        //

    }

    5、@LoadBalanced + RestTemplate 的主流程

    5.1、实例

    (1)使用 @LoadBalanced 注解修饰的 RestTemplate:

    @Configuration
    public class RestConfig {
        //创建restTemplate对象。LoadBalanced注解表示赋予restTemplate使用Ribbon的负载均衡的能力(一定要加上注解,否则无法远程调用)
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }

    (2)通过 RestTemplate 请求远程服务地址并接收返回值

    @RestController
    @RequestMapping(value = "api/invoke")
    public class InvokeController{

        @Autowired
        private RestTemplate restTemplate;

        public String getByRestTemplate(Integer num1, Integer num2){
            //第一个cloud-producer-server代表在nacos注册中心中的服务名,第二个cloud-producer-server代表contextPath配置的项目路径
            String url = "http://cloud-producer-server/cloud-producer-server/getSum";
            MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
            params.add("num1", num1);
            params.add("num2", num2);
            return restTemplate.postForObject(url, params, String.class);
        }
    }

    5.2、RestTemplate到LoadBalancerClient(RibbonLoadBalancerClient)的流程

    * 这里的RestTemplate使用的是原生的RestTemplate,即没有进过任何定制的(可支持自定义超时属性、底层HttpClient 工具)

      默认使用JDK自带的HttpURLConnection进行通信的

      @Bean
      @LoadBalanced
      public RestTemplate restTemplate(){
         return new RestTemplate();
      }

      

    * 标志了@LoadBalanced的restTemplate.postForObject(url, params, String.class) ->doExecute的流程

      – 通过restTemplate实例中设置的ClientHttpRequestFactory返回,由于存在拦截器则为InterceptingClientHttpRequestFactory

        会创建InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);

      – 执行InterceptingClientHttpRequest.execute()

    就会执行拦截器链的,最后会调用LoadBalancerClient.execute(serviceName,this.requestFactory.createRequest(request, body, execution))

    ①在LoadBalancerAutoConfiguration自动配置类中可以指定

    – LoadBalancerClient指RibbonLoadBalancerClient类型对象

    – requestFactory指LoadBalancerRequestFactory类型对象

        ②参数serviceName为url路径的域名

    ③参数request是InterceptingClientHttpRequest:封装了请求对象和通信工具

      

    //RestTemplate
    //postForObjec->doExecute
    @Nullable
    protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
                              @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
        ClientHttpResponse response = null;
        try {
            //InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
            ClientHttpRequest request = createRequest(url, method);
            if (requestCallback != null) {
                requestCallback.doWithRequest(request);
            }
            //InterceptingClientHttpRequest.executeInternal->InterceptingRequestExecution类的.execute
            //执行拦截器链的
            response = request.execute();
            handleResponse(url, method, response);
            return (responseExtractor != null ? responseExtractor.extractData(response) : null);
        }
        catch (IOException ex) {throw new ResourceAccessException();
        }
        finally {
            if (response != null) {response.close(); }
        }
    }
    protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
        ClientHttpRequest request = getRequestFactory().createRequest(url, method);
        initialize(request);
        return request;
    }

    //RestTemplate的父类,InterceptingHttpAccessor

    //InterceptingClientHttpRequestFactory底层封装了原始SimpleClientHttpRequestFactory和拦截器,装饰了拦截功能
    @Override
    public ClientHttpRequestFactory getRequestFactory() {
       List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
       if (!CollectionUtils.isEmpty(interceptors)) {
          ClientHttpRequestFactory factory = this.interceptingRequestFactory;
          if (factory == null) {
             factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
             this.interceptingRequestFactory = factory;
          }
          return factory;
       }
       else {
          return super.getRequestFactory();
       }
    }

    //InterceptingRequestExecution
    private class InterceptingRequestExecution implements ClientHttpRequestExecution {

       private final Iterator<ClientHttpRequestInterceptor> iterator;

       public InterceptingRequestExecution() {
          this.iterator = interceptors.iterator();
       }

    //存在拦截器的:走每一个拦截器的intercept方法

    //不存在拦截器:走原始SimpleClientHttpRequestFactory创建的SimpleBufferingClientHttpRequest会设置好超时配置     

    //              setConnectTimeout/setReadTimeout,值是在restTemplate中设置的

    //              并且是使用JDK自带的HttpURLConnection进行通信的
       @Override
       public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
          if (this.iterator.hasNext()) {
             ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
             return nextInterceptor.intercept(request, body, this);
          }
          else {
             HttpMethod method = request.getMethod();
             ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
             request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));
             if (body.length > 0) {
                if (delegate instanceof StreamingHttpOutputMessage) {
                   StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
                   streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream));
                }
                else {
                   StreamUtils.copy(body, delegate.getBody());
                }
             }
             return delegate.execute();
          }
       }
    }

    //最终调用的LoadBalancerClient.execute(serviceName,this.requestFactory.createRequest(request, body, execution))

    public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

       private LoadBalancerClient loadBalancer;
       private LoadBalancerRequestFactory requestFactory;
       public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
             LoadBalancerRequestFactory requestFactory) {
          this.loadBalancer = loadBalancer;
          this.requestFactory = requestFactory;
       }

       public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
          this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
       }

       @Override
       public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
             final ClientHttpRequestExecution execution) throws IOException {
          final URI originalUri = request.getURI();
          String serviceName = originalUri.getHost();
          return this.loadBalancer.execute(serviceName,this.requestFactory.createRequest(request, body, execution));
       }

    }

    5.3、RibbonLoadBalancerClient.execute方法(LoadBalancerClient接口方法)

    public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
       //..
       @Override
       public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
             final ClientHttpRequestExecution execution) throws IOException {
          final URI originalUri = request.getURI();
          String serviceName = originalUri.getHost();
          return this.loadBalancer.execute(serviceName,this.requestFactory.createRequest(request, body, execution));
       }

    }

    * 核心方法RibbonLoadBalancerClient.execute的实现

      – 此方法是LoadBalancerClient接口的核心方法,用于选中一个服务实例后,执行

      – 基于SpringClientFactory在serviceId对应的上下文中获取ILoadBalancer实例,默认为ZoneAwareLoadBalancer

    SpringClientFactory的作用和LoadBalancerClientFactory一模一样,都是Name容器,以serviceId为一个容器的维度

    里面包含了ribbon的所有必要的组件bean。每一个serverId是相互隔离的

      – 核心调用其ILoadBalancer的chooseServer方法获取对应目标服务实例

      – 最后调用LoadBalancerRequest.apply(serviceInstance)执行请求

        执行 LoadBalancerRequestFactory.createRequest(request, body, execution)创建一个函数指针,进行url通信

    * LoadBalancerRequestFactory.createRequest(request, body, execution)

     – 使用LoadBalancerRequestFactory创建一个函数指针,把选择的服务实例,使用InterceptingRequestExecution.execute(serviceRequest, body);完成通信,

     – InterceptingRequestExecution的execute流程见上,由于此时没有拦截器了,就会走默认的execute步骤

    走原始SimpleClientHttpRequestFactory创建的SimpleBufferingClientHttpRequest会设置好超时配置,setConnectTimeout/setReadTimeout,值是在restTemplate中设置的

    最终使用使用JDK自带的HttpURLConnection进行通信的

    public class RibbonLoadBalancerClient implements LoadBalancerClient {

       private SpringClientFactory clientFactory;

       public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
          this.clientFactory = clientFactory;
       }

       @Override
       public URI reconstructURI(ServiceInstance instance, URI original) {
          //
          return context.reconstructURIWithServer(server, uri);
       }

       public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
             throws IOException {
          //基于SpringClientFactoryserviceId对应的上下文中获取ILoadBalancer实例,默认为ZoneAwareLoadBalancer
          ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
          //使用ILoadBalancerchooseServer方法获取最终选定的服务
          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));

          //核心是调用request.apply(serviceInstance)执行请求
          //LoadBalancerRequest<T> requestRestTemplate执行拦截器时调用提供放入
          return execute(serviceId, ribbonServer, request);
       }

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

    //核心是调用loadBalancer.chooseServer方法

    protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
           if (loadBalancer == null) {
              return null;
           }
           return loadBalancer.chooseServer(hint != null ? hint : "default");
        }
       //

    }

    public class LoadBalancerRequestFactory {

       private LoadBalancerClient loadBalancer;

       private List<LoadBalancerRequestTransformer> transformers;

       public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer,
             List<LoadBalancerRequestTransformer> transformers) {
          this.loadBalancer = loadBalancer;
          this.transformers = transformers;
       }

       public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer) {
          this.loadBalancer = loadBalancer;
       }

       public LoadBalancerRequest<ClientHttpResponse> createRequest(
             final HttpRequest request, final byte[] body,
             final ClientHttpRequestExecution execution) {
          return instance -> {
             HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance,
                   this.loadBalancer);
             if (this.transformers != null) {
                for (LoadBalancerRequestTransformer transformer : this.transformers) {
                   serviceRequest = transformer.transformRequest(serviceRequest,
                         instance);
                }
             }
             return execution.execute(serviceRequest, body);
          };
       }

    }

    赞(0)
    未经允许不得转载:171主机测评 » 微服务组件源码2——Spring Ribbon原理(基于RibbonLoadBalancerClient)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址