Spring Cloud Ribbon 是一套应用于微服务的负载均衡,它和我们熟知的服务端负载均衡的不同之处在于,它是客户端负载均衡。
服务端负载均衡是服务端收到客户端请求后,根据相应机制,将请求分发到对应后台服务来处理请求;客户端负载均衡也就是调用方来控制调用哪个服务,在Spring Cloud中Ribbon的负载均衡机制默认采用轮询机制,也就是轮流访问,这种机制有一个前提就是,客户端手里持有一个服务列表,这样才能做到轮询服务。
我们接下来就实现一个负载均衡的例子。
实操O(∩_∩)O~开始
我们将于前面的Service-a服务,复制出来,新建两个服务Service-c、Service-d。
Service-c负责提供服务,因为要实现负载均衡(实现轮询调用效果),所有服务c至少要启动两个服务。
Service-d负责发起调用,也就是在d里实现负载均衡调用功能。
1、Service-c代码,因为和Service-a相同,我们这里只贴出提供服务调用的Controller代码(可以看一下之前的文章,获取Service-a项目代码)
package com.itzhimei.service.c.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ServiceCController {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCController.class);
@GetMapping(value = "/getRibbonInfo")
public String getInfo(){
LOGGER.info("当前请求服务为Service-C, /getRibbonInfo");
return "当前请求服务为Service-C";
}
}
2、然后启动两个服务C,命令如下:
java -jar d:/service-c-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev --server.port=1130
java -jar d:/service-c-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev --server.port=1131
两个服务分别启动在1130和1131端口。
此时在注册中心,你也能看到有两个服务C注册了。
接下来就是构建发起请求方的代码,也就是Service-d
3、Service-d的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.itzhimei</groupId>
<artifactId>service-d</artifactId>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
这里和之前的服务项目的区别在于多了一个依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
4、应用启动类
package com.itzhimei.d;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
public class ServiceDApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ServiceDApplication.class, args);
}
}
这里重点在这里:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
RestTemplate:是一个REST请求的模板,封装了不同Rest请求类型,支持get、、post、put等请求方式,这里同时使用了@LoadBalanced注解,相当于开启了负载均衡。
5、业务调用类,在这里就是真正发生请求的代码,ribbon的效果也在这里应用了。
package com.itzhimei.d.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ServiceDController {
@Autowired
RestTemplate restTemplate;
@RequestMapping(value = "/getRibbonInfo", method = RequestMethod.GET)
public String getRibbonInfo() {
String url = "http://SERVICE-C/getRibbonInfo";
String body = restTemplate.getForEntity(url, String.class).getBody();
return body;
}
}
我通过ServiceId,找到服务C,就是通过这里:String url = “http://SERVICE-C/getRibbonInfo”。
然后用restTemplate发起调用,并返回结果。
6、验证结果
服务D向服务C发起请求,
http://localhost:1140/getRibbonInfo
页面输出:当前请求服务为Service-C
我一共发起了6次请求,两个服务C的控制台都输出了三次日志,如下图: