前面章节介绍了 Spring Cloud 自带的 LoadBalancer 负载均衡器简单用法,默认使用轮询算法。本章节将介绍怎样通过配置文件指定其他负载均衡算法(随机算法)。
参考“Netflix Ribbon 简单实例”章节提供的 User 服务,这里将不再赘述。需要注意的是,这里的 User 服务需要注册到 Eureka 注册中心,因此需要在 application.yml 配置文件中添加如下配置:
# 端口不需要指定,通过命令行动态指定 spring: application: name: user # 服务地址 eureka: instance: hostname: localhost # 心跳间隔5s,默认30s。每一个服务配置后,心跳间隔和心跳超时时间会被保存在server端, # 不同服务的心跳频率可能不同,server 端会根据保存的配置来分别探活 lease-renewal-interval-in-seconds: 5 # 心跳超时时间10s,默认90s。从client端最后一次发出心跳后,达到这个时间没有再次发出 # 心跳,表示服务不可用,将它的实例从注册中心移除 lease-expiration-duration-in-seconds: 10 client: service-url: # 注册中心路径,表示我们向这个注册中心注册服务,如果向多个注册中心注册,用“,”进行分隔 defaultZone: http://localhost:8077/eureka
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <!-- 省略代码 --> <properties> <java.version>1.8</java.version> <spring-cloud.version>2020.0.1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <!-- 省略代码 --> </project>
上面添加了 Spring Cloud 和 Spring Boot 依赖,以及 Eureka client 依赖。
在该配置文件中指定 Eureka 注册中心地址,以及应用名称和端口。配置如下:
server: port: 8080 spring: application: name: demo eureka: client: serviceUrl: # 指定注册中心 defaultZone: http://localhost:8077/eureka/
创建 RestTemplateConfig 类,用来配置 RestTemplate 类,并且在该类上面添加 @LoadBalanced 注解,该注解用来在 RestTemplate 类上面启用负载均衡功能。
同时,该配置类在类上面添加 @LoadBalancerClient 注解,用来指定 USER 服务的负载均衡配置,这里我们的配置类 ReactorLoadBalancerConfig 中指定 LoadBalancer 负载均衡算法为随机算法。代码如下:
import com.hxstrive.springcloud.ReactorLoadBalancerConfig; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration // 指定你自定义的配置文件,配置 LoadBalancer 的负载均衡算法 @LoadBalancerClient(value = "USER", configuration = ReactorLoadBalancerConfig.class) public class RestTemplateConfig { // 配置一个 RestTemplate @Bean // 添加该注解后,可以使得 RestTemplate 拥有负载均衡能力 @LoadBalanced public RestTemplate restTemplate() { System.out.println("init RestTemplate"); return new RestTemplate(); } }
创建 ReactorLoadBalancerConfig 配置类,该类中使用 @Bean 创建随机负载均衡算法实现类 RandomLoadBalancer 的实例。代码如下:
import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer; import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; import org.springframework.context.annotation.Bean; import org.springframework.core.env.Environment; public class ReactorLoadBalancerConfig { @Bean ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { System.out.println("init ReactorLoadBalancer"); String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); return new RandomLoadBalancer( loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name); } }
注意:ReactorLoadBalancerConfig 类上面不要添加 @Configuration 注解,并且不能将 ReactorLoadBalancerConfig 配置类放到应用程序能扫描的路径下面。在 Spring Boot 中,默认将扫描 @SpringBootApplication 注解标识的 Application 启动类当前目录及子目录下面的类。
创建 Demo1 类,使用 @Autowired 注解注入 RestTemplate 实例,然后使用 RestTemplate 实例调用目标用户服务。代码如下:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @Component public class Demo1 { @Autowired private RestTemplate restTemplate; public void demo() { System.out.println("restTemplate = " + restTemplate); // 如果你要实现负载均衡,调用地址不能写具体主机地址 // 需要将主机地址替换成对于服务的服务名,即 spring.application.name 指定的值 // 你也可以到 Eureka server 中去查看 String url = "http://USER/info"; for(int i = 0; i < 21; i++) { ResponseEntity<String> resp = restTemplate.getForEntity(url, String.class); if(resp.getStatusCode().is2xxSuccessful()) { System.out.println(resp.getStatusCode().value() + " :: " + resp.getBody()); } else { System.out.println(resp.getStatusCode().value() + " :: " + resp.getBody()); } } } }
执行结果如下:
200 :: From user2 - Wed Apr 14 23:07:31 CST 2021 200 :: From user2 - Wed Apr 14 23:07:31 CST 2021 200 :: From user3 - Wed Apr 14 23:07:32 CST 2021 ... 200 :: From user1 - Wed Apr 14 23:07:36 CST 2021 200 :: From user1 - Wed Apr 14 23:07:36 CST 2021 200 :: From user1 - Wed Apr 14 23:07:36 CST 2021 200 :: From user3 - Wed Apr 14 23:07:37 CST 2021 200 :: From user2 - Wed Apr 14 23:07:38 CST 2021 200 :: From user3 - Wed Apr 14 23:07:38 CST 2021 200 :: From user1 - Wed Apr 14 23:07:39 CST 2021
根据上面的结果得知,我们配置的负载均衡随机算法生效了。