《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Spring-Cloud-Netflix/Ribbon/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labslabx-02-spring-cloud-netflix-ribbon 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

1. 概述

本文我们来学习 Spring Cloud Netflix 提供的 Spring Cloud Netflix Ribbon 组件,基于 Spring Cloud 的编程模型,接入 Ribbon 框架,实现客户端级别的负载均衡,完成服务实例的选择与调用。

可能胖友对 Ribbon 框架不是很了解,我们来简单看看 Ribbon 的简介:

FROM https://github.com/Netflix/ribbon

Ribbon is a client side IPC(Inter Process Communication) library that is battle-tested in cloud. It provides the following features

  • Load balancing
  • Fault tolerance
  • Multiple protocol (HTTP, TCP, UDP) support in an asynchronous and reactive model
  • Caching and batching

Ribbon 是一个进程间通信的客户端库,并且已经在云环境下经过大量的生产测试。它提供了以下功能:

  • 负载均衡
  • 容错机制
  • 支持 HTTP、TCP、UDP 等多种协议,并支持异步和响应式的调用方式
  • 缓存与批处理

2. 负载均衡的实现方式

为了保证服务的可用性,我们会给每个服务部署多个实例,避免因为单个实例挂掉之后,导致整个服务不可用。并且,因为每个服务部署了多个实例,也提升了服务的承载能力,可以同时处理更多的请求。

不过此时需要考虑到需要将请求均衡合理的分配,保证每个服务实例的负载。一般来说,我们有两种方式实现服务的负载均衡,分别是客户端级别服务端级别

服务端级别的负载均衡,客户端通过外部的代理服务器,将请求转发到后端的多个服务。比较常见的有 Nginx 服务器,如下图所示:服务端级别

客户端级别的负载均衡,客户端通过内嵌的“代理”,将请求转发到后端的多个服务。比较常见的有 Dubbo、Ribbon 框架提供的负载均衡功能,如下图所示:客户端级别

相比来说,客户端级别的负载均衡可以有更好的性能,因为不需要多经过一层代理服务器。并且,服务端级别的负载均衡需要额外考虑代理服务的高可用,以及请求量较大时的负载压力。因此,在微服务场景下,一般采用客户端级别的负载均衡为主。

对于客户端的负载均衡来说,最好搭配注册中心一起使用。这样,服务实例的启动和关闭,可以向注册中心发起注册和取消注册,保证能够动态的通知到客户端。

本文,我们会使用 Ribbon 提供客户端级别的负载均衡,使用 Nacos 作为注册中心。整体架构图如下:Ribbon + Nacos

友情提示:如果对 Nacos 不了解的胖友,需要先前置阅读《芋道 Spring Cloud Alibaba 注册中心 Nacos 入门》文章。

3. 快速入门

示例代码对应仓库:

本小节,我们来搭建一个 Spring Cloud Netflix Ribbon 组件的快速入门示例。步骤如下:

  • 首先,搭建一个服务提供者 demo-provider,启动 2 个实例,注册服务到 Nacos 中。
  • 然后,搭建一个服务消费者 demo-consumer,使用 Ribbon 进行负载均衡,调用服务提供者 demo-provider 的 HTTP 接口。

3.1 搭建服务提供者

创建 labx-02-scn-ribbon-demo01-provider 项目,作为服务提供者 demo-provider。最终项目代码如下图所示:`demo-provider` 项目

3.1.1 引入依赖

pom.xml 文件中,主要引入 Spring Cloud Nacos Discovery 相关依赖。代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>labx-02</artifactId>
<groupId>cn.iocoder.springboot.labs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>labx-02-scn-ribbon-demo01-consumer</artifactId>

<properties>
<spring.boot.version>2.2.4.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>
</properties>

<!--
引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。
在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系
-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- 引入 SpringMVC 相关依赖,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖,将 Nacos 作为注册中心,并实现对其的自动配置 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>

</project>
  • 具体每个依赖的作用,请看艿艿添加的注释。

3.1.2 配置文件

创建 application.yaml 配置类,添加相应配置项。配置如下:

spring:
application:
name: demo-provider # Spring 应用名
cloud:
nacos:
# Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址

server:
port: ${random.int[10000,19999]} # 服务器端口。默认为 8080

因为稍后我们会启动 2 个服务提供者的实例,所以我们设置 server.port 配置项为 10000 至 19999 之间随机。

3.1.3 DemoProviderApplication

创建 DemoProviderApplication 类,创建应用启动类,并提供 HTTP 接口。代码如下:

@SpringBootApplication
public class DemoProviderApplication {

public static void main(String[] args) {
SpringApplication.run(DemoProviderApplication.class, args);
}

@RestController
static class TestController {

private Logger logger = LoggerFactory.getLogger(TestController.class);

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

@GetMapping("/echo")
public String echo(String name) throws InterruptedException {
// 模拟执行 100ms 时长。方便后续我们测试请求超时
Thread.sleep(100L);

// 记录被调用的日志
logger.info("[echo][被调用啦 name({})]", name);

return serverPort + "-provider:" + name;
}

}

}

TestController 提供的 /echo 接口,返回的结果会带有启动的服务器的端口。这样我们稍后在服务消费者使用 Ribbon 调用服务提供者时,从返回结果就知道调用的是哪个实例。

3.2 搭建服务消费者

创建 labx-02-scn-ribbon-demo01-consumer 项目,作为服务提供者 demo-consumer。最终项目代码如下图所示:`demo-provider` 项目

整个项目的代码,和服务提供者是基本一致的,毕竟是示例代码 😜

3.2.1 引入依赖

pom.xml 文件中,主要引入 Spring Cloud Nacos Discovery + Spring Cloud Netflix Ribbon 相关依赖。代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>labx-02</artifactId>
<groupId>cn.iocoder.springboot.labs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>labx-02-scn-ribbon-demo01-consumer</artifactId>

<properties>
<spring.boot.version>2.2.4.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>
</properties>

<!--
引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。
在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系
-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- 引入 SpringMVC 相关依赖,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖,将 Nacos 作为注册中心,并实现对其的自动配置 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>

</project>

这里我们没有主动引入 spring-cloud-netflix-ribbon 依赖,因为 spring-cloud-starter-alibaba-nacos-discovery 默认引入了它。如下图所示:依赖关系

3.2.2 配置文件

创建 application.yaml 配置类,添加相应配置项。配置如下:

spring:
application:
name: demo-consumer # Spring 应用名
cloud:
nacos:
# Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址

server:
port: 28080 # 服务器端口。默认为 8080

3.2.3 DemoConsumerApplication

创建 DemoConsumerApplication 类,创建应用启动类,并提供一个调用服务提供者的 HTTP 接口。代码如下:

@SpringBootApplication
public class DemoConsumerApplication {

public static void main(String[] args) {
SpringApplication.run(DemoConsumerApplication.class, args);
}

@Configuration
public class RestTemplateConfiguration {

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

}

@RestController
static class TestController {

@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;

@GetMapping("/hello")
public String hello(String name) {
// 获得服务 `demo-provider` 的一个实例
ServiceInstance instance = loadBalancerClient.choose("demo-provider");
// 发起调用
String targetUrl = instance.getUri() + "/echo?name=" + name;
String response = restTemplate.getForObject(targetUrl, String.class);
// 返回结果
return "consumer:" + response;
}

@GetMapping("/hello02")
public String hello02(String name) {
// 直接使用 RestTemplate 调用服务 `demo-provider`
String targetUrl = "http://demo-provider/echo?name=" + name;
String response = restTemplate.getForObject(targetUrl, String.class);
// 返回结果
return "consumer:" + response;
}

}

}

@LoadBalanced 注解,声明 RestTemplate Bean 被配置使用 Spring Cloud LoadBalancerClient(负载均衡客户端),实现在请求目标服务时,能够进行负载均衡。

FROM @LoadBalanced 注解的类注释

Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.

我们把 @LoadBalanced 注解,添加在 RestTemplateConfiguration 创建的 RestTemplate Bean 上,最终效果如下图:`@LoadBalanced` + RestTemplate

② TestController 提供了 /hello/hello02 接口,都用于调用服务提供者的 /demo 接口。代码略微有几行,我们来稍微解释下哈。

loadBalancerClient 属性,LoadBalancerClient 对象,负载均衡客户端。稍后我们会使用它,从 Nacos 获取的服务 demo-provider 的实例列表中,选择一个进行 HTTP 调用。

拓展小知识:在 Spring Cloud Common 项目中,定义了LoadBalancerClient 接口,作为通用的负载均衡客户端,提供从指定服务中选择一个实例、对指定服务发起请求等 API 方法。而想要集成到 Spring Cloud 体系的负载均衡的组件,需要提供对应的 LoadBalancerClient 实现类。

例如说,Spring Cloud Netflix Ribbon 提供了 RibbonLoadBalancerClient 实现。

如此,所有需要使用到的地方,只需要获取到 DiscoveryClient 客户端,而无需关注具体实现,保证其通用性。😈 不过貌似 Spring Cloud 体系中,暂时只有 Ribbon 一个负载均衡组件。

当然,LoadBalancerClient 的服务的实例列表,是来自 DiscoveryClient 提供的。

/hello 接口,使用 LoadBalancerClient 先选择服务 demo-provider 的一个实例,在使用 RestTemplate 调用服务 demo-provider/demo 接口。不过要注意,这里执行会报如下异常:

java.lang.IllegalStateException: No instances available for 10.171.1.115
  • 因为我们这里创建的 RestTemplate Bean 是添加了 @LoadBalanced 注解,它会把传入的 "10.171.1.115" 当做一个服务,显然是找不到对应的服务实例,所以会报 IllegalStateException 异常。
  • 解决办法也非常简单,再声明一个未使用 @LoadBalanced 注解的 RestTemplate Bean 即可,并使用它发起请求。

/hello02 接口,直接使用 RestTemplate 调用服务 demo-provider,代码精简了。这里要注意,在使用 @LoadBalanced 注解的 RestTemplate Bean 发起 HTTP 请求时,需要将原本准备传入的 host:port 修改成服务名,例如这里我们传入了 demo-provider

虽然 /hello02 接口相比 /hello 接口只精简了一行代码,但是它带来的不仅仅是表面所看到的。例如说,如果我们调用服务的一个实例失败时,想要重试另外一个示例,就存在了很大的差异。

  • /hello02 接口的方式,可以自动通过 LoadBalancerClient 重新选择一个该服务的实例,再次发起调用。
  • /hello 接口的方式,需要自己手动写逻辑,使用 LoadBalancerClient 重新选择一个该服务的实例,后交给 RestTemplate 再发起调用。

稍后我们在「9. 请求重试」小节,就会提供接口重试的示例。

3.3 简单测试

① 通过 DemoProviderApplication 启动 2 次,启动服务提供者的 2 个实例。因为 IDEA 默认同一个程序只允许启动 1 次,所以我们需要配置 DemoProviderApplication 为 Allow parallel run。如下图所示:Allow parallel run

打开 Nacos 控制台,可以在服务列表看到服务 demo-consumer 有 2 个实例。如下图:服务列表

② 通过 DemoConsumerApplication 启动服务消费者。

访问服务消费者http://127.0.0.1:28080/hello02?name=yudaoyuanma 接口 6 次,返回结果如下:

consumer:10239-provider:123
consumer:14648-provider:123

consumer:10239-provider:123
consumer:14648-provider:123

consumer:10239-provider:123
consumer:14648-provider:123

在默认配置下,Ribbon 采用 ZoneAvoidanceRule 负载均衡策略,在未配置所在区域的情况下,和轮询负载均衡策略是相对等价的。所以服务消费者 demo-consumer 调用服务提供者 demo-provider 时,顺序将请求分配给每个实例。

4. 负载均衡规则

Ribbon 内置了 7 种负载均衡规则,如下图所示:负载均衡策略

每个发负载均衡规则说明如下:

FROM 《深入理解 RPC 之集群篇》

策略名 策略描述 实现说明
RandomRule 随机选择一个 server 在 index 上随机,选择 index 对应位置的 Server
RoundRobinRule 轮询选择 server 轮询 index,选择 index 对应位置的 server
ZoneAvoidanceRule 复合判断 server 所在区域的性能和 server 的可用性选择 server 使用 ZoneAvoidancePredicate 和 AvailabilityPredicate 来判断是否选择某个 server。ZoneAvoidancePredicate 判断判定一个 zone 的运行性能是否可用,剔除不可用的 zone(的所有 server);AvailabilityPredicate 用于过滤掉连接数过多的 server。
BestAvailableRule 选择一个最小并发请求的 server 逐个考察 server,如果 server 被 tripped 了则忽略,在选择其中activeRequestsCount 最小的 server
AvailabilityFilteringRule 过滤掉那些因为一直连接失败的被标记为 circuit tripped 的后端 server,并过滤掉那些高并发的的后端 server(activeConnections 超过配置的阈值) 使用一个 AvailabilityPredicate 来包含过滤 server 的逻辑,其实就就是检查 status 里记录的各个 server 的运行状态
WeightedResponseTimeRule 根据 server 的响应时间分配一个 weight,响应时间越长,weight 越小,被选中的可能性越低 一个后台线程定期的从 status 里面读取评价响应时间,为每个 server 计算一个 weight。weight 的计算也比较简单,responseTime 减去每个 server 自己平均的 responseTime 是 server 的权重。当刚开始运行,没有形成 status 时,使用 RoundRobinRule 策略选择 server。
RetryRule 对选定的负载均衡策略机上重试机制 在一个配置时间段内当选择 server 不成功,则一直尝试使用 subRule 的方式选择一个可用的 server

默认情况下,Ribbon 采用 ZoneAvoidanceRule 规则。因为大多数公司是单机房,所以一般只有一个 zone,而 ZoneAvoidanceRule 在仅有一个 zone 的情况下,会退化成轮询的选择方式,所以会和 RoundRobinRule 规则类似。

不同框架或者组件,会采用不同的负载均衡规则,感兴趣的胖友可以阅读如下文章:

总的来说,经典的负载均衡规则可以整理如下:

  • 轮询(Round Robin) or 加权轮询(Weighted Round Robin)
  • 随机(Random) or 加权随机(Weighted Random)
  • 源地址哈希(Hash) or 一致性哈希(ConsistentHash)
  • 最少连接数(Least Connections)
  • 最小响应时间(ResponseTime)

5. 自定义 Ribbon 配置

本小节,我们来学习如何对 Ribbon 进行自定义配置。例如说,不再使用默认的负载均衡规则 ZoneAvoidanceRule,而是使用随机负载均衡规则 RandomRule。

在自定义 Ribbon 配置的时候,会有全局客户端两种级别。相比来说,客户端级别是更细粒度的配置。针对每个服务,Spring Cloud Netflix Ribbon 会创建一个 Ribbon 客户端,并且使用服务名作为 Ribbon 客户端的名字

实现 Ribbon 自定义配置,可以通过配置文件Spring JavaConfig 两种方式。

下面,让我们来搭建下具体的示例。注意,本小节的所有示例,都是从「3.2 搭建服务消费者」小节的 labx-02-scn-ribbon-demo01-consumer 项目,复制出一个项目,然后在将其配置,修改使用随机负载均衡规则 RandomRule。

5.1 配置文件方式

示例代码对应仓库:

本小节,我们使用配置文件的方式,实现 Ribbon 客户端级别的自定义配置。注意,是客户端级别噢。

旁白君:暂时没有找到配置文件的方式,实现 Ribbon 全局级别的自定义配置。搜了官方文档,也暂时没找到,目前猜测 Spring Cloud Netflix Ribbon 暂时没有提供。

5.1.1 复制项目

复制出 labx-02-scn-ribbon-demo02A-consumer 项目,进行修改。

5.1.2 配置文件

通过在配置文件中,添加 {clientName}.ribbon.{key}={value} 配置项,设置指定名字的 Ribbon 客户端的指定属性。如此,我们就可以实现 Ribbon 客户端级别的自定义配置。

修改 application.yaml 配置文件,额外添加如下配置:

demo-provider:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则全类名
  • 通过 demo-provider.ribbon.NFLoadBalancerRuleClassName 配置项,设置名字为 user-provider 的 Ribbon 客户端的负载均衡规则为随机负载均衡规则 RandomRule。

更多 Ribbon 配置项的 KEY,可以查看 CommonClientConfigKey 类。

5.1.3 简单测试

① 通过「3.1 搭建服务提供者」小节的 DemoProviderApplication 启动 2 次,启动服务提供者的 2 个实例。

② 通过 DemoConsumerApplication 启动服务消费者。

访问服务消费者http://127.0.0.1:28080/hello02?name=yudaoyuanma 接口 10 次,返回结果如下:

consumer:10239-provider:123
consumer:10239-provider:123
consumer:14648-provider:123
consumer:14648-provider:123
consumer:10239-provider:123
consumer:14648-provider:123
consumer:14648-provider:123
consumer:14648-provider:123
consumer:14648-provider:123

从返回的结果看来,是有点随机负载均衡规则的味道。如果胖友想要进一步验证,可以在 RandomRule 类上添加断点,进行调试。

5.2 Spring JavaConfig 方式

示例代码对应仓库:

本小节,我们使用 Spring JavaConfig 的方式,实现 Ribbon 全局客户端两种级别的自定义配置。

5.2.1 复制项目

复制出 labx-02-sca-nacos-discovery-demo02B-consumer 项目,进行修改。最终项目如下图所示:项目结构

5.2.2 RibbonConfiguration

创建 RibbonConfigurationDefaultRibbonClientConfigurationUserProviderRibbonClientConfiguration 三个配置类,实现 Ribbon 自定义配置。代码如下:

// RibbonConfiguration.java
@Configuration
@RibbonClients(
value = {
@RibbonClient(name = "demo-provider", configuration = UserProviderRibbonClientConfiguration.class) // 客户端级别的配置
},
defaultConfiguration = DefaultRibbonClientConfiguration.class // 全局配置
)
public class RibbonConfiguration {
}

// DefaultRibbonClientConfiguration.java
@Configuration
public class DefaultRibbonClientConfiguration {

@Bean
public IRule ribbonDefaultRule() {
return new RoundRobinRule();
}

}

// UserProviderRibbonClientConfiguration.java
@Configuration
public class UserProviderRibbonClientConfiguration {

@Bean
@Primary
public IRule ribbonCustomRule() {
return new RandomRule();
}

}

① 对于 DefaultRibbonClientConfiguration 和 UserProviderRibbonClientConfiguration 两个配置类,我们并没有和 DemoConsumerApplication 启动类放在一个包路径下。

因为,Spring Boot 项目默认扫描 DemoConsumerApplication 所在包以及子包下的所有 Bean 们。而 @Configuration 注解也是一种 Bean,也会被扫描到。

如果将 DefaultRibbonClientConfiguration 和 UserProviderRibbonClientConfiguration 放在 DemoConsumerApplication 所在包或子包中,将会被 Spring Boot 所扫描到,导致整个项目的 Ribbon 客户端都使用相同的 Ribbon 配置,就无法到达 Ribbon 客户端级别的自定义配置的目的

因此,这里在根路径下又创建了 ribbon 包,并将 DefaultRibbonClientConfiguration、UserProviderRibbonClientConfiguration 放入其中,避免被 Spring Boot 所扫描到。

@RibbonClients 注解,通过 defaultConfiguration 属性声明 Ribbon 全局级别的自定义配置,通过 value 属性声明多个 Ribbon 客户端级别的自定义配置。

具体的自定义配置,通过创建 Spring 配置类,例如说 DefaultRibbonClientConfiguration 和 UserProviderRibbonClientConfiguration。

@RibbonClient 注解,声明一个 Ribbon 客户端级别的自定义配置,其中 name 属性用于设置 Ribbon 客户端的名字

为了避免多个 Ribbon 客户端级别的配置类创建的 Bean 之间互相冲突,Spring Cloud Netflix Ribbon 通过 SpringClientFactory 类,为每一个 Ribbon 客户端创建一个 Spring 子上下文

不过这里要注意,因为 DefaultRibbonClientConfiguration 和 UserProviderRibbonClientConfiguration 都创建了 IRule Bean,而 DefaultRibbonClientConfiguration 是在 Spring 父上下文生效,会和 UserProviderRibbonClientConfiguration 所在的 Spring 子上下文共享。这样就导致从 Spring 获取 IRule Bean 时,存在两个而不知道选择哪一个。因此,我们声明 UserProviderRibbonClientConfiguration 创建的 IRule Bean 为 @Primary,优先使用它。

友情提示:这里会有一点点绕,胖友好好理解哈~

5.2.3 简单测试

① 通过「3.1 搭建服务提供者」小节的 DemoProviderApplication 启动 2 次,启动服务提供者的 2 个实例。

② 通过 DemoConsumerApplication 启动服务消费者。

访问服务消费者http://127.0.0.1:28080/hello02?name=yudaoyuanma 接口 10 次,返回结果如下:

consumer:14648-provider:123
consumer:10239-provider:123
consumer:10239-provider:123
consumer:14648-provider:123
consumer:10239-provider:123
consumer:10239-provider:123
consumer:14648-provider:123
consumer:10239-provider:123
consumer:10239-provider:123
consumer:10239-provider:123

从返回的结果看来,是有点随机负载均衡规则的味道。如果胖友想要进一步验证,可以在 RandomRule 类上添加断点,进行调试。

如果胖友想测试全局级别的自定配置的效果,可以将 RibbonConfiguration 配置类上的 @RibbonClients 直接去掉。

5.3 实践建议

  • 对于 Ribbon 客户端级别的自定义配置,推荐使用配置文件的方式,简单方便好管理。在配置文件的方式无法满足的情况下,使用 Spring JavaConfig 的方式作为补充。不过绝大多数场景下,都基本不需要哈~
  • 对于 Ribbon 全局级别的自定义配置,暂时只能使用 Spring JavaConfig 的方式
  • 配置文件方式的优先级高于 Spring JavaConfig 方式,客户端级别的优先级高于全局级别

6. Nacos 自定义负载均衡规则

示例代码对应仓库:

「4. 负载均衡规则」小节中,我们看到 Ribbon 内置提供的 7 种负载均衡规则。如果我们想要自定义 Ribbon 负载均衡规则,可以通过实现 IRule 接口或者继承 AbstractLoadBalancerRule 来实现。

Spring Cloud Alibaba Nacos Discovery 组件,在和 Ribbon 集成时,提供了自定义负载均衡规则 NacosRule。规则如下:

  • 第一步,获得健康的方服务实例列表
  • 第二步,优先选择相同 Nacos 集群的服务实例列表,保证高性能。如果选择不到,则允许使用其它 Nacos 集群的服务实例列表,保证高可用
  • 第三步,从服务实例列表按照权重进行随机,选择一个服务实例返回

NacosRule 源码如下:

public class NacosRule extends AbstractLoadBalancerRule { // 继承 AbstractLoadBalancerRule 抽象类

private static final Logger LOGGER = LoggerFactory.getLogger(NacosRule.class);

@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;

@Override
public Server choose(Object key) {
try {
String clusterName = this.nacosDiscoveryProperties.getClusterName();
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
String name = loadBalancer.getName();

// 第一步,获得健康的方服务实例列表
NamingService namingService = nacosDiscoveryProperties
.namingServiceInstance();
List<Instance> instances = namingService.selectInstances(name, true);
if (CollectionUtils.isEmpty(instances)) {
LOGGER.warn("no instance in service {}", name);
return null;
}

// 第二步,优先获得相同集群的服务实例
List<Instance> instancesToChoose = instances;
if (StringUtils.isNotBlank(clusterName)) {
// 优先选择相同 Nacos 集群的服务实例列表,保证高性能
List<Instance> sameClusterInstances = instances.stream()
.filter(instance -> Objects.equals(clusterName,
instance.getClusterName()))
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(sameClusterInstances)) {
instancesToChoose = sameClusterInstances;
// 如果选择不到,则允许使用其它 Nacos 集群的服务实例列表,保证高可用
} else {
LOGGER.warn("A cross-cluster call occurs,name = {}, clusterName = {}, instance = {}", name, clusterName, instances);
}
}

// 第三步,从服务实例列表按照权重进行随机,选择一个服务实例返回
Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose);

// 返回
return new NacosServer(instance);
} catch (Exception e) {
LOGGER.warn("NacosRule error", e);
return null;
}
}

@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}

}
  • 胖友可以根据艿艿的注释,自己理解一下噢。

拓展小知识:如果我们想要实现类似 Dubbo 多版本 的功能,应该怎么实现呢?步骤如下:

  • 首先,通过 Nacos 元数据,在服务注册到 Nacos 时,将服务的版本号一起带上。
  • 然后,自定义 Ribbon 负载均衡规则 VersionRule,实现基于服务版本号来筛选服务实例。
  • 最后,使用 Ribbon 客户端级别的自定义配置,设置每个服务的负载均衡规则为 VersionRule,并配置调用的服务的版本号。

下面,让我们来搭建下 NacosRule 的使用示例。

6.1 复制项目

「3.2 搭建服务消费者」小节的 labx-02-scn-ribbon-demo01-consumer 项目,复制出 labx-02-scn-ribbon-demo03-consumer 项目。然后在其上进行修改,方便搭建~

6.2 配置文件

修改 application.yaml 配置文件,额外添加如下配置:

demo-provider:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
  • 通过 demo-provider.ribbon.NFLoadBalancerRuleClassName 配置项,设置名字为 user-provider 的 Ribbon 客户端的负载均衡规则为 Nacos 自定义负载均衡规则 NacosRule。

6.3 简单测试

① 通过「3.1 搭建服务提供者」小节的 DemoProviderApplication 启动 2 次,启动服务提供者的 2 个实例。

② 在 Nacos 控制台中,修改服务的某个实例的权重为 0。如下图所示:Nacos 控制台

因为在服务实例的权重为 0 时,负载均衡规则 NacosRule 在随机实例时,是不会选择该实例的。

友情提示:通过配置文件中的 spring.cloud.nacos.discovery.weight 配置项,也可以实现修改服务实例的权重。

③ 通过 DemoConsumerApplication 启动服务消费者。

访问服务消费者http://127.0.0.1:28080/hello02?name=yudaoyuanma 接口 10 次,返回结果如下:

consumer:19270-provider:123
consumer:19270-provider:123
consumer:19270-provider:123
consumer:19270-provider:123
consumer:19270-provider:123
consumer:19270-provider:123
consumer:19270-provider:123
consumer:19270-provider:123
consumer:19270-provider:123
consumer:19270-provider:123

从返回的结果看来,是有点 NacosRule 的味道。如果胖友想要进一步验证,可以在 NacosRule 类上添加断点,进行调试。

7. 饥饿加载

示例代码对应仓库:

默认配置下,Ribbon 客户端是在首次请求服务时,才创建该服务的对应的 Ribbon 客户端。

好处是项目在启动的时候,能够更加快速,因为 Ribbon 客户端创建时,需要从注册中心获取服务的实例列表,需要有网络请求的消耗。

坏处是首次请求服务时,因为需要 Ribbon 客户端的创建,会导致请求比较慢,严重情况下会导致请求超时。

因此,Spring Cloud Netflix Ribbon 提供了 ribbon.eager-load 配置项,允许我们在项目启动时,提前创建 Ribbon 客户端。翻译成中文就是“饥饿加载”

下面,让我们来搭建下饥饿加载的使用示例。

7.1 复制项目

「3.2 搭建服务消费者」小节的 labx-02-scn-ribbon-demo01-consumer 项目,复制出 labx-02-scn-ribbon-demo04-consumer 项目。然后在其上进行修改,方便搭建~

7.2 配置文件

修改 application.yaml 配置文件,额外添加如下配置:

ribbon:
# Ribbon 饥饿加载配置项,对应 RibbonEagerLoadProperties 配置类
eager-load:
enabled: true # 是否开启饥饿加载。默认为 false 不开启
clients: user-provider # 开启饥饿加载的 Ribbon 客户端名字。如果有多个,使用 , 逗号分隔。

每个配置项目的说明,胖友自己看下注释噢。

7.3 简单测试

① 通过「3.1 搭建服务提供者」小节的 DemoProviderApplication 启动 2 次,启动服务提供者的 2 个实例。

② 通过 DemoConsumerApplication 启动服务消费者。可以看到 IDEA 控制台多输出了如下日志:

2020-02-09 11:38:54.833  INFO 40004 --- [           main] c.netflix.config.ChainedDynamicProperty  : Flipping property: user-provider.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-02-09 11:38:54.858 INFO 40004 --- [ main] c.netflix.loadbalancer.BaseLoadBalancer : Client: user-provider instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=user-provider,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2020-02-09 11:38:54.863 INFO 40004 --- [ main] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater
2020-02-09 11:38:54.876 INFO 40004 --- [ main] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client user-provider initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=user-provider,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:com.alibaba.cloud.nacos.ribbon.NacosServerList@6629643d
  • 可以看到服务 demo-provider 对应的 Ribbon 客户端已经初始化完毕。

首次访问服务消费者http://127.0.0.1:28080/hello02?name=yudaoyuanma 接口,是不是感觉变快了。嘿嘿,用心感受~

7.4 小结

  • 在本地开发环境时,可能会频繁重启项目,为了项目启动更快,可以考虑关闭 Ribbon 饥饿加载。
  • 在生产环境下,一定要开启 Ribbon 饥饿加载。

8. HTTP 客户端

示例代码对应仓库:

在上述的示例中,Ribbon 只负责服务实例的选择,提供负载均衡的功能,而服务的 HTTP 调用则是交给 RestTemplate 来完成。实际上,Ribbon 也是提供 HTTP 调用功能的。

在 Spring Cloud Netflix Ribbon 中,提供了 3 种 HTTP 客户端。配置方式如下:

#ribbon:
# okhttp:
# enabled: true # 设置使用 OkHttp,对应 OkHttpRibbonConfiguration 配置类
# restclient:
# enabled: true # 设置使用 Jersey Client,对应 RestClientRibbonConfiguration 配置类
# httpclient:
# enabled: true # 设置使用 Apache HttpClient,对应 HttpClientRibbonConfiguration 配置类

整理表格如下:

配置项 HTTP 组件 配置类
ribbon.okhttp.enable OkHttp OkHttpRibbonConfiguration
ribbon.restclient.enable Jersey Client RestClientRibbonConfiguration
ribbon.httpclient.enable Apache HttpClient HttpClientRibbonConfiguration

下面,让我们来搭建下使用 Jersey Client 的使用示例。

友情提示:配置 ribbon.okhttp.enable = trueribbon.httpclient.enable = true 的两种情况,一直没有生效,具体原因还在寻找~

因此,这里我们搭建略微冷门的 Jersey Client 的使用示例。

8.1 复制项目

「3.2 搭建服务消费者」小节的 labx-02-scn-ribbon-demo01-consumer 项目,复制出 labx-02-scn-ribbon-demo05-consumer 项目。然后在其上进行修改,方便搭建~

8.2 配置文件

修改 application.yaml 配置文件,额外添加如下配置:

ribbon:
# okhttp:
# enabled: true # 设置使用 OkHttp,对应 OkHttpRibbonConfiguration 配置类
restclient:
enabled: true # 设置使用 Jersey Client,对应 RestClientRibbonConfiguration 配置类
# httpclient:
# enabled: true # 设置使用 Apache HttpClient,对应 HttpClientRibbonConfiguration 配置类

8.3 简单测试

① 通过「3.1 搭建服务提供者」小节的 DemoProviderApplication 启动,启动服务提供者的实例。

② 通过 DemoConsumerApplication 启动服务消费者。

在 RestClient 类的 #execute(...) 方法上,打一个断点,用于验证我们配置成功。如下图所示:RestClient 断点

访问服务消费者http://127.0.0.1:28080/hello02?name=yudaoyuanma 接口,成功进入 RestClient 的断点,说明配置成功。

9. 请求重试

示例代码对应仓库:

一般情况下,我们在 HTTP 请求远程服务时,都能够正常返回。但是极端情况下,可能会存在请求失败的情况下,例如说:

  • 请求的服务执行逻辑过久,导致超过请求的等待时间
  • 请求的服务异常挂掉了,未从注册中心中移除,导致服务消费者还是会调用该服务
  • 网络一个抖动,导致请求失败

此时,我们通过重试请求到当前服务实例或者其它服务实例,以获得请求的结果,实现更高的可用性。

在 Spring Cloud 中,提供 spring.cloud.loadbalancer.retry 配置项,通过设置为 true,开启负载均衡的重试功能。

下面,让我们来搭建下请求重试的使用示例。

9.1 复制项目

「3.2 搭建服务消费者」小节的 labx-02-scn-ribbon-demo01-consumer) 项目,复制出 labx-02-scn-ribbon-demo06-consumer 项目。然后在其上进行修改,方便搭建~

9.2 配置文件

修改 application.yaml 配置文件,额外添加如下配置:

ribbon:
restclient:
enabled: true # 设置使用 Jersey Client,对应 RestClientRibbonConfiguration 配置类

demo-provider:
ribbon:
ConnectTimeout: 1000 # 请求的连接超时时间,单位:毫秒。默认为 1000
ReadTimeout: 1 # 请求的读取超时时间,单位:毫秒。默认为 1000
OkToRetryOnAllOperations: true # 是否对所有操作都进行重试,默认为 false。
MaxAutoRetries: 0 # 对当前服务的重试次数,默认为 0 次。
MaxAutoRetriesNextServer: 1 # 重新选择服务实例的次数,默认为 1 次。注意,不包含第 1 次哈。

① 设置 ribbon.restclient.enable 配置项为 true,因为我们通过 Ribbon 实现请求重试,所以需要使用 Ribbon 内置的 HTTP 客户端进行请求服务。

ConnectTimeoutReadTimeout 两个配置项,用于设置请求的连接和超时时间。

这里,我们设置 ReadTimeout 配置项为 1,用于模拟请求服务超时,从而演示请求重试的效果。

OkToRetryOnAllOperationsMaxAutoRetriesMaxAutoRetriesNextServer 三个配置项,设置请求重试相关的参数。具体每个参数的解释,看下配置文件中的注释哈。

MaxAutoRetriesMaxAutoRetriesNextServer 两个配置项可能略微难以理解,艿艿再简单描述下。

  • 第一步,在使用 Ribbon 选择一个服务实例后,如果请求失败,重试 MaxAutoRetries 请求直到成功。
  • 第二步,如果经过 1 + MaxAutoRetries 次,请求一个服务实例还是失败,重新使用 Ribbon 选择一个新的服务实例,重复第一步的过程。最多可以重新选择 MaxAutoRetriesNextServer 次新的服务实例。

也就是说,在服务实例足够的情况下,最多会发起 (1 + MaxAutoRetries) * (1 + MaxAutoRetriesNextServer) 请求。不过一般情况下,推荐设置 MaxAutoRetries 为 0,不重试当前实例。


在 ② 和 ③ 中的参数,如果想要全局级别的配置,可以添加到 ribbon 配置项下。例如说:

ribbon:
ConnectTimeout: 1000 # 请求的连接超时时间,单位:毫秒。默认为 1000
ReadTimeout: 1 # 请求的读取超时时间,单位:毫秒。默认为 1000
OkToRetryOnAllOperations: true # 是否对所有操作都进行重试,默认为 false。
MaxAutoRetries: 0 # 对当前服务的重试次数,默认为 0 次。
MaxAutoRetriesNextServer: 1 # 重新选择服务实例的次数,默认为 1 次。注意,不包含第 1 次哈。

9.3 简单测试

① 通过「3.1 搭建服务提供者」小节的 DemoProviderApplication 启动 2 次,启动服务提供者的 2 个实例。

② 通过 DemoConsumerApplication 启动服务消费者。

访问服务消费者http://127.0.0.1:28080/hello02?name=yudaoyuanma 接口,返回结果如下:

I/O error on GET request for "http://demo-provider/echo": com.netflix.client.ClientException: Number of retries on next server exceeded max 1 retries, while making a call for: 10.171.1.115:18229; nested exception is java.io.IOException: com.netflix.client.ClientException: Number of retries on next server exceeded max 1 retries, while making a call for: 10.171.1.115:18229
  • 符合预期,多次重试后依然失败。

打开 DemoProviderApplication 控制台,可以看到两个服务都被调用的示例:

# demo-provider 实例 1
2020-02-09 17:56:02.517 INFO 53096 --- [io-19452-exec-1] p.DemoProviderApplication$TestController : [echo][被调用啦 name(123)]

# demo-provider 实例 2
2020-02-09 17:56:02.550 INFO 53102 --- [io-18229-exec-1] p.DemoProviderApplication$TestController : [echo][被调用啦 name(123)]

如果胖友对 MaxAutoRetriesMaxAutoRetriesNextServer 两个配置项还是有点迷糊,可以多做做测试哈~

10. Ribbon 主要组件

绝大多数情况下,我们并不需要去深入了解 Ribbon 的主要组件。所以本小节的内容,更多作为拓展知识,胖友可以根据自己的需要进行阅读。

Spring Cloud Netflix Ribbon 提供的 RibbonClientConfiguration 自动配置类,默认创建了 Ribbon 的主要组件的 Bean。整理成如下表格:

FROM 《Spring Cloud Netflix 官方文档 —— Customizing the Ribbon Client》

Bean Type Bean Name Class Name
IClientConfig ribbonClientConfig DefaultClientConfigImpl
IRule ribbonRule ZoneAvoidanceRule
IPing ribbonPing DummyPing
ServerList ribbonServerList ConfigurationBasedServerList
ServerListFilter ribbonServerListFilter ZonePreferenceServerListFilter
ServerListUpdater ribbonServerListUpdater PollingServerListUpdater
ILoadBalancer ribbonLoadBalancer ZoneAwareLoadBalancer

10.1 IClientConfig

IClientConfig 接口,定义用于初始化 Ribbon 客户端和负载均衡器的配置的方法。简单来说,它就是负责读取 Ribbon 配置的。

IClientConfig 只有一个实现类 DefaultClientConfigImpl。在该类中,我们可以看到 Ribbon 配置项的默认值。

10.2 IRule

IRule 接口,负载均衡规则接口,定义了从服务中选择一个实例的方法。

Ribbon 提供了 7 个 IRule 实现类,在「5. 自定义 Ribbon 配置」小节中已经看到。

可以通过实现 IRule 接口,实现自定义负载均衡规则,可见「6. Nacos 自定义负载均衡规则」小节。

10.3 IPing

IPing 接口,定义如何 “ping” 服务实例的方法,判断其是否存活。

Ribbon 提供了 4 个 IPing 实现类,如下图所示:IPing 类图

  • DummyPing:默认实现类,永远返回 true,即认为服务实例永远存活。
  • NoOpPing:不对服务实例进行“ping”,永远返回 true,即认为服务实例永远存活。
  • PingUrl:使用 Apache HttpClient 请求目标服务实例,通过判断响应状态码和状态结果,判断服务实例是否存活。
  • NIWSDiscoveryPing:不执行真实的“ping”,而是通过 Eureka 注册中心获得服务实例的状态是否为 UP,从而判断服务实例是否存活。

10.5 ServerList

ServerList 接口,定义获得服务实例列表的方法。

ServerList 分成静态和动态的。如果是动态的,会有后台定时任务定时重新获取最新的服务实例列表。

Ribbon 提供了 4 个 ServerList 实现类,如下图所示:ServerList 类图

Nacos 提供了自定义的 NacosServerList 实现类,从 Nacos 注册中心获取服务实例列表。

10.6 ServerListFilter

ServerListFilter 接口,定义过滤不符合条件的服务实例的方法。

Ribbon 提供了 4 个 ServerListFilter 实现类,如下图所示:ServerListFilter 类图

  • ZoneAffinityServerListFilter:过滤掉和当前客户端不同区域(Zone)的服务实例列表。如果过滤后结果为空,则不进行过滤,返回原服务实例列表。
  • ZonePreferenceServerListFilter:在 ZoneAffinityServerListFilter 的基础之上,允许手动设置区域(Zone),并使用该区域(Zone)进行过滤。如果过滤后结果为空,则不进行过滤,返回原服务实例列表。
  • DefaultNIWSServerListFilter:是 ZoneAffinityServerListFilter 的子类,无任何代码,也就是说和 ZoneAffinityServerListFilter 是等价的。
  • ServerListSubsetFilter:筛选返回固定数量的服务实例列表。主要用于服务提供者数量过大,避免在连接池中保存过多的服务实例的连接。

    This is useful if the server farm is large (e.g., in the hundreds) and making use of every one of them and keeping the connections in http client’s connection pool is unnecessary. It also has the capability of eviction of relatively unhealthy servers by comparing the total network failures and concurrent connections.

10.7 ServerListUpdater

ServerListUpdater 接口,ServerList 更新器,用于 DynamicServerListLoadBalancer 动态更新服务实例列表。

Ribbon 提供了 2 个 ServerListUpdater 实现类,如下图所示:ServerListUpdater 类图

  • PollingServerListUpdater:启动一个定时任务,定时重新获取最新的服务实例列表。
  • EurekaNotificationServerListUpdater:监听 Eureka 事件,在收到 Eureka Client 本地服务实例列表的缓存刷新事件 CacheRefreshedEvent 时,通知 DynamicServerListLoadBalancer 更新方服务实例列表。

10.7 ILoadBalancer

ILoadBalancer 接口,Ribbon 作为负载均衡器的入口,定义了服务器实例的增删查和选择方法。

Ribbon 提供了 3 个 ILoadBalancer 实现类,如下图所示:ILoadBalancer 类图

  • BaseLoadBalancer:基础的 ILoadBalancer 实现类,支持手动进行服务器实例的增删查,支持使用 IRule 进行服务器实例的选择,支持使用 IPing 进行服务器实例的存活判断。
  • DynamicServerListLoadBalancer动态的 ILoadBalancer 实现类,支持使用 ServerList + ServerListFilter + ServerListUpdater 进行服务器实例的动态的增删查。
  • ZoneAwareLoadBalancer:在 DynamicServerListLoadBalancer 的基础之上,在选择服务器实例的时候,增加区域(Zone)的考虑。

666. 彩蛋

至此,我们已经完成 Spring Cloud Netflix Ribbon 的学习。如下是 Ribbon 相关的官方文档:

后续,胖友可以继续阅读* 《芋道 Spring Cloud 声明式调用 Feign 入门》文章。

文章目录
  1. 1. 1. 概述
  2. 2. 2. 负载均衡的实现方式
  3. 3. 3. 快速入门
    1. 3.1. 3.1 搭建服务提供者
      1. 3.1.1. 3.1.1 引入依赖
      2. 3.1.2. 3.1.2 配置文件
      3. 3.1.3. 3.1.3 DemoProviderApplication
    2. 3.2. 3.2 搭建服务消费者
      1. 3.2.1. 3.2.1 引入依赖
      2. 3.2.2. 3.2.2 配置文件
      3. 3.2.3. 3.2.3 DemoConsumerApplication
    3. 3.3. 3.3 简单测试
  4. 4. 4. 负载均衡规则
  5. 5. 5. 自定义 Ribbon 配置
    1. 5.1. 5.1 配置文件方式
      1. 5.1.1. 5.1.1 复制项目
      2. 5.1.2. 5.1.2 配置文件
      3. 5.1.3. 5.1.3 简单测试
    2. 5.2. 5.2 Spring JavaConfig 方式
      1. 5.2.1. 5.2.1 复制项目
      2. 5.2.2. 5.2.2 RibbonConfiguration
      3. 5.2.3. 5.2.3 简单测试
    3. 5.3. 5.3 实践建议
  6. 6. 6. Nacos 自定义负载均衡规则
    1. 6.1. 6.1 复制项目
    2. 6.2. 6.2 配置文件
    3. 6.3. 6.3 简单测试
  7. 7. 7. 饥饿加载
    1. 7.1. 7.1 复制项目
    2. 7.2. 7.2 配置文件
    3. 7.3. 7.3 简单测试
    4. 7.4. 7.4 小结
  8. 8. 8. HTTP 客户端
    1. 8.1. 8.1 复制项目
    2. 8.2. 8.2 配置文件
    3. 8.3. 8.3 简单测试
  9. 9. 9. 请求重试
    1. 9.1. 9.1 复制项目
    2. 9.2. 9.2 配置文件
    3. 9.3. 9.3 简单测试
  10. 10. 10. Ribbon 主要组件
    1. 10.1. 10.1 IClientConfig
    2. 10.2. 10.2 IRule
    3. 10.3. 10.3 IPing
    4. 10.4. 10.5 ServerList
    5. 10.5. 10.6 ServerListFilter
    6. 10.6. 10.7 ServerListUpdater
    7. 10.7. 10.7 ILoadBalancer
  11. 11. 666. 彩蛋