Spring Cloud Hoxton.SR4 Spring Boot 2.3.0.RELEASE
GitHub:shpunishment/spring-cloud-learning/spring-cloud-ribbon-test
1. 简介
Spring Cloud Netflix Ribbon是Spring Cloud Netflix子项目的核心组件之一,主要给服务间调用及API网关转发提供负载均衡的功能。Spring Cloud OpenFeign已集成了Ribbon。
在微服务架构中,很多服务都会部署多个,其他服务去调用该服务的时候,负载均衡可以增加系统的可用性和扩展性,当我们使用RestTemplate来调用其他服务时,Ribbon可以很方便的实现负载均衡功能。
2. 使用
先用IDEA创建一个Spring Boot的项目,可以随意引用一个Spring Cloud的组件,之后也会删掉。
创建完,删掉除了pom.xml以外的其他文件,再修改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.3.0.RELEASE</version>
</parent>
<groupId>com.shpun</groupId>
<artifactId>spring-cloud-ribbon-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-ribbon-test</name>
<description>spring cloud ribbon test</description>
<!--修改打包方式为pom-->
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Hoxton.SR4</spring-cloud.version>
</properties>
<modules>
<!--后续添加子模块用-->
</modules>
<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>
</project>
2.1 eureka-server
创建子模块eureka-server
修改pom继承
<parent>
<groupId>com.shpun</groupId>
<artifactId>spring-cloud-ribbon-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
再添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
修改application.yml
server:
port: 8100
spring:
application:
name: eureka-server
eureka:
instance:
hostname: localhost
client:
# 是否从注册中心获取服务(注册中心不需要开启)
register-with-eureka: false
# 是否将服务注册到注册中心(注册中心不需要开启)
fetch-registry: false
在启动类上添加@EnableEurekaServer
注解来启用Euerka注册中心功能。
2.2 user-service
创建子模块user-service,用于给Ribbon提供服务调用。
修改pom继承
<parent>
<groupId>com.shpun</groupId>
<artifactId>spring-cloud-ribbon-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
再添加依赖
<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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
修改application.yml
server:
port: 8101
spring:
application:
name: user-service
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:4306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
eureka:
instance:
# 是否优先使用ip来作为主机名
prefer-ip-address: true
client:
service-url:
defaultZone: http://localhost:8100/eureka
mybatis:
typeAliasesPackage: com.shpun.model
mapper-locations: classpath:mapper/**.xml
Model,Mapper,Service等省略。
UserController 完成对User的CURD接口。
@RequestMapping("/api/user")
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/add")
public ResultVo<?> add(@RequestBody User user) {
userService.insertSelective(user);
return ResultVo.ok();
}
@GetMapping("/delete/{userId}")
public ResultVo<?> delete(@PathVariable("userId") Integer userId) {
userService.deleteByPrimaryKey(userId);
return ResultVo.ok();
}
@PostMapping("/update")
public ResultVo<?> update(@RequestBody User user) {
userService.updateByPrimaryKeySelective(user);
return ResultVo.ok();
}
@GetMapping("/{userId}")
public ResultVo<User> get(@PathVariable("userId") Integer userId) {
User user = userService.selectByPrimaryKey(userId);
return ResultVo.okData(user);
}
}
在启动类上添加@EnableDiscoveryClient
注解表明是一个服务发现的客户端。
2.3 ribbon-service
创建子模块ribbon-service,调用user-service模块演示负载均衡的服务。
修改pom继承
<parent>
<groupId>com.shpun</groupId>
<artifactId>spring-cloud-ribbon-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
再添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</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>
修改application.yml
server:
port: 8200
spring:
application:
name: ribbon-service
eureka:
instance:
# 是否优先使用ip来作为主机名
prefer-ip-address: true
client:
service-url:
defaultZone: http://localhost:8100/eureka
service-url:
user-service: http://user-service
# 全局配置
ribbon:
# 服务请求连接超时时间(毫秒)
ConnectTimeout: 1000
# 服务请求处理超时时间(毫秒)
ReadTimeout: 3000
# 对超时请求启用重试机制
OkToRetryOnAllOperations: true
# 切换重试实例的最大个数
MaxAutoRetriesNextServer: 1
# 切换实例后重试最大次数
MaxAutoRetries: 1
# 修改负载均衡算法
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
# 指定应用进行配置
user-service:
ribbon:
ConnectTimeout: 1000
ReadTimeout: 3000
OkToRetryOnAllOperations: true
MaxAutoRetriesNextServer: 1
MaxAutoRetries: 1
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
@LoadBalanced
赋予RestTemplate负载均衡的能力
@Configuration
public class RibbonConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Model等省略。
UserRibbonController 使用RestTemplate调用user-service调用相关接口。
@RequestMapping("/api/user")
@RestController
public class UserRibbonController {
@Autowired
private RestTemplate restTemplate;
@Value("${service-url.user-service}")
private String userServiceUrl;
@PostMapping("/add")
public ResultVo<?> add(@RequestBody User user) {
return restTemplate.postForObject(userServiceUrl + "/api/user/add", user, ResultVo.class);
}
@GetMapping("/delete/{userId}")
public ResultVo<?> delete(@PathVariable("userId") Integer userId) {
return restTemplate.getForObject(userServiceUrl + "/api/user/delete/{0}", ResultVo.class, userId);
}
@PostMapping("/update")
public ResultVo<?> update(@RequestBody User user) {
return restTemplate.postForObject(userServiceUrl + "/api/user/update", user, ResultVo.class);
}
@GetMapping("/{userId}")
public ResultVo<?> get(@PathVariable("userId") Integer userId) {
return restTemplate.getForObject(userServiceUrl + "/api/user/{0}", ResultVo.class, userId);
}
}
在启动类上添加@EnableDiscoveryClient
注解表明是一个服务发现的客户端。
2.4 测试
启用 eureka-server,ribbon-srevice,user-service。
可以覆盖参数,实现使用不同的配置来启动user-service。
测试请求用户2
查看日志可看到调用端口为8102的user-service
测试请求用户3
查看日志可看到调用端口为8101的user-service
3. 负载均衡策略
负载均衡策略是当A服务调用B服务时,B服务有多个实例,A服务需要决定使用何种方式来调用的B实例。
Ribbon可以选择以下几种负载均衡策略:
负载均衡策略 | 描述 |
---|---|
com.netflix.loadbalancer.RandomRule | 从提供服务的实例中以随机的方式 |
com.netflix.loadbalancer.RoundRobinRule | 以线性轮询的方式,就是维护一个计数器,从提供服务的实例中按顺序选取,第一次选第一个,第二次选第二个,以此类推,到最后一个以后再从头来过 |
com.netflix.loadbalancer.RetryRule | 在RoundRobinRule的基础上添加重试机制,即在指定的重试时间内,反复使用线性轮询策略来选择可用实例 |
com.netflix.loadbalancer.WeightedResponseTimeRule | 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择 |
com.netflix.loadbalancer.BestAvailableRule | 选择并发较小的实例 |
com.netflix.loadbalancer.AvailabilityFilteringRule | 先过滤掉故障实例,再选择并发较小的实例 |
com.netflix.loadbalancer.ZoneAwareLoadBalancer | 采用双重过滤,同时过滤不是同一区域的实例和故障实例,选择并发较小的实例。 |
参考:
Spring Cloud Netfix 官方文档
Spring Cloud入门-Ribbon服务消费者(Hoxton版本)