在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于 Http Restful 的。Spring Cloud 有两种服务调用方式,一种是Ribbon + RestTemplate消费服务,另一种是 Feign消费服务。这篇我们主要讲解 Ribbon 消费服务。
官网地址:https://docs.spring.io/spring-cloud-netflix/docs/2.2.8.RELEASE/reference/html/#spring-cloud-ribbon
Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡器,它可以在通过客户端中配置的 RibbonServerList 服务端列表去轮询访问以达到均衡负载的作用。当 Ribbon 与 Eureka 联合使用时,Ribbon 的服务实例清单 RibbonServerList 会被 DiscoveryEnabledNIWSServerList 重写,扩展成从 Eureka 注册中心中获取服务端列表。同时它也会用 NIWSDiscoveryPing 来取代 IPing,它将职责委托给 Eureka 来确定服务端是否已经启动。
高版本中没有找到Ribbon的文档。
一、准备工作
1.1、创建启动一个注册中心,二个服务提供者
请参考:Spring Cloud Eureka 服务注册中心组建1:搭建注册中心
- piao-server:注册中心服务端,端口2000。
- piao-client:客户端服务提供者,端口2010。
- piao-client2:客户端服务提供者,端口2011。
如果不想创建多个项目,可以请考这篇文章:IDEA 启动多个SpringBoot项目不同端口
启动成功如下:
二、创建服务消费者
2.1、创建 piao-ribbon 项目。
我们这里使用IDEA创建,文件-》新建-》项目-》下一步到如下的页面:
然后在点下一步,直到完成。
2.2、修改依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
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.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.piao</groupId>
<artifactId>piao-ribbon</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>piao-ribbon</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
</properties>
<dependencies>
<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>
</dependency>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</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>
2.3、添加配置
编辑项目的 application.properites 文件,添加如下相关配置:
#服务注册中心端口号
server.port=2005
#指定服务名称
spring.application.name=piao-ribbon
#指定服务注册中心的地址
eureka.client.serviceUrl.defaultZone=http://localhost:2000/eureka/
在启动类上添加 @EnableDiscoveryClient 注解。使用@LoadBalanced注解赋予RestTemplate负载均衡的能力。
@EnableDiscoveryClient
@SpringBootApplication
public class PiaoRibbonApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(PiaoRibbonApplication.class, args);
}
}
Spring Cloud Ribbon 中实现客户端负载均衡的原理简单来说:就是通过 LoadBalancerInterceptor 拦截器对 RestTemplate 的请求进行拦截,并利用 Spring Cloud 的负载均衡器 LoadBalancerClient 将一服务名为 host 的 URI 转换成具体的服务实例地址的过程。
2.4、编写一个消费者的请求类
在该接口中,通过前面定义的 RestTemplate 来实现对 PIAO-CLIENT 服务提供的 /client 接口进行负载均衡的调用。
@RestController
public class ConsumerController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/consumer")
public String helloConsumer() {
// 通过服务名调用服务提供者的接口
return restTemplate.getForEntity("http://PIAO-CLIENT/client", String.class).getBody();
}
}
2.5、启动服务并访问
我们访问注册中心地址:http://127.0.0.1:2000/
访问地址:http://127.0.0.1:2005/consumer
第一次访问:
第二次访问:
我们看到了请求分别调用了我们之前准备的 PIAO-CLIENT 接口。实现负载均衡的效果。
Ribbon 输出了当前客户端维护的 IAO-CLIENT 的服务列表情况。其中包含了各个实例的位置,Ribbon 就是按照此信息进行轮训访问,以实现基于客户端的负载均衡。除此之外还输出了一些其他非常有用的信息,如对各个实例的请求总数量、第一次连接信息、上一次连接信息、总的请求失败数量等。
三、Ribbon 重试机制
Spring Cloud Eureka 实现的服务治理机制强调了 CAP 原理中的 AP,即可用性与可靠性,牺牲了一定的一致性(在极端情况下它宁愿接受故障实例也不要丢掉"健康"实例)。但不论是由于触发了保护机制还是服务剔除的延迟,引起服务调用到故障实例的时候,我们还是希望能够增强对这类问题的容错。所以,我们在实现服务调用的时候通常会加入一些重试机制。
从 Camden SR2 版本开始,Spring Cloud 就整合了 Spring Retry 来增强 RestTemplate 的重试能力,对于开发者来说只需通过简单的配置,原来那些通过 RestTemplate 实现的服务访问就会自动根据配置来实现重试策略。
3.1、添加依赖
<!-- 重试机制-->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
3.2、修改配置
编辑项目的 application.properites 文件,对 ribbon 重试相关参数进行设置。
spring.cloud.loadbalancer.retry.enabled 参数用来开启重试机制,因为默认是 true(开启)。因此只要引入 spring-retry 依赖就可以自动实现重试功能。如果要将其关闭,只要将其设为 false 即可。
全局配置:
#断路器的超时时长需要大于Ribbon的超时时间,不然不会触发重试
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
#请求连接超时时间(毫秒)
ribbon.ConnectTimeout=1000
#请求处理的超时时间(毫秒)
ribbon.ReadTimeout=3000
#对所有请求都进行重试(是否所有操作都重试,若false则仅get请求重试)
ribbon.OkToRetryOnAllOperations=true
#切换实例的重试次数
ribbon.MaxAutoRetriesNextServer=2
#对当前实例的重试次数
ribbon.MaxAutoRetries=1
指定服务配置:
#断路器的超时时长需要大于Ribbon的超时时间,不然不会触发重试
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
#请求连接超时时间(毫秒)
piao-client.ribbon.ConnectTimeout=1000
#请求处理的超时时间(毫秒)
piao-client.ribbon.ReadTimeout=3000
#对所有请求都进行重试(是否所有操作都重试,若false则仅get请求重试)
piao-client.ribbon.OkToRetryOnAllOperations=true
#切换实例的重试次数
piao-client.ribbon.MaxAutoRetriesNextServer=2
#对当前实例的重试次数
piao-client.ribbon.MaxAutoRetries=1
配置说明:当访问到故障请求的时候,它会再尝试访问一次当前实例(次数由 MaxAutoRetries 配置),如果不行,就换一个实例进行访问,如果还不行,再换一次实例访问(更换次数由 MaxAutoRetriesNextServer 配置),如果依然不行,返回失败信息。
四、配置负载均衡策略
全局配置:
#修改负载均衡算法
ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
指定服务配置:
#修改负载均衡算法
piao-client.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
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:采用双重过滤,同时过滤不是同一区域的实例和故障实例,选择并发较小的实例。