Ribbon
Spring Cloud Ribbon 是一套实现客户端负载均衡的工具。当然也有服务端的负载均衡工具,我们后面再介绍。可以认为 Ribbon 就是一个负载均衡器(Load Balancer)。负载均衡就是将用户的请求平摊的分配到多个服务上,从而达到系统的高可用。
简单来说,Ribbon 的主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 客户端组件给我们提供了一套很完善的配置项,比如可以配置连接超时、重试等等。
Spring Cloud 项目使用Ribbon
前面文章中,将订单服务注册到 Eureka,然后消费方可以通过 http 请求去获取订单的信息,但是这是最原始的 http 调用,没有任何 Ribbon 的东西在里面,接下来我们要在消费方(即microservice-order-consumer)植入 Ribbon
1.导入Ribbon依赖包
<!--eureka Client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--ribbon负载均衡依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
Eureka Client 的依赖也需要导入,因为服务注册到了 Eureka,Ribbon 也需要和 Eureka 整合,所以在消费方也导入了 Eureka 依赖。
2.配置application文件:
server.port = 9001
eureka.client.register-with-eureka = false
eureka.client.service-url.defaultZone = http://eureka01:7001/eureka/,http://eureka02:7002/eureka/,http://eureka03:7003/eureka/
消费方这里也分别向eureka01, eureka02和eureka03注册,也作为了一个Eureka Client
3. 在http中植入Ribbon
Ribbon为客户端提供负载均衡服务,之前的 microservice-order-consumer 中是使用 RestTemplate 来发送 http 请求,调用订单服务的,但是没有负载均衡,所以现在我们要让这个 http 调用自带负载均衡。
@Configuration
public class RestTmplateConfig {
/**
* '@LoadBalanced'注解表示使用Ribbon实现客户端负载均衡
* @return RestTemplate
*/
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
在RestTemplate上添加一个 @LoadBalanced
注解即可开启 Ribbon 负载均衡。这样就可以通过微服务的名字从 Eureka 中找到对应的服务并访问了
4. 将IP改成服务名
将接口中的localhost:8001 改成服务提供方在Eureka中注册服务的暴露名称:MICROSERVICE-ORDER
@EnableEurekaClient
@RestController
@RequestMapping("/consumer/order")
public class OrderConsumerController {
// 订单服务提供者模块的 url 前缀
//private static final String ORDER_PROVIDER_URL_PREFIX = "http://localhost:8001";
private static final String ORDER_PROVIDER_URL_PREFIX = "http://MICROSERVICE-ORDER";
@Resource
private RestTemplate restTemplate;
@GetMapping("/get/{id}")
public TOrder getOrder(@PathVariable Long id) {
return restTemplate.getForObject(ORDER_PROVIDER_URL_PREFIX + "/provider/order/get/" + id, TOrder.class);
}
@GetMapping("/get/list")
public List<TOrder> getAll() {
return restTemplate.getForObject(ORDER_PROVIDER_URL_PREFIX + "/provider/order/get/list", List.class);
}
}
注释掉的那部分,是原来的访问方式,订单提供服务是8001端口,现在我们将ip+端口号这种访问方式,改成微服务名称,这个名称就是 Eureka 管理界面显示的注册进去的名称,也即服务提供方的application配置文件中配置的服务名称:
# 服务端口号
server.port = 8001
# 对外暴露的服务名称
spring.application.name = MICROSERVICE-ORDER
启动测试:
分别启动 eureka7001、eureka7002、eureka7003以及订单服务8001,可以看到订单服务已经注册到 Eureka 集群。
然后启动消费服务访问:http://localhost:9001/consumer/order/get/list
初步实现客户端使用Ribbon负载均衡
完善Ribbon负载均衡
实现负载均衡,要有多个订单服务提供者,目前我们就一个provider01,端口号 8001 ,参照这个服务,再创建两个子模块,也是订单服务提供者,取名为 provider02和provider03,端口号分别命名为8002和8003
现在的负载均衡架构图
Ribbon 是客户端的负载均衡工具,它在服务的消费方,首先三个订单服务 8001、8002和8003都会注册到 Eureka 集群,Ribbon 会先从 Eureka 集群中查询可用的服务列表,然后根据 Ribbon 提供的负载均衡算法,负载均衡的请求可用的订单服务。
Ribbon 默认的负载均衡算法是轮询,也就是按顺序挨个的调用。我们先来搭建一下整个服务架构,上面提到了,首先要拷贝两个订单服务,端口号分别为 8002 和 8003
三个订单服务配置对比:
#8001
# 服务端口号
server.port = 8001
# 数据库配置
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/demo?
# 人性化显示出服务的信息
eureka.instance.instance-id = provicer01-8001
#8001
# 服务端口号
server.port = 8002
# 数据库配置
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/demo2?
# 人性化显示出服务的信息
eureka.instance.instance-id = provicer02-8002
#8001
# 服务端口号
server.port = 8003
# 数据库配置
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/demo3?
# 人性化显示出服务的信息
eureka.instance.instance-id = provicer03-8003
对比可以看出以下三点:
- 对外暴露的服务名称必须要相同,因为都是同一个服务,只不过有多个而已,因为接下来Ribbon是通过服务名来调用服务的;
- 每个服务连接了不同的数据库,这样用来区分不同的服务,便于测试,实际中也可能是便于维护;
- 每个服务的个性化名称展示可以区分一下,这样在eureka里可以很好的辨别出来
Eureka 集群还是使用前面的 7001、7002 和 7003,首先我们启动 Eureka 集群,然后分别启动 8001、8002 和 8003 三个订单服务。
可以看到三个订单服务都已经注册到Eureka服务上了
接下来启动消费者服务测试:启动consumer端
访问:http://localhost:9001/consumer/order/get/2
第一次:
第二次:
第三次:
可以看到Ribbon 的默认负载均衡算法是轮询,dbsource的值一在 microservice01、microservice02 和 microservice03 之间轮询切换(这个值是我在三个数据库中设置的不同的值,就是为了在这展示,方便看到效果),这就说明了,Ribbon 的负载均衡已经起作用了,客户端会轮询的根据服务名,从 8001、8002 和 8003 之间切换访问,从而达到负载均衡的效果。
Ribbon 自带的负载均衡策略有如下几个:
- RoundRibbonRule:轮询。人人有份,一个个来!
- RandomRule:随机。拼人品了!
- AvailabilityFilteringRule:先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,以及并发连接数超过阈值的服务,剩下的服务,使用轮询策略。
- WeightedResponseTimeRule:根据平均响应时间计算所有服务的权重,响应越快的服务权重越高,越容易被选中。一开始启动时,统计信息不足的情况下,使用轮询。
- RetryRule:先轮询,如果获取失败则在指定时间内重试,重新轮询可用的服务。
- BestAvailableRule:先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。
- ZoneAvoidanceRule:复合判断 server 所在区域的性能和 server 的可用性选择服务器
自定义Ribbon负载均衡算法,在配置类中new RandomRule() 制定负载均衡算法!
/**
* 配置RestTemplate
* @author shengwu ni
*/
@Configuration
public class RestTemplateConfig {
/**
* '@LoadBalanced'注解表示使用Ribbon实现客户端负载均衡
* @return RestTemplate
*/
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
/**
* 指定其他负载均衡策略
* @return IRule
*/
@Bean
public IRule myRule() {
// 指定重试策略:随机策略
return new RandomRule();
}
}
Spring Cloud基本Eureka客户端使用Ribbon实现负载均衡!