三、Spring Cloud Netflix Ribbon 客户端的负载均衡

本章简单使用Spring Cloud Netflix Ribbon进行客户端的负载均衡,并分析其原理实现。
官网地址 spring-cloud-ribbon

一、 RESTful HTTP协议通信

REST,全称是Resource Representational State Transfer,即:资源在网络中以某种形式进行状态转移。规范了HTTP通信协议的标准:

  • HTTP METHOD 约束资源操作类型 GET/POST/PUT/DELETE
Verd 描述
GET(SELECT) 查询
POST(CREATE) 提交
PUT(UPDATE) 修改,客户端需要提供新建资源的所有属性
DELETE(DELETE) 删除
  • REST是面向资源的:

以订单接口为例
根据id获取订单信息:对应类型 GET METHOD

/order(GET)  /order/${id}

保存订单: 对应类型 POST METHOD

/order(POST) 

修改订单:对应类型 PUT METHOD

/order(PUT) 

删除订单:对应类型 DELETE METHOD

/order(DELETE)  /order/${id}

查询订单列表: 使用

/orders
  • 名词

以根据id查询订单明细为例,普通接口命名可能是queryOrderById,而RESTful接口则是(GET) /order/${id}

  • HTTP返回码
状态码 描述
2XX 请求正常处理并返回
3XX 重定向,请求的资源位置发生变化
4XX 客户端发送的请求有误
5XX 服务器端的错误

这里有一篇RESTful详细的介绍,转载下 老_张 RESTful API浅谈

二、RestTemplate

当服务由单体架构一步步演变为集群->垂直拆分->SOA->微服务后,不可避免的涉及到服务间的相互通信。
此时我们可以使用 HttpClient 、 RestTemplate 、 OkHttp 、JDK HttpUrlConnection这几种方式来达到通信数据交互的目的。
以电商的用户、订单服务为例,当用户服务内需要根据用户找到所有订单时,则用户服务会与订单服务建立HTTP通信:

下面基于SpringBoot简单演示接口实现:

1. OrderService

提供订单查询接口http://localhost:8081/orders供UserService调用:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @GetMapping("/orders")
    public String getAllOrder() {

        return "Shen all orders";
    }

}

2. UserService

UserServer使用RestTemplate进行服务调用

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class UserController {

    @Autowired
    RestTemplate restTemplate;

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

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
        return restTemplateBuilder.build();
    }

    @GetMapping("/user/{id}")
    public String findById(@PathVariable("id") int id) {

        // 调用远程订单服务
        // HttpClient 、 RestTemplate 、 OkHttp 、JDK HttpUrlConnection
        String rst = restTemplate.getForObject("http://localhost:8081/orders", String.class);
        return rst;
    }

}

三、Spring Cloud Netflix Ribbon 集群负载均衡调用

上例子是以单服务节点的调用为例,真实场景中为了避免单点故障、提升并发效率,都会部署多台服务,此时就涉及客户端在同时调用多台服务时的负载均衡问题。

此时服务提供者OrderService的地址,可以先维护在客户端UserService的配置文件内,先看一下以Ribbon方法来实现客户端的负载均衡。

1. 声明LoadBalancerClient实现负载均衡

OrderService

服务提供方,订单服务,此时将服务部署在8081和8082两个不同端口,Intellij IDEA中支持同一个SpringBoot项目同时配置多个端口:

UserService

服务调用方、客户端 用户服务:

  • 首先引入 netflix ribbot依赖
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>2.2.3.RELEASE</version>
        </dependency>
  • 之后,application.properties配置文件内根据Ribbon规则配置服务地址
# 配置服务提供者的地址
spring-cloud-order-service.ribbon-listOfServers=
  localhost:8081,localhost:8082 
  • 最后,客户端实现负载均衡改造

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class UserController {

    @Autowired
    RestTemplate restTemplate;

    @Autowired
    LoadBalancerClient loadBalancerClient;

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
        return restTemplateBuilder.build();
    }

    @GetMapping("/user/{id}")
    public String findById(@PathVariable("id") int id) {

        // 调用远程订单服务
        // HttpClient 、 RestTemplate 、 OkHttp 、JDK HttpUrlConnection
//        String rst = restTemplate.getForObject("http://localhost:8081/orders", String.class);

        ServiceInstance serviceInstance = loadBalancerClient.choose("spring-cloud-order-service");
        String url = String.format("http://%s:%s" + "/orders", serviceInstance.getHost(), serviceInstance.getPort());
        System.out.println(url);
        
        String rst = restTemplate.getForObject(url, String.class);
        return rst;
    }

}

当我们多次调用客户端/user/{id}服务后,控制台输出如下,可见用户服务在调用集群订单服务时,已经实现了负载均衡,其实内部算法是默认的轮询机制。

http://localhost:8081/orders
http://localhost:8082/orders
http://localhost:8081/orders
http://localhost:8082/orders
http://localhost:8081/orders
http://localhost:8082/orders
http://localhost:8081/orders
http://localhost:8082/orders
http://localhost:8081/orders
http://localhost:8082/orders
http://localhost:8081/orders
http://localhost:8082/orders
http://localhost:8081/orders
http://localhost:8082/orders

2. 通过注解@LoadBalanced注解实现自动负载均衡

除了上面直接声明 @Autowired LoadBalancerClient loadBalancerClient;负载均衡客户端外,也可以通过@LoadBalanced注解来声明一个RestTemplate来达到负载均衡的目的

UserService

其余配置不变,将声明RestTemplate的Bean加上@LoadBalanced注解,服务调用地址也改为http://spring-cloud-order-service,与配置文件内的key相对应。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class UserController2 {

    @Autowired
    RestTemplate restTemplate;

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

    @GetMapping("/user2/{id}")
    public String getBuId(@PathVariable("id") int id) {

        String rst = restTemplate.getForObject("http://spring-cloud-order-service" + "/orders", String.class);
        return rst;
    }

}

OrderService

订单服务其实可以不变,不过为了方便看到客户端负载均衡调用的结果,可以在订单服务内输出服务端口,来验证:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @Value("${server.port}")
    private int port;

    @GetMapping("/orders")
    public String getAllOrder() {
        System.out.println(port);
        return "Shen all orders";
    }

}

这样就更方便的实现了负载均衡的调用。

本地案例demo地址:Spring-Cloud-Netflix-Ribbon Demo

四、Ribbon 源码分析

Ribbon其实是基于客户端的负载均衡的一个组件,从上面【二、1】例子的使用其实可以推测,Ribbon实现负载均衡很可能做了两个事情:

  • 解析配置拿到服务方地址列表
  • 基于负载均衡算法实现请求的分发

从【二、2】的例子,为RestTemplate添加了@LoadBalanced注解可以自动负载均衡,可以尝试推测:

  • 上面两个步骤需要做
  • 需要在某个地方扫描加了@LoadBalanced注解的RestTemplate,并对RestTemplate做改造,来实现地址的动态解析

A、对RestTemplate添加@LoadBalanced注解进行修饰

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

我们只是为 RestTemplate 添加了 LoadBalanced注解,为什么可以实现负载均衡,肯定是某些地方会扫描 加了@LoadBalanced注解的 RestTemplate,并做相应改造

B、LoadBalancerAutoConfiguration获取到添加了@LoadBalanced的RestTemplate,进行包装

  • 我们可以先看下@LoadBalanced源码,可以看到除了配置了一些支持的特性外,本质是一个 @Qualifier,相当于是一个继承了@Qualifier@LoadBalanced标记。
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {

}
  • 而直接使用@LoadBalanced注解,可以看做是使用了Spring的Bean的自动装配的特性,那么应该是在spring.factories内配置了对应声明Bean的Configuration,

果然,我们可以在jar包内的META-INF/spring.factories内找到维护了EnableAutoConfigurationLoadBalancerAutoConfiguration配置:

  1. @LoadBalanced注解是在spring-cloud-commons-2.2.3.RELEASE.jar内的
  2. 在该jar包的META-INF的spring.factories内可以找到对应Configuration: LoadBalancerAutoConfiguration
  • 现在我们就可以定位,LoadBalancerAutoConfiguration 内是如何对RestTemplate进行改造,来达到加了LoadBalanced注解就可以自动负载均衡

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)  // 自动装配条件 onClass
@ConditionalOnBean(LoadBalancerClient.class)   // 自动装配条件  onBean
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

        // Spring 的依赖注入
        // 拿到加了@LoadBalanced标记的RestTemplate放入list集合中去,也就是我们之前在UserController2 里声明的`@LoadBalanced RestTemplate`
	@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);
				}
			}
		});
	}

C、LoadBalancerAutoConfiguration为这些RestTemplate添加拦截器

在Configuration类做了一系列的Bean的初始化,且Bean的初始化其实是有先后顺序的依赖关系的:

  • LoadBalancerAutoConfiguration 源码

LoadBalancerAutoConfiguration 类里声明了一系列Bean,并且Bean的初始化由先后依赖关系


@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();  // 获取到所有加了 @LoadBalanced 声明的 RestTemplate 集合

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

        // `restTemplates`集合是在这里使用的,所有可以以这里的@Bean,Bean的初始化为出发点
        // 因为是@Bean进行方法参数的依赖注入,存在Bean的初始化的依赖关系;大致有 C01 -> C02 -> C03 -> C04 -> C05 几个步骤的Bean初始化
	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {   // C01. 初始化SmartInitializingSingleton Bean,依赖注入 RestTemplateCustomizer -> C02
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
			for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {  // C06. 获取到所有restTemplates集合,
				for (RestTemplateCustomizer customizer : customizers) { 
					customizer.customize(restTemplate);   // C07. 对这些RestTemplate进行包装,其实调用的是 RestTemplateCustomizer.customize 至C08  
				}
			}
		});
	}

	@Bean
	@ConditionalOnMissingBean
	public LoadBalancerRequestFactory loadBalancerRequestFactory(  // C05.初始化 LoadBalancerRequestFactory Bean,也依赖于 LoadBalancerClient Bean的初始化 -> C04
			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) {   //  C03. 初始化LoadBalancerInterceptor,依赖于 LoadBalancerClient -> C04   和   LoadBalancerRequestFactory -> C05
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}

		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {  // C02.  初始化RestTemplateCustomizer Bean,依赖于 LoadBalancerInterceptor -> C03
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());   // C08. 取得默认的拦截器列表
				list.add(loadBalancerInterceptor);  // C09. 添加 loadBalancerInterceptor 拦截器,类型为 LoadBalancerInterceptor 
				restTemplate.setInterceptors(list);  // C10. 为restTemplate添加 LoadBalancerInterceptor 拦截器
			};
		}

	}


}

  • RibbonAutoConfiguration 源码,

对应 C04. 初始化 LoadBalancerClient Bean
前面C01-C03几步可以看到,在对RestTemplate进行封装时,都需要 LoadBalancerClient Bean的实例,那么这个Bean是在什么时候进行初始化的呢?
因为我们使用的是Ribbon,所以推测 LoadBalancerClient 的自动装配是在 Ribbon 的 jar包内实现的。
定位到 META-INF/spring.factories文件的AutoConfiguration配置

@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(
		name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
		AsyncLoadBalancerAutoConfiguration.class })  // AutoConfigureBefore,定义了RibbonAutoConfiguration 的初始化是在LoadBalancerAutoConfiguration之前,也和我们上面几步分析的Bean初始化顺序一致
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
		ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {

	@Autowired(required = false)
	private List<RibbonClientSpecification> configurations = new ArrayList<>();

	@Autowired
	private RibbonEagerLoadProperties ribbonEagerLoadProperties;


        // LoadBalancerClient Bean的初始化,使用的是 RibbonLoadBalancerClient
	@Bean
	@ConditionalOnMissingBean(LoadBalancerClient.class)
	public LoadBalancerClient loadBalancerClient() {   // C04. 初始化 LoadBalancerClient Bean
		return new RibbonLoadBalancerClient(springClientFactory());  // 其实就是自动装配了 RibbonLoadBalancerClient 
	}

至此,我们已经看到了一条Bean初始化的先后顺序:

首先:在 spring-cloud-netflix-ribbon-2.2.3.RELEASE.jar 内 根据Bena的自动装配规则初始化: LoadBalancerClient
RibbonAutoConfiguration : [ LoadBalancerClient(RibbonLoadBalancerClient) ]

-> 

之后: 在 spring-cloud-commons-2.2.3.RELEASE.jar 内,通过一系列Bean的先后初始化达到对RestTemplate进行包装的目的
LoadBalancerAutoConfiguration :[ LoadBalancerRequestFactory -> LoadBalancerInterceptor -> RestTemplateCustomizer -> SmartInitializingSingleton ]
 也正是这条链路来实现了对RestTemplate的重新包装

而这些Bean的初始化,其实是为了对加了@LoadBalanced注解的RestTemplate进行包装,包装内部逻辑是为RestTemplate添加拦截器LoadBalancerInterceptor restTemplate.setInterceptors(list);

D、LoadBalancerInterceptor对请求进行拦截

因为之前是为 RestTemplate 添加了 LoadBalancerInterceptor 拦截器,所以当我们进行restTemplate.getForObject("http://spring-cloud-order-service" + "/orders", String.class);调用时,一定会进入拦截器LoadBalancerInterceptor#intercept内:

  • LoadBalancerInterceptor#intercept 对 请求进行拦截

LoadBalancerInterceptor#intercept 拦截器 intercept() 方法对请求 request 进行拦截

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

	private LoadBalancerClient loadBalancer;  // D01. 此时的LoadBalancerClient,其实就是我们之前分析过得,在 RibbonAutoConfiguration 内声明的 RibbonLoadBalancerClient

	@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));  //   D02. 取得负载均衡器,执行相应逻辑
	}

}

  • 负载均衡器来获取其中一个srever
org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#execute(java.lang.String, org.springframework.cloud.client.loadbalancer.LoadBalancerRequest<T>)
	@Override
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
			throws IOException {
		return execute(serviceId, request, null);  // D03. execute 
	}

org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#execute(java.lang.String, org.springframework.cloud.client.loadbalancer.LoadBalancerRequest<T>, java.lang.Object)
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);  // D04. 获得负载均衡器
		Server server = getServer(loadBalancer, hint);  // D05. 根据负载均衡器获得一个 server,就是我们维护在配置文件中的服务地址之一
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		RibbonServer ribbonServer = new RibbonServer(serviceId, server,
				isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));

		return execute(serviceId, ribbonServer, request);
	}
  • 获取server逻辑

源码过多,在此列出方法流转的流程,主要逻辑是
先解析到配置文件内的地址列表,然后根据负载均衡算法得到一个服务地址


org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#getServer(com.netflix.loadbalancer.ILoadBalancer, java.lang.Object)
  com.netflix.loadbalancer.ZoneAwareLoadBalancer#chooseServer
    com.netflix.loadbalancer.BaseLoadBalancer#chooseServer
      com.netflix.loadbalancer.PredicateBasedRule#choose
	 com.netflix.loadbalancer.AbstractServerPredicate#chooseRoundRobinAfterFiltering(java.util.List<com.netflix.loadbalancer.Server>, java.lang.Object)
	   com.netflix.loadbalancer.AbstractServerPredicate#incrementAndGetModulo
			

E、负载均衡算法

所支持的负载均衡算法可以从com.netflix.loadbalancer.IRule类图中看出:

RoundRobinRule(轮询算法)
RandomRule(随机算法)
AvailabilityFilteringRule():会先过滤由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问
WeightedResponseTimeRule():根据平均响应的时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高,刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够会切换到WeightedResponseTimeRule
RetryRule():先按照RoundRobinRule的策略获取服务,如果获取失败则在制定时间内进行重试,获取可用的服务。
BestAviableRule():会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
ZoneAvoidanceRule():默认规则,符合判断server所在区域的性能和server的可用性选择服务器

其中Ribbon默认的负载均衡算法为RoundRobinRule轮询算法,实现也比较简单

com.netflix.loadbalancer.RoundRobinRule#choose(com.netflix.loadbalancer.ILoadBalancer, java.lang.Object)

    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }

        Server server = null;
        int count = 0;
        while (server == null && count++ < 10) {
            List<Server> reachableServers = lb.getReachableServers();
            List<Server> allServers = lb.getAllServers();   // 1. 获取到所有服务地址
            int upCount = reachableServers.size();
            int serverCount = allServers.size();  // 2. 服务地址数量

            if ((upCount == 0) || (serverCount == 0)) {
                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }

            int nextServerIndex = incrementAndGetModulo(serverCount);  // 3. 递增拿到当前轮训数量
            server = allServers.get(nextServerIndex);  // 8. 根据下标从服务列表List中获取到对应地址

            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }

            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);  // 9. 返回服务地址
            }

            // Next.
            server = null;
        }

        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                    + lb);
        }
        return server;
    }

    private AtomicInteger nextServerCyclicCounter;
    private int incrementAndGetModulo(int modulo) {
        for (;;) {
            int current = nextServerCyclicCounter.get();   // 4. 获取当前游标
            int next = (current + 1) % modulo;  // 5. +1,和服务总数量取模
            if (nextServerCyclicCounter.compareAndSet(current, next))   // 6. cas保证线程安全
                return next;  // 7. 返回递增后的游标
        }
    }

总结:

使用Ribbon的两种方式:

  • 为RestTemplate添加@LoadBalanced
  • 直接使用@Autowired LoadBalancerClient loadBalancerClient;客户端

Ribbon原理的主要几种类型:

  • ILoadBalancer 负载均衡器
  • IRule 负载均衡算法规则(权重机制(区间算法))
  • IPing 存活状态监测 定时任务在不断地发起请求,ping,每10s去访问一次目标服务的地址,如果不可用则剔除无效服务
  • ServerList 定时任务每30s执行一次更新服务列表
  • 自定义负载均衡算法、自定义Ping

官网介绍可从:https://spring.io/ -> 头部导航 Project/SpringCloud -> 左侧导航 Spring Cloud Netflix -> Learn/Reference Doc. -> 左侧导航 7. Client Side Load Balancer: Ribbon 内看到
本地案例demo地址:Spring-Cloud-Netflix-Ribbon Demo

原文地址:https://www.cnblogs.com/Qkxh320/p/SpringCloud_netflix_ribbon.html