Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将NetFlix的中间层服务连接在一起.Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等.简单的说,就是在配置文件中列出 Load Balancer(简称LB) 后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器.我们也很容易使用Ribbon实现自定义的负载均衡算法.
1. 负载均衡
1.1 服务端负载均衡
服务端负载均衡一般是指 Nginx 或者 F5 之类的工具,其中 Nginx也叫反向代理代理服务器,它的工作流程如下图:
所有的请求首先到达负载均衡服务器,再由负载均衡服务器根据提前配置好的负载均衡策略,将请求转发到不同的 Real Server 上,对于客户端来说,它并不知道究竟是哪一个 Real Server 提供的服务,这种负载均衡方式我们一般称之为服务端负载均衡。
1.2 客户端负载均衡
客户端负载均衡是先将 服务提供者 的所有地址拿到,然后 服务消费者 根据本地配置的负载均衡策略,从 服务提供者 地址列表中挑选一个地址去调用,调用过程如下图:
在这个调用过程中,客户端首先去 Eureka 中获取 服务提供者 地址,获取到 服务提供者 地址列表之后,再根据本地提前配置好的负载均衡策略从地址列表中挑选一个地址去调用,这个过程没有中间代理服务器,到底调用哪一个服务是由 服务消费者 自己决定的,因此这种负载均衡我们也称之为客户端负载均衡。
2. 演示 Ribbon 的使用
Ribbon内置的负载均衡策略有:
类名 | 描述 |
---|---|
RandomRule | 随机策略,随机选择server |
RoundRobinRule | 轮询策略,轮询选择index对应位置的Server |
RetryRule | 重试策略, 对选定的负载均衡策略机上重试机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的server; |
BestAvailableRule | 最低并发策略,会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务 |
AvailabilityFilteringRule | 可用过滤策略,先过滤掉故障实例,再选择并发较小的实例 |
ResponseTimeWeightedRule | 响应时间加权重策略,根据server的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低。响应时间越短,权重越高,被选中的概率越高,这个策略很贴切,综合了各种因素,比如:网络,磁盘,io等,都直接影响响应时间 |
ZoneAvoidanceRule | 区域权重策略,也是默认策略,综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个Zone的运行性能是否可用,剔除不可用的Zone中的所有server |
2.1 consumer-order8001
中配置负载均衡
**注意:**下面演示的内容,都是在上一篇文章《SpringCloud-01-Eureka》代码基础上做修改的。
在 Spring Cloud 中,要实现负载均衡其实非常容易,只需要在 RestTemplate 的Bean上添加一个 @LoadBalanced 注解即可,如下:
RestTemplate 的Bean上添加一个 @LoadBalanced 注解
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
将 OrderController PAYMENT_URL 由:
public static final String PAYMENT_URL = "http://localhost:9001";
改为服务名:
public static final String PAYMENT_URL = "http://PAYMENTSERVICE";
修改后的 OrderController
@RestController
@RequestMapping("/order")
public class OrderController {
public static final String PAYMENT_URL = "http://PAYMENTSERVICE";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/getPayment/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") String id) {
return this.restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
}
}
配置应用的负载均衡规则为轮询策略 RoundRobinRule。
新增配置类 AppConfig.java,配置 IRule
@Configuration
public class AppConfig {
/**
* 配置应用的负载均衡规则为轮询策略 RoundRobinRule
*
* @return
*/
@Bean
public IRule rule() {
return new RoundRobinRule();
}
@Bean
@LoadBalanced // 添加@LoadBalanced注解,开启Ribbon负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2.2 新建一个服务提供者 provider-payment9002
根据工程 provider-payment9001
新建provider-payment9002
,也就是新建多一个服务提供者 paymentService。
当前整个工程的结构如下:
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>
<artifactId>006SpringCloud</artifactId>
<groupId>com.xander</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>provider-payment9002</artifactId>
<dependencies>
<!--eureka-client-->
<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>
<!--common-api-->
<dependency>
<groupId>com.xander</groupId>
<artifactId>common-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 9002
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
# 集群版-
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
# 服务实例id
instance-id: payment9002
#访问路径可以显示IP地址
prefer-ip-address: true
spring:
application:
# 微服务名称
name: paymentService
服务启动类 ProviderPayment9002.java
@SpringBootApplication
@EnableEurekaClient
public class ProviderPayment9002 {
public static void main(String[] args) {
SpringApplication.run(ProviderPayment9002.class, args);
}
}
PaymentController
@RestController
@RequestMapping("/payment")
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
if (id != null && id % 2 == 0) {
Payment payment = Payment.newInstance().setSerial(UUID.randomUUID().toString()).setId(id);
return CommonResult.newInstance().setCode(200).setMessage("查询成功,serverPort:" + serverPort).setData(payment);
} else {
return CommonResult.newInstance().setCode(444).setMessage("没有对应记录,查询ID: " + id).setData(null);
}
}
}
启动服务,可以看到微服务 paymentService 已经注册进 Eureka 注册中心,paymentService有两个实例
2.3 测试Ribbon负载均衡策略
在浏览器上发送两次请求,可以发现响应体中 serverPort 分别为 9001 和 9002,说明我们配置的轮询负载均衡策略起效果了。
3. Ribbon的常用配置
注意:
这里讲个坑,使用配置文件添加Ribbon配置时,一定要添加 ribbon.restclient.enabled=true
这一配置,否则连接超时、读取超时、重试等这些配置都不会生效。
针对单个服务的重试与超时配置:
# 针对 paymentService 服务的Ribbon配置
paymentService:
ribbon:
# 基于配置文件形式的 针对单个服务的 Ribbon 负载均衡策略
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
# http建立socket超时时间,毫秒
ConnectTimeout: 2000
# http读取响应socket超时时间
ReadTimeout: 10000
# 同一台实例最大重试次数,不包括首次调用
MaxAutoRetries: 0
# 重试负载均衡其他的实例最大重试次数,不包括首次server
MaxAutoRetriesNextServer: 2
# 是否所有操作都重试,POST请求注意多次提交错误。
# 默认false,设定为false的话,只有get请求会重试
OkToRetryOnAllOperations: true
ribbon:
#启用rest client配置
restclient:
enabled: true
全局服务的重试与超时配置,针对所有服务:
### 全局Ribbon 配置
ribbon:
#启用rest client配置
restclient:
enabled: true
# http建立socket超时时间,毫秒
ConnectTimeout: 2000
# http读取响应socket超时时间
ReadTimeout: 10000
# 同一台实例最大重试次数,不包括首次调用
MaxAutoRetries: 0
# 重试负载均衡其他的实例最大重试次数,不包括首次server
MaxAutoRetriesNextServer: 2
# 是否所有操作都重试,POST请求注意多次提交错误。
# 默认false,设定为false的话,只有get请求会重试
OkToRetryOnAllOperations: true