Springcloud基础知识(4)- Spring Cloud Ribbon | 负载均衡与服务调用



Spring Cloud Ribbon 是一套基于 Netflix Ribbon 实现的客户端负载均衡和服务调用工具。

Netflix Ribbon 是 Netflix 公司发布的开源组件,其主要功能是提供客户端的负载均衡算法和服务调用。Spring Cloud 将其与 Netflix 中的其他开源服务组件(例如 Eureka、Feign 以及 Hystrix 等)一起整合进 Spring Cloud Netflix 模块中,整合后全称为 Spring Cloud Netflix Ribbon。

Ribbon 是 Spring Cloud Netflix 模块的子模块,它是 Spring Cloud 对 Netflix Ribbon 的二次封装。通过它,我们可以将面向服务的 REST 模板(RestTemplate)请求转换为客户端负载均衡的服务调用。

Ribbon 是 Spring Cloud 体系中最核心、最重要的组件之一。它虽然只是一个工具类型的框架,并不像 Eureka Server(服务注册中心)那样需要独立部署,但它几乎存在于每一个使用 Spring Cloud 构建的微服务中。

Spring Cloud 微服务之间的调用,API 网关的请求转发等内容,实际上都是通过 Spring Cloud Ribbon 来实现的,包括后续我们要介绍的 OpenFeign 也是基于它实现的。


1. 负载均衡

    在任何一个系统中,负载均衡都是一个十分重要且不得不去实施的内容,它是系统处理高并发、缓解网络压力和服务端扩容的重要手段之一。

    负载均衡(Load Balance) ,简单点说就是将用户的请求平摊分配到多个服务器上运行,以达到扩展服务器带宽、增强数据处理能力、增加吞吐量、提高网络的可用性和灵活性的目的。

    常见的负载均衡方式有两种:服务端负载均衡、客户端负载均衡

    1) 服务端负载均衡

        服务端负载均衡是在客户端和服务端之间建立一个独立的负载均衡服务器,该服务器既可以是硬件设备(例如 F5),也可以是软件(例如 Nginx)。这个负载均衡服务器维护了一份可用服务端清单,然后通过心跳机制来删除故障的服务端节点,以保证清单中的所有服务节点都是可以正常访问的。

        当客户端发送请求时,该请求不会直接发送到服务端进行处理,而是全部交给负载均衡服务器,由负载均衡服务器按照某种算法(例如轮询、随机等),从其维护的可用服务清单中选择一个服务端,然后进行转发。

        服务端负载均衡具有以下特点:
        
            (1) 需要建立一个独立的负载均衡服务器。
            (2) 负载均衡是在客户端发送请求后进行的,因此客户端并不知道到底是哪个服务端提供的服务。
            (3) 可用服务端清单存储在负载均衡服务器上。

    2) 客户端负载均衡

        相较于服务端负载均衡,客户端服务在均衡则是一个比较小众的概念。

        客户端负载均衡是将负载均衡逻辑以代码的形式封装到客户端上,即负载均衡器位于客户端。客户端通过服务注册中心(例如 Eureka Server)获取到一份服务端提供的可用服务清单。有了服务清单后,负载均衡器会在客户端发送请求前通过负载均衡算法选择一个服务端实例再进行访问,以达到负载均衡的目的;

        客户端负载均衡也需要心跳机制去维护服务端清单的有效性,这个过程需要配合服务注册中心一起完成。

        客户端负载均衡具有以下特点:

            (1) 负载均衡器位于客户端,不需要单独搭建一个负载均衡服务器。
            (2) 负载均衡是在客户端发送请求前进行的,因此客户端清楚地知道是哪个服务端提供的服务。
            (3) 客户端都维护了一份可用服务清单,而这份清单都是从服务注册中心获取的。

        Ribbon 就是一个基于 HTTP 和 TCP 的客户端负载均衡器,当我们将 Ribbon 和 Eureka 一起使用时,Ribbon 会从 Eureka Server(服务注册中心)中获取服务端列表,然后通过负载均衡策略将请求分摊给多个服务提供者,从而达到负载均衡的目的。
        
    3) 服务端负载均衡 VS 客户端负载均衡

        下面我们就来对比下,服务端负载均衡和客户端负载均衡到底有什么区别,如下表。

不同点 服务端负载均衡 客户端负载均衡
是否需要建立负载均衡服务器 需要在客户端和服务端之间建立一个独立的负载均衡服务器。 将负载均衡的逻辑以代码的形式封装到客户端上,因此不需要单独建立负载均衡服务器。
是否需要服务注册中心 不需要服务注册中心。 需要服务注册中心。在客户端负载均衡中,所有的客户端和服务端都需要将其提供的服务注册到服务注册中心上。
可用服务清单存储的位置 可用服务清单存储在位于客户端与服务器之间的负载均衡服务器上。 所有的客户端都维护了一份可用服务清单,这些清单都是从服务注册中心获取的。
负载均衡的时机 先将请求发送到负载均衡服务器,然后由负载均衡服务器通过负载均衡算法,在多个服务端之间选择一个进行访问;即在服务器端再进行负载均衡算法分配。简单点说就是,先发送请求,再进行负载均衡。 在发送请求前,由位于客户端的服务负载均衡器(例如 Ribbon)通过负载均衡算法选择一个服务器,然后进行访问。简单点说就是,先进行负载均衡,再发送请求。
客户端是否了解服务提供方信息 由于负载均衡是在客户端发送请求后进行的,因此客户端并不知道到底是哪个服务端提供的服务。 负载均衡是在客户端发送请求前进行的,因此客户端清楚的知道是哪个服务端提供的服务。

2. Ribbon 实现服务调用

    Ribbon 可以与 RestTemplate 配合使用,以实现微服务之间的调用。如何使用 RestTemplate 请参考 “ ”。

    本文将在 “ ” 里 SpringcloudDemo02 项目基础上,完全复制 SpringcloudDemo02 的代码和配置到新项目 SpringcloudDemo03,修改 SpringcloudDemo03 下的所有子模块的 Parent 为 SpringcloudDemo03。

在 SpringcloudDemo03 项目中添加一个子 ConsumerRibbon 模块,来演示使用 Ribbon 实现服务调用。

    1) 调整 Spring Boot 版本

        修改 SpringcloudDemo03 主项目的 pom.xml,内容如下:

 1             <?xml version="1.0" encoding="UTF-8"?>
 2             <project xmlns="http://maven.apache.org/POM/4.0.0"
 3                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4                     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
 5                                         http://maven.apache.org/xsd/maven-4.0.0.xsd">
 6                 <parent>
 7                     <groupId>org.springframework.bootgroupId>
 8                     <artifactId>spring-boot-starter-parentartifactId>
 9                     
10                     <version>2.3.12.RELEASEversion>
11                     <relativePath/> 
12                 parent>
13                 <modelVersion>4.0.0modelVersion>
14                 <packaging>pompackaging>
15 
16                 ...
17 
18                 <dependencyManagement>
19                     <dependencies>
20                         
23                         <dependency>
24                             <groupId>org.springframework.cloudgroupId>
25                             <artifactId>spring-cloud-dependenciesartifactId>
26                             <version>Hoxton.SR12version> 
27                              
28                             <type>pomtype>
29                             <scope>importscope>
30                         dependency>
31                     dependencies>
32                 dependencyManagement>
33 
34                 ...
35 
36             project>

        注:Spring Boot 2.6.6 下 Ribbon 依赖存在兼容性问题,把项目 Spring Boot 的版本改成 2.3.12.RELEASE,同时把 Spring Cloud 版本调整到对应的 Hoxton.SR12 版。


    2) 创建 ConsumerRibbon 模块

        选择左上的项目列表中的 SpringcloudDemo03,点击鼠标右键,选择 New -> Module 进入 New Module 页面:

            Maven -> Project SDK: 1.8 -> Check "Create from archtype" -> select "org.apache.maven.archtypes:maven-archtype-quickstart" -> Next

                Name: ConsumerRibbon
                GroupId: com.example
                ArtifactId: ConsumerRibbon

            -> Finish

        注:模块 ConsumerRibbon创建后,Maven 命令会自动修改主项目 SpringcloudDemo03 的 pom.xml,添加如下内容:

           
                ...

                ConsumerRibbon
           


    3) 修改 ConsumerRibbon 的 pom.xml 内容如下

 1         <?xml version="1.0" encoding="UTF-8"?>
 2         <project xmlns="http://maven.apache.org/POM/4.0.0"
 3                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4                 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
 5                                     http://maven.apache.org/xsd/maven-4.0.0.xsd">
 6             <parent>
 7                 <artifactId>SpringcloudDemo03artifactId>
 8                 <groupId>com.examplegroupId>
 9                 <version>1.0-SNAPSHOTversion>
10             parent>
11             <modelVersion>4.0.0modelVersion>
12 
13             <artifactId>ConsumerRibbonartifactId>
14 
15             <name>ConsumerRibbonname>
16             
17             <url>http://www.example.comurl>
18 
19             <properties>
20                 <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
21                 <maven.compiler.source>1.8maven.compiler.source>
22                 <maven.compiler.target>1.8maven.compiler.target>
23             properties>
24 
25             <dependencies>
26                 <dependency>
27                     <groupId>junitgroupId>
28                     <artifactId>junitartifactId>
29                     <version>4.12version>
30                     <scope>testscope>
31                 dependency>
32                 <dependency>
33                     <groupId>org.springframework.bootgroupId>
34                     <artifactId>spring-boot-starter-webartifactId>
35                 dependency>
36                 <dependency>
37                     <groupId>org.springframework.bootgroupId>
38                     <artifactId>spring-boot-starter-testartifactId>
39                     <scope>testscope>
40                 dependency>
41                 
42                 <dependency>
43                     <groupId>com.examplegroupId>
44                     <artifactId>CommonAPIartifactId>
45                     <version>${project.version}version>
46                 dependency>
47                 <dependency>
48                     <groupId>org.projectlombokgroupId>
49                     <artifactId>lombokartifactId>
50                     <version>1.18.8version>
51                 dependency>
52                 <dependency>
53                     <groupId>org.springframework.cloudgroupId>
54                     <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
55                 dependency>
56                 <dependency>
57                     <groupId>org.springframework.cloudgroupId>
58                     <artifactId>spring-cloud-starter-netflix-ribbonartifactId>
59                 dependency>
60             dependencies>
61 
62             <build>
63                 <plugins>
64                     <plugin>
65                         <groupId>org.springframework.bootgroupId>
66                         <artifactId>spring-boot-maven-pluginartifactId>
67                         <configuration>
68                             <mainClass>com.example.AppmainClass>
69                             <layout>JARlayout>
70                         configuration>
71                         <executions>
72                             <execution>
73                                 <goals>
74                                     <goal>repackagegoal>
75                                 goals>
76                             execution>
77                         executions>
78                     plugin>
79                 plugins>
80             build>
81         project>


    3) 创建 src/main/resources/application.yml 文件

 1         server:
 2             port: 80
 3 
 4         #### Spring Cloud Ribbon 负载均衡配置 ####
 5         eureka:
 6             client:
 7                 register-with-eureka: false # 本微服务为消费者,不需要将自己注册到服务注册中心
 8                 fetch-registry: true  # 本微服务为消费者,需要到服务注册中心搜索服务
 9                 service-url:
10                     defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

    4) 创建 src/main/java/com/example/config/RestTemplateConfig.java 文件

 1         package com.example.config;
 2 
 3         import org.springframework.context.annotation.Bean;
 4         import org.springframework.context.annotation.Configuration;
 5         import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 6         import org.springframework.web.client.RestTemplate;
 7         import org.springframework.cloud.client.loadbalancer.LoadBalanced;
 8 
 9         @Configuration
10         public class RestTemplateConfig {
11 
12             @ConditionalOnMissingBean(RestTemplate.class)
13             @Bean
14             @LoadBalanced   // 在客户端使用 RestTemplate 请求服务端时,开启负载均衡(Ribbon)
15             public RestTemplate restTemplate(){
16                 RestTemplate restTemplate = new RestTemplate();
17                 return restTemplate;
18             }
19         }


    5) 创建 src/main/java/com/example/controller/ConsumerController.java 文件

 1         package com.example.controller;
 2 
 3         import java.util.List;
 4 
 5         import org.springframework.beans.factory.annotation.Autowired;
 6         import org.springframework.web.bind.annotation.PathVariable;
 7         import org.springframework.web.bind.annotation.RequestMapping;
 8         import org.springframework.web.bind.annotation.RestController;
 9         import org.springframework.web.client.RestTemplate;
10         import com.example.entity.Employee;
11 
12         @RestController
13         @RequestMapping(value = "/consumer")
14         public class ConsumerController {
15 
16             // 使用固定地址无法实现负载均衡
17             //private static final String REST_URL_PROVIDER_PREFIX = "http://localhost:8001";
18 
19             // 使用注册到 Spring Cloud Eureka 服务注册中心中的微服务名,即 spring.application.name (需要大写)
20             private static final String REST_URL_PROVIDER_PREFIX = "http://EMPLOYEE-SERVICE-PROVIDER";
21 
22             @Autowired
23             private RestTemplate restTemplate;
24 
25             // 根据 id 获取 Employee
26             @RequestMapping(value = "/employee/get/{id}")
27             public Employee get(@PathVariable("id") Integer id) {
28 
29                 return restTemplate.getForObject(REST_URL_PROVIDER_PREFIX + "/employee/get/3", Employee.class);
30             }
31 
32             // 获取 Employee 列表
33             @RequestMapping(value = "/employee/list")
34             public List list() {
35                 return restTemplate.getForObject(REST_URL_PROVIDER_PREFIX + "/employee/list", List.class);
36             }
37 
38         }


    6) 修改 src/main/java/com/example/App.java 文件

 1         package com.example;
 2 
 3         import org.springframework.boot.SpringApplication;
 4         import org.springframework.boot.autoconfigure.SpringBootApplication;
 5         import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
 6 
 7         @SpringBootApplication
 8         @EnableEurekaClient     // 开启 Eureka client,检索 Eureka Server 注册中心
 9         public class App {
10             public static void main(String[] args) {
11                 SpringApplication.run(App.class, args);
12             }
13         }


    7) 单机版测试

        (1) 启动 server-7001

            鼠标双击 start.bat 文件,跳出 cmd 窗口:

                C:\Applications\Java\eureka\server-7001>java -jar RegisterServer-1.0-SNAPSHOT.jar application.yml  

                    ...

        (2) 启动 service-8001

            鼠标双击 start.bat 文件,跳出 cmd 窗口:

                C:\Applications\Java\eureka\service-8001>java -jar ServiceProvider-1.0-SNAPSHOT.jar application.yml

                    ...

        (3) 启动 ConsumerRibbon 模块

            浏览器访问 http://localhost/consumer/employee/list,显示结果如下:

                [{"id":1,"name":"Test Name2","port":8001},{"id":2,"name":"Test Name2","port":8001}]

            注:"port":8001 是 service-8001 的端口号,在测试多个 Provider 的负载均衡时,端口就可以区分不同的 Provider。


3. Ribbon 实现负载均衡

    Ribbon 是一个客户端的负载均衡器,它可以与 Eureka 配合使用轻松地实现客户端的负载均衡。Ribbon 会先从 Eureka Server(服务注册中心)去获取服务端列表,然后通过负载均衡策略将请求分摊给多个服务端,从而达到负载均衡的目的。

    Spring Cloud Ribbon 提供了一个 IRule 接口,该接口主要用来定义负载均衡策略,它有 7 个默认实现类,每一个实现类都是一种负载均衡策略。

实现类 负载均衡策略
RoundRobinRule 按照线性轮询策略,即按照一定的顺序依次选取服务实例
RandomRule 随机选取一个服务实例
RetryRule 按照 RoundRobinRule(轮询)的策略来获取服务,如果获取的服务实例为 null 或已经失效,则在指定的时间之内不断地进行重试(重试时获取服务的策略还是 RoundRobinRule 中定义的策略),如果超过指定时间依然没获取到服务实例则返回 null 。
WeightedResponseTimeRule WeightedResponseTimeRule 是 RoundRobinRule 的一个子类,它对 RoundRobinRule 的功能进行了扩展。 根据平均响应时间,来计算所有服务实例的权重,响应时间越短的服务实例权重越高,被选中的概率越大。刚启动时,如果统计信息不足,则使用线性轮询策略,等信息足够时,再切换到 WeightedResponseTimeRule。
BestAvailableRule 继承自 ClientConfigEnabledRoundRobinRule。先过滤点故障或失效的服务实例,然后再选择并发量最小的服务实例。
AvailabilityFilteringRule 先过滤掉故障或失效的服务实例,然后再选择并发量较小的服务实例。
ZoneAvoidanceRule 默认的负载均衡策略,综合判断服务所在区域(zone)的性能和服务(server)的可用性,来选择服务实例。在没有区域的环境下,该策略与轮询(RandomRule)策略类似。


    下面我们就来通过一个实例来验证下,Ribbon 默认是使用什么策略选取服务实例的。

    这里在 “ ” 里的集群基础上,测试 Ribbon 实现负载均衡的策略,操作步骤如下:


        依次启动 server-7001、server-7002、server-7003,启动的间隔 5 ~ 10 秒,都启动后等待 10 秒左右。

        再依次启动 service-8001、service-8002、service-8003,启动的间隔 5 ~ 10 秒,都启动后等待 10 秒左右。

        浏览器访问 http://eureka7001.com:7001/,页面上 “Instances currently registered with Eureka” 区域显示:

            Application                          AMIs      Availability Zones      Status
            EMPLOYEE-SERVICE-PROVIDER    n/a (3)        (3)                    UP (3) - sevice-provider-8003 , sevice-provider-8002 , sevice-provider-8001

        启动 ConsumerRibbon 模块

            浏览器访问 http://localhost/consumer/employee/list,显示结果如下:

                [{"id":1,"name":"Test Name1","port":8001},{"id":2,"name":"Test Name2","port":8001}]

            刷新页面:

                [{"id":1,"name":"Test Name1","port":8002},{"id":2,"name":"Test Name2","port":8002}]

            再次刷新页面:

                [{"id":1,"name":"Test Name1","port":8003},{"id":2,"name":"Test Name2","port":8003}]


            注:通过 port 的变化可以看出,Spring Cloud Ribbon 默认使用轮询策略进行负载均衡。


4. 切换负载均衡策略

    Spring Cloud Ribbon 默认使用轮询策略选取服务实例,我们也可以根据自身的需求切换负载均衡策略。

    切换负载均衡策略的方法很简单,只需要在 Consumer 的配置类中,将 IRule 的其他实现类注入到容器中即可。

    示例,修改 src/main/java/com/example/config/RestTemplateConfig.java 文件

 1         package com.example.config;
 2 
 3         import org.springframework.context.annotation.Bean;
 4         import org.springframework.context.annotation.Configuration;
 5         import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 6         import org.springframework.web.client.RestTemplate;
 7         import org.springframework.cloud.client.loadbalancer.LoadBalanced;
 8 
 9         import com.netflix.loadbalancer.IRule;
10         import com.netflix.loadbalancer.RandomRule;
11 
12         @Configuration
13         public class RestTemplateConfig {
14 
15             @ConditionalOnMissingBean(RestTemplate.class)
16             @Bean
17             @LoadBalanced   // 在客户端使用 RestTemplate 请求服务端时,开启负载均衡(Ribbon)
18             public RestTemplate restTemplate(){
19                 RestTemplate restTemplate = new RestTemplate();
20                 return restTemplate;
21             }
22 
23             @Bean
24             public IRule myRule() {
25                 // RandomRule 为随机策略
26                 return  new RandomRule();
27             }           
28         }


    启动 ConsumerRibbon 模块

        浏览器访问 http://localhost/consumer/employee/list,显示结果如下:

            [{"id":1,"name":"Test Name1","port":8003},{"id":2,"name":"Test Name2","port":8003}]

        刷新页面:

            [{"id":1,"name":"Test Name1","port":8003},{"id":2,"name":"Test Name2","port":8003}]

        再次刷新页面:

            [{"id":1,"name":"Test Name1","port":8001},{"id":2,"name":"Test Name2","port":8001}]

        注:通过 port 的变化可以看出,负载均衡策略已变成随机策略。 


5. 定制负载均衡策略

    通常情况下,Ribbon 提供的这些默认负载均衡策略是可以满足我们的需求的,如果有特殊的要求,我们还可以根据自身需求定制负载均衡策略。

    下面我们就来演示下如何定制负载均衡策略,先注释掉上文 RestTemplateConfig 类里添加的 myRule。

    1) 创建 src/main/java/com/example/config/CustomRule.java 文件

 1         package com.example.config;
 2 
 3         import com.netflix.client.config.IClientConfig;
 4         import com.netflix.loadbalancer.AbstractLoadBalancerRule;
 5         import com.netflix.loadbalancer.ILoadBalancer;
 6         import com.netflix.loadbalancer.Server;
 7         import java.util.List;
 8 
 9         /**
10         * 定制 Ribbon 负载均衡策略
11         */
12         public class CustomRule extends AbstractLoadBalancerRule {
13             private int total = 0;            // 总共被调用的次数,目前要求每台被调用 3 次
14             private int currentIndex = 0;    // 当前提供服务的机器号
15             public Server choose(ILoadBalancer lb, Object key) {
16                 if (lb == null) {
17                     return null;
18                 }
19                 Server server = null;
20                 while (server == null) {
21                     if (Thread.interrupted()) {
22                         return null;
23                     }
24                     // 获取所有有效的服务实例列表
25                     List upList = lb.getReachableServers();
26                     // 获取所有的服务实例的列表
27                     List allList = lb.getAllServers();
28                     // 如果没有任何的服务实例则返回 null
29                     int serverCount = allList.size();
30                     if (serverCount == 0) {
31                         return null;
32                     }
33                     // 与随机策略相似,但每个服务实例只有在调用 3 次之后,才会调用其他的服务实例
34                     if (total < 3) {
35                         server = upList.get(currentIndex);
36                         total++;
37                     } else {
38                         total = 0;
39                         currentIndex++;
40                         if (currentIndex >= upList.size()) {
41                             currentIndex = 0;
42                         }
43                     }
44                     if (server == null) {
45                         Thread.yield();
46                         continue;
47                     }
48                     if (server.isAlive()) {
49                         return (server);
50                     }
51                     server = null;
52                     Thread.yield();
53                 }
54                 return server;
55             }
56 
57             @Override
58             public Server choose(Object key) {
59                 return choose(getLoadBalancer(), key);
60             }
61 
62             @Override
63             public void initWithNiwsConfig(IClientConfig clientConfig) {
64                 // TODO Auto-generated method stub
65             }
66         }


    2) 创建 src/main/java/com/example/config/CustomRuleConfig.java 文件

 1         package com.example.config;
 2 
 3         import com.netflix.loadbalancer.IRule;
 4         import org.springframework.context.annotation.Bean;
 5         import org.springframework.context.annotation.Configuration;
 6 
 7         @Configuration
 8         public class CustomRuleConfig {
 9             @Bean
10             public IRule customRule() {
11                 // 自定义 Ribbon 负载均衡策略
12                 return new CustomRule(); // 自定义,随机选择某一个微服务,执行五次
13             }
14         }


    3) 修改 src/main/java/com/example/App.java 文件

 1         package com.example;
 2 
 3         import org.springframework.boot.SpringApplication;
 4         import org.springframework.boot.autoconfigure.SpringBootApplication;
 5         import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
 6         import org.springframework.cloud.netflix.ribbon.RibbonClient;
 7 
 8         import com.example.config.CustomRuleConfig;
 9 
10         @SpringBootApplication
11         @EnableEurekaClient     // 开启 Eureka client,检索 Eureka Server 注册中心
12         @RibbonClient(name = "EMPLOYEE-SERVICE-PROVIDER", configuration = CustomRuleConfig.class)
13         public class App {
14             public static void main(String[] args) {
15                 SpringApplication.run(App.class, args);
16             }
17         }

        自定义 Ribbon 负载均衡策略在主启动类上使用 RibbonClient 注解,在该微服务启动时,自动去加载自定义的 Ribbon 配置类使配置生效。参数:

            (1) name 为需要定制负载均衡策略的微服务名称(spring.application.name)
            (2) configuration 为定制的负载均衡策略的配置类

        注:官方文档中明确提出,该配置类不能在 ComponentScan 注解(SpringBootApplication 注解包含了该注解)下的包或其子包中。


    4) 启动 ConsumerRibbon 模块

        浏览器访问 http://localhost/consumer/employee/list,显示结果如下:

            [{"id":1,"name":"Test Name1","port":8002},{"id":2,"name":"Test Name2","port":8002}]

        刷新页面:

            [{"id":1,"name":"Test Name1","port":8002},{"id":2,"name":"Test Name2","port":8002}]

        再次刷新页面:

            [{"id":1,"name":"Test Name1","port":8002},{"id":2,"name":"Test Name2","port":8002}]

        注:通过 port 的变化可以看出,负载均衡策略已变成我们的定制策略。