【注意】
前面章节介绍了怎样使用 Netflix Ribbon API 实现负载均衡,以及集成 Spring Cloud 实现负载均衡。由于 Spring Cloud 在 Hoxton.M2 版本中整合新的 LoadBalancer 实现来替代 Ribbon 的版本。因此前面章节为了正常的演示 Spring Cloud 集成 Netflix Ribbon,特意选择了 Spring Cloud 1.3.0.RC1 版本(至少低于 Hoxton 版本),Spring Boot 1.5.2.RELEASE 版本。
在本章节和后续章节中,我们将采用较新版本的 Spring Cloud(将采用 Spring Cloud 2020.0.1 版本,Spring Boot 2.4.3 版本)。
本章节将介绍 Spring Cloud 中负载均衡注解 @LoadBalanced(org.springframework.cloud.client.loadbalancer.LoadBalanced)。
我们在使用 Spring Cloud Ribbon 客户端负载均衡的时候,可以给 RestTemplate bean 加一个 @LoadBalanced 注解,就能让这个 RestTemplate 在请求时拥有客户端负载均衡的能力:
@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }
下面将通过一个简单的实例来验证。
参考“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
查看 Eureka server 注册中心,如下图:
我们通过一个实例来介绍怎样使用 @LoadBalanced 注解。
下面是实例项目的 pom.xml 文件内容,如下:
<?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> <groupId>com.hxstrive.springcloud</groupId> <artifactId>ribbon_demo4</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ribbon_demo4</name> <description>Demo project for Spring Boot</description> <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.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</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> </dependencies> <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> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
在配置文件中配置服务端口、应用名称、以及 Eureka 注册中心地址。配置如下:
server: port: 8080 spring: application: name: demo # 服务地址 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
创建一个 @Configuration 配置类 RibbonConfig,在配置类中配置一个 RestTemplate,且添加 @LoadBalanced 注解,使它具有负载均衡的功能。如下:
package com.hxstrive.springcloud.ribbon_demo1.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * Ribbon 配置 * @author Administrator 2021/3/11 13:35 */ @Configuration public class RibbonConfig { // 配置一个 RestTemplate @Bean // 添加该注解后,可以使得 RestTemplate 拥有负载均衡能力 @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } }
编写客户端代码,它通过 RestTemplate 对指定的 URL 发起 HTTP 请求。注意,这里填写的 URL 不能使用具体的IP地址,需要使用服务名称。代码如下:
package com.hxstrive.springcloud.ribbon_demo1; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.Date; @RestController @SpringBootApplication public class RibbonDemo1Application { @Autowired private RestTemplate restTemplate; public static void main(String[] args) { SpringApplication.run(RibbonDemo1Application.class, args); } @GetMapping({"/", "/index.html"}) public String index() { // 如果你要实现负载均衡,调用地址不能写具体主机地址 // 需要将主机地址替换成对于服务的服务名,即 spring.application.name 指定的值 // 你也可以到 Eureka server 中去查看 String url = "http://USER/info"; for(int i = 0; i < 10; 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()); } } return "Load success " + new Date(); } }
运行结果如下:
2021-03-31 13:17:27.736 INFO 1468 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_DEMO/MS-OYCYMLXUSLLD:demo:8080 - registration status: 204 200 :: From user3 - Wed Mar 31 13:17:28 CST 2021 200 :: From user2 - Wed Mar 31 13:17:29 CST 2021 200 :: From user1 - Wed Mar 31 13:17:29 CST 2021 200 :: From user3 - Wed Mar 31 13:17:29 CST 2021 200 :: From user2 - Wed Mar 31 13:17:29 CST 2021 200 :: From user1 - Wed Mar 31 13:17:29 CST 2021 200 :: From user3 - Wed Mar 31 13:17:29 CST 2021 200 :: From user2 - Wed Mar 31 13:17:29 CST 2021 200 :: From user1 - Wed Mar 31 13:17:29 CST 2021 200 :: From user3 - Wed Mar 31 13:17:29 CST 2021
仔细观察上面结果,调用将采用轮询的方式依次调用 user3、user2、user1 服务。到这里 Netflix Ribbon 的基本用法就介绍完了,关于更多用法自行参考官方手册。