文章首发:Spring Cloud Ribbon入门和实战

Ribbon是什么

Ribbon是一个客户端负载均衡器,它赋予了利用一些摆布HTTP与TCP行为的能力,这里的负载平衡是客户端的负载平衡,也有人称为后端负载平衡是过程内负载平衡的一种。Ribbon是SpringCloud生态里的不可短少的组件,有了它,是个服务的横向扩大更加不便了。此外想Feign和Zuul默认是集成了Ribbon。

Ribbon是Neflix开源的一个组件,目前Ribbon早已进入保护状态,然而就目前的状况来看,Spring Cloud Netflix的一些组件还是能够应用。

Spring Cloud Loadbalancer是Spring Cloud社区开源的组件,目标是代替进入保护状态的Ribbon,然而Loadbalancer还是有很长的一段路要走。

Ribbon入门

因为客户端负载平衡须要从注册核心获取服务列表,所以须要集成注册核心。

创立父级工程cloud-ribbon-practice

    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.2.3.RELEASE</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>        <java.version>1.8</java.version>        <spring.cloud-version>Hoxton.SR3</spring.cloud-version>    </properties>    <dependencyManagement>        <dependencies>            <dependency>                <groupId>org.springframework.cloud</groupId>                <artifactId>spring-cloud-dependencies</artifactId>                <version>${spring.cloud-version}</version>                <type>pom</type>                <scope>import</scope>            </dependency>        </dependencies>    </dependencyManagement>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-actuator</artifactId>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build>

创立注册核心cloud-eureka-server

<dependencies>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>        </dependency>    </dependencies>

配置文件application.xml

server:  port: 8761eureka:  instance:    hostname: localhost  client:    registerWithEureka: false    fetchRegistry: false    serviceUrl:      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

启动类

@SpringBootApplication@EnableEurekaServerpublic class EurekaServerApplication {    public static void main(String[] args) {        SpringApplication.run(EurekaServerApplication.class, args);    }}

创立源服务工程cloud-ribbon-hello-b1、cloud-ribbon-hello-b2、cloud-ribbon-hello-b3

    <dependencies>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>    </dependencies>

配置文件application.yml

b1

spring:  application:    name: ribbon-service-bserver:  port: 7777eureka:  client:    serviceUrl:      defaultZone: http://localhost:8761/eureka/  instance:    prefer-ip-address: true

b2

spring:  application:    name: ribbon-service-bserver:  port: 7778eureka:  client:    serviceUrl:      defaultZone: http://localhost:8761/eureka/  instance:    prefer-ip-address: true

b3

spring:  application:    name: ribbon-service-bserver:  port: 7779eureka:  client:    serviceUrl:      defaultZone: http://localhost:8761/eureka/  instance:    prefer-ip-address: true

b1、b2、b3的controller

@RestControllerpublic class OrderController {    @Value("${server.port}")    private Integer port;    @Value("${spring.application.name}")    private String name;    @GetMapping("/test")    public String add() {        return "this service name is " + name + " and port is " + port;    }}

b1、b2、b3的启动类

@SpringBootApplication@EnableDiscoveryClientpublic class ServiceB1Application {    public static void main(String[] args) {        SpringApplication.run(ServiceB1Application.class, args);    }}@SpringBootApplication@EnableDiscoveryClientpublic class ServiceB2Application {    public static void main(String[] args) {        SpringApplication.run(ServiceB2Application.class, args);    }}@SpringBootApplication@EnableDiscoveryClientpublic class ServiceB3Application {    public static void main(String[] args) {        SpringApplication.run(ServiceB3Application.class, args);    }}

创立服务调用者cloud-ribbon-hello-a

spring-cloud-starter-netflix-eureka-client曾经集成了ribbon。不须要额定引入,间接应用即可。

    <dependencies>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>    </dependencies>

配置文件application.yml

spring:  application:    name: ribbon-hello-aserver:  port: 7776eureka:  client:    serviceUrl:      defaultZone: http://localhost:8761/eureka/  instance:    prefer-ip-address: true

启动类,RestTemplate应用了@LoadBalanced,这样RestTemplate就开启了ribbon的负载平衡了。

@SpringBootApplication@EnableDiscoveryClientpublic class ServiceAApplication {    public static void main(String[] args) {        SpringApplication.run(ServiceAApplication.class, args);    }    @LoadBalanced    @Bean    public RestTemplate restTemplate() {        return new RestTemplate();    }}

controller

@RestController@RequestMapping("ribbon")public class TestController {    @Autowired    private RestTemplate restTemplate;    @GetMapping("/test")    public String test(){        String body = restTemplate.getForEntity("http://RIBBON-SERVICE-B/test", String.class).getBody();        return body;    }}

测试,应用postman拜访几次:http://localhost:7776/ribbon/...

Ribbon实战

从上一节能够看到,开启负载平衡只需通过@LoadBalanced注解即可。负载平衡中又很多的负载平衡策略,如轮询(Round Robin)、权重(Weight)、ip_hash等。这些丰盛的策略让咱们在构建利用的时候,有很多抉择的余地,能够依据理论的业务场景抉择最合适的策略。

在Ribbon中一共提供了7中负载平衡策略:

策略类命名形容
RandomRule随机策略随机抉择Server
RoundRobinRule轮询策略按程序抉择Server
RetryRule重试策略在一个配置时间段内当抉择Server不胜利,则始终尝试抉择一个可用的Server
BestAvailableRule最低并发策略卓哥考查Server,如果Server断路器关上,则疏忽,再抉择其中并发连贯最低的Server
AvailabilityFilteringRule可用过滤策略过滤始终连贯失败并标记为circuit tripped的Server,过滤掉那些高并发连贯的Server(active connections超过配置的阈值)
ResponseTimeWeightedRule响应工夫加权策略曾经被弃用,作用同WeightedResponseTimeRule
WeightedResponseTimeRule响应工夫加权策略依据Server的响应工夫调配权重,响应工夫越长,权重越低,被选中的概率就越低。响应工夫越短,权重越高,被抉择到的概率越高
ZoneAvoidanceRule区域衡量策略综合判断Server所在区域的性能和Server的可用性轮询抉择Server,并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有Server

在下面的入门案例中Ribbon的默认负载平衡策略是轮询策略,

Ribbon自定义配置负载平衡策略

全局配置

应用Ribbon时配置全局的负载平衡策略,须要加一个配置类。改配置类须要被@ComponentScan扫描到能力全局失效。

@Configurationpublic class GlobalRuleConfig {    @Bean    public IRule ribbonRule() {        return new RandomRule();    }}

下面配置了随机的策略,屡次拜访http://localhost:7776/ribbon/...

基于@RibbonClient@RibbonClients注解的配置

配置类,留神:编写自定义配置类,须要特地留神的是官网文档明确给出了正告:这个自定义配置类不能放在@ComponentScan所扫描的包以及其子包下(即不能放在主启动类所在的包及其子包下,因而咱们须要新建一个包来放该配置类),否则咱们自定义的这个配置类就会被所有的Ribbon客户端所共享,也就达不到特殊化定制的目标了

@Configurationpublic class AnnoRuleConfig {    @Bean    public IRule ribbonRule() {        return new RandomRule();    }}

启动类的配置

@SpringBootApplication@EnableDiscoveryClient@RibbonClient(name = "ribbon-service-b", configuration = AnnoRuleConfig.class)public class ServiceAApplication {    public static void main(String[] args) {        SpringApplication.run(ServiceAApplication.class, args);    }    @LoadBalanced    @Bean    public RestTemplate restTemplate() {        return new RestTemplate();    }}

通过@RibbonClient指定某个服务的负载平衡策略,其余没有被指定的,就是用默认的负载平衡策略。该注解能够把其余的配置类作为另外一个IOC容器导入到利用中,相当于加载了两个齐全不相干的Spring的beans配置文件,此时利用中会有两个IOC容器。

@RibbonClient(name = "RIBBON-SERVICE-B", configuration = AnnoRuleConfig.class)

也能够应用一下的形式,指定多个服务的负载平衡策略

@RibbonClients(value = {        @RibbonClient(name = "RIBBON-SERVICE-B", configuration = AnnoRuleConfig.class),        @RibbonClient(name = "RIBBON-SERVICE-C", configuration = AnnoRuleConfig.class)})

基于配置文件

上面对服务ribbon-service-b的负载平衡策略应用

RIBBON-SERVICE-B:  ribbon:    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

Ribbon超时与重试

应用HTTP发动申请难免会产生问题,在F版开始Ribbon的重试机制默认是开启的,须要增加对超时工夫与重试策略的配置。列入上面ribbon-service-b服务的配置

RIBBON-SERVICE-B:  ribbon:    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule    ConnectTimeout: 3000    ReadTimeout: 60000    MaxAutoRetries: 3 #对第一次申请的服务的重试次数    MaxAutoRetriesNextServer: 1 #要重试的下一个服务的最大数量(不包含第一个服务)    OkToRetryOnAllOperations: true

也能够全局配置

ribbon:  ConnectTimeout: 3000  ReadTimeout: 60000  MaxAutoRetries: 3 #对第一次申请的服务的重试次数  MaxAutoRetriesNextServer: 1 #要重试的下一个服务的最大数量(不包含第一个服务)  OkToRetryOnAllOperations: true

个别Ribbon都是搭配OpenFeign这类Http客户端或者其余RPC应用。因为这样去调用近程服务会更加优雅和不便。而OpenFeign默认是继承了Ribbon,对于Ribbon的超时工夫配置也是很简略。

对于网络抖动这些能够应用spring-retry,spring-retry是spring提供的一个基于spring的重试框架,十分好用。

Ribbon饥饿加载

Ribbon在进行客户端负载平衡的时候,并不是启动时就加载上下文,而是在理论申请的时候采取创立。因为要加载上下文的起因,在第一次调用时可能会很慢,甚至导致超时。所以咱们能够指定Ribbon客户端开启立刻加载(饥饿加载),在利用启动的时候就立刻加载所有配置项的应用程序上下文。

ribbon:  eager-load:    clients: ribbon-service-b, ribbon-service-order    enabled: true

自定义Ribbon客户端

在Ribbon的1.2.0版本之后,就能够应用配置文件来定制Ribbon客户端,其实本质就是应用配置文件来指定一些默认加载类,从而更改Ribbon客户端的行为,并且应用这种形式优先级最高,优先级高于应用注解@RibbonClient指定的配置和源码中加载的相干的Bean。看下表:

配置项阐明
<clientName>.ribbon.NFLoadBalancerClassName指定ILoadBalancer的实现类
<clientName>.ribbon.NFLoadBalancerRuleClassName指定IRule的实现类
<clientName>.ribbon.NFLoadBalancerPingClassName指定IPing的实现类
<clientName>.ribbon.NiWSServerListClassName指定ServerList的实现类
<clientName>.ribbon.NIWSServerListFilterClassName指定ServerListFilter的实现类

例如:这里应用的实现的RIbbon提供的实现

RIBBON-SERVICE-B:  ribbon:    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule    NiWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList

Ribbon脱离Eureka应用

在默认的状况下,Ribbon客户端须要从Eureka注册核心读取服务注册信息列表,来达到一种动静负载平衡的性能。当应用的注册核心是公共的注册核心,例如:社区公益Eureka(http://eureka.springcloud.cn)...

ribbon:   eureka:    enabled: false#RIBBON-SERVICE-B服务的获取地址RIBBON-SERVICE-B:  ribbon:    listOfServers: http://localhost:8088/

Ribbon进阶

外围工作原理

Ribbon的外围接口:

接口形容默认实现
IClientConfig定义Ribbon中治理配置的接口DefaultClientConfigImpl
IRule定义Ribbon中负载平衡策略的接口ZoneAdvoidanceRule
IPing定义定期Ping服务查看可用性的接口DummyPing
ServerList\<Server>定义获取服务列表办法的接口ConfigurationBasedServerList
ServerListFilter\<Server>定义特定冀望获取服务列表办法的接口ZonePreferenceServerListFilter
ILoadBalancer定义负载平衡抉择服务的外围办法的接口ZoneAwareLoadBalancer
ServerListUpdater为DynamicServerListLoadBalancer定义动静更新服务列表的接口PollingServerListUpdater

Ribbon齐全是基于这些接口上建设起来的,是Ribbon的外围。理解这些外围的类的性能对于了解Ribbon的原理和扩大很无利。

在之前的例子中,应用Ribbon负载平衡都是通过在RestTemplate的Bean上增加注解@LoadBalanced,使得RestTemplate领有了负载平衡的能力。

LoadBalanced源码:

/** * Annotation to mark a RestTemplate or WebClient bean to be configured to use a * LoadBalancerClient. * @author Spencer Gibb */@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Qualifierpublic @interface LoadBalanced {}

在正文中能够看到:该注解标记在RestTemplate或者其余的WebClient的Bean上,来应用LoadBalancerClient

LoadBalancerClient:该接口扩大自ServiceInstanceChooser

public interface LoadBalancerClient extends ServiceInstanceChooser {    /**     * Executes request using a ServiceInstance from the LoadBalancer for the specified     * service.     * @param serviceId The service ID to look up the LoadBalancer.     * @param request Allows implementations to execute pre and post actions, such as     * incrementing metrics.     * @param <T> type of the response     * @throws IOException in case of IO issues.     * @return The result of the LoadBalancerRequest callback on the selected     * ServiceInstance.     */    <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;    /**     * Executes request using a ServiceInstance from the LoadBalancer for the specified     * service.     * @param serviceId The service ID to look up the LoadBalancer.     * @param serviceInstance The service to execute the request to.     * @param request Allows implementations to execute pre and post actions, such as     * incrementing metrics.     * @param <T> type of the response     * @throws IOException in case of IO issues.     * @return The result of the LoadBalancerRequest callback on the selected     * ServiceInstance.     */    <T> T execute(String serviceId, ServiceInstance serviceInstance,            LoadBalancerRequest<T> request) throws IOException;    /**     * Creates a proper URI with a real host and port for systems to utilize. Some systems     * use a URI with the logical service name as the host, such as     * http://myservice/path/to/service. This will replace the service name with the     * host:port from the ServiceInstance.     * @param instance service instance to reconstruct the URI     * @param original A URI with the host as a logical service name.     * @return A reconstructed URI.     */    URI reconstructURI(ServiceInstance instance, URI original);}

ServiceInstanceChooser:

public interface ServiceInstanceChooser {    /**     * Chooses a ServiceInstance from the LoadBalancer for the specified service.     * @param serviceId The service ID to look up the LoadBalancer.     * @return A ServiceInstance that matches the serviceId.     */    ServiceInstance choose(String serviceId);}
  • ServiceInstance choose(String serviceId):依据ServiceId,联合负载均衡器抉择一个服务实例
  • <T> T execute(String serviceId, LoadBalancerRequest<T> request):应用来自LoadBalancer的ServiceInstance为指定的服务执行申请
  • <T> T execute(String serviceId, ServiceInstance serviceInstance,

            LoadBalancerRequest<T> request):应用来自LoadBalancer的ServiceInstance为指定的服务执行申请,是上一个办法的重载,在实现类中能够看到它们的关系,就是前一个办法的细节实现、
  • URI reconstructURI(ServiceInstance instance, URI original):应用注解ip和port构建特定的URL以供Ribbon外部应用。Ribbon应用具备逻辑服务名称的URL作为host,例如:http://service-b/order/add。

从这些办法的性能能够晓得这两个接口的重要性了。这两个接口的同一包下有一个类LoadBalancerAutoConfigurationLoadBalancerAutoConfigurationorg.springframework.cloud.client.loadbalancer包下,在spring-cloud-commons外面。该主动配置类正式Ribbon的外围配置类。

@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();    @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);            };        }    }    /**     * Auto configuration for retry mechanism.     */    @Configuration(proxyBeanMethods = false)    @ConditionalOnClass(RetryTemplate.class)    public static class RetryAutoConfiguration {        @Bean        @ConditionalOnMissingBean        public LoadBalancedRetryFactory loadBalancedRetryFactory() {            return new LoadBalancedRetryFactory() {            };        }    }    /**     * Auto configuration for retry intercepting mechanism.     */    @Configuration(proxyBeanMethods = false)    @ConditionalOnClass(RetryTemplate.class)    public static class RetryInterceptorAutoConfiguration {        @Bean        @ConditionalOnMissingBean        public RetryLoadBalancerInterceptor ribbonInterceptor(                LoadBalancerClient loadBalancerClient,                LoadBalancerRetryProperties properties,                LoadBalancerRequestFactory requestFactory,                LoadBalancedRetryFactory loadBalancedRetryFactory) {            return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,                    requestFactory, loadBalancedRetryFactory);        }        @Bean        @ConditionalOnMissingBean        public RestTemplateCustomizer restTemplateCustomizer(                final RetryLoadBalancerInterceptor loadBalancerInterceptor) {            return restTemplate -> {                List<ClientHttpRequestInterceptor> list = new ArrayList<>(                        restTemplate.getInterceptors());                list.add(loadBalancerInterceptor);                restTemplate.setInterceptors(list);            };        }    }}

这就清晰起来了,他的配置加载机会是以后工程环境必须有RestTemplate的实例和必须初始化了LoadBalancerClient的实现类。

@ConditionalOnClass(RestTemplate.class)@ConditionalOnBean(LoadBalancerClient.class)

LoadBalancerRequestFactory:用于创立LoadBalancerRequest给LoadBalancerInterceptor应用。

LoadBalancerInterceptorConfig:保护了LoadBalancerInterceptor与RestTemplateCustomizer的实例。

  • LoadBalancerInterceptor:拦挡每一次的HTTP申请,将申请绑定金Ribbon的负载平衡的生命周期。
  • RestTemplateCustomizer:为每一个Restemplate绑定LoadBalancerInterceptor拦截器。

LoadBalancerInterceptor的作用曾经和贴近答案了。

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));    }    @Override    public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,            final ClientHttpRequestExecution execution) throws IOException {        final URI originalUri = request.getURI();        String serviceName = originalUri.getHost();        Assert.state(serviceName != null,                "Request URI does not contain a valid hostname: " + originalUri);        return this.loadBalancer.execute(serviceName,                this.requestFactory.createRequest(request, body, execution));    }}

在源码能够看到它是通过ClientHttpRequestInterceptor实现每次对HTTP申请的拦挡,ClientHttpRequestInterceptor类是Spring中保护的申请拦截器,实现它的intercept办法就能够使得申请进入办法内,从而Ribbon就能够做一些本人的解决了。

在应用RestTemplate申请服务时应用的URI:http://serviceName/path/to/se...

LoadBalancerInterceptor中的intercept办法,最终调用的是RibbonLoadBalancerClient的execute办法:

    public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {        // 拿到负载均衡器的实现        ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);        // 拿到具体的Server        Server server = this.getServer(loadBalancer, hint);        if (server == null) {            throw new IllegalStateException("No instances available for " + serviceId);        } else {            RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));            return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);        }    }    protected Server getServer(ILoadBalancer loadBalancer, Object hint) {        return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");    }

对于chooseServer是接口ILoadBalancer的办法,这里就先看一下其中的一个实现BaseLoadBalancer

    public Server chooseServer(Object key) {        if (this.counter == null) {            this.counter = this.createCounter();        }        this.counter.increment();        if (this.rule == null) {            return null;        } else {            try {                return this.rule.choose(key);            } catch (Exception var3) {                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", new Object[]{this.name, key, var3});                return null;            }        }    }

最初是通过:rule.choose(key)拿到Server,而rule就是IRule。

RibbonClientConfiguration中初始化了下面表格提到几个外围类

  1. 初始化ribbonRule: ZoneAvoidanceRule
  2. 初始化ribbonPing:DummyPing
  3. 初始化ribbonServerList:ConfigurationBasedServerList
  4. 初始化ServerListUpdater:new PollingServerListUpdater(config)
  5. 初始化ILoadBalancer:ZoneAwareLoadBalancer
  6. 初始化ribbonServerListFilter:ZonePreferenceServerListFilter
  7. 初始化ribbonLoadBalancerContext:RibbonLoadBalancerContext
  8. 初始化serverIntrospector:DefaultServerIntrospector

对于BlockingLoadBalancerClient:

Spring Cloud Hoxton.RELEASE 版本公布之后,新增了一个新的负载均衡器实现BlockingLoadBalancerClient。它是第一个蕴含阻塞式非阻塞式负载均衡器客户端实现的版本,作为已进入保护模式的Netflix Ribbon的代替计划。

如果想在 RestTemplate应用新的 BlockingLoadBalancerClient, 须要减少 spring-cloud-loadbalancer 的依赖,否则默认应用RibbonLoadBalancerClient