迎着风跑  

Ribbon:客户端负载均衡器

在 Spring Cloud 体系中,发起远程调用,本质上就是发起 HTTP 请求。

服务的提供者本质上就是一个 RESTful 风格的 Web 服务,因此,在知道其 API 的情况下我们只要能够发出 HTTP 请求,实际上就『调用』到了这个服务。

#1. Ribbon 在 RestTemplate 中的负载均衡

在你没有意识到 Ribbon(读作 [ˈrɪbən] )存在的时候,Ribbon 就已经可以在你的项目中(配合 RestTemplate)起作用了。

为你的 RestTemplate 的 @Bean 加上 @LoadBalanced 注解:

@Bean
@LoadBalanced 
RestTemplate restTemplate() {
  return new RestTemplate();
}
Copied!

@LoadBalanced 注解背后就是 Spring AOP 动态代理的思想。

你循环调用 RestTemplate,以目标服务在 Nacos Server 上注册的名字来代替 URL 中的 IP 地址,你就会发现有负载均衡的效果:

String url = "http://xxx-service/hello";

for (int i = 0; i < 10; i++) {
    String str = template.postForObject(url, null, String.class);
    System.out.println(str);
}
Copied!

这里之所以能出现负载均衡现象是因为:

  1. 你的项目间接引用到了 Ribbon ,spring-cloud-starter-alibaba-nacos-discovery 包中已经包含了 ribbon;

    当然,你也可以单独引用(spring-cloud-starter-netflix-eureka-ribbon),不过,逻辑上这就是一句啰嗦的废话。

  2. 你的项目是一个 Nacos Client 项目,当它启动时,它会去 Nacos Server 上拉取已注册的所有的服务的 IP 地址等相关信息;

  3. 当你通过 RestTemplate 以『application name』为依据发出请求时,Ribbon 会参与进来,会将 application-name『替换』成上述第 2 步中查到的 IP 地址。

  4. 在『替换』的过程中,它以某种规则轮流使用同一个服务的多个实例的 IP 地址,从而实现负载均衡效果。这里的『某种规则』指的就是负载策略。

  5. 默认的负载策略是轮循。

注意

使用了 @LoadBalanced 注解之后,RestTemplate 的 URL 中不能再出现 IP:Port,哪怕只有一个单点,也只能出现目标服务在 Nacos Server 上注册的 name ,反之亦然。

Spring Cloud Ribbon 不像注册中心、配置中心、网关那样独立部署、运行。它更像是一个工具类(库),『嵌套』在各个组件中配合其它组件使用。

#2. Ribbon 的执行原理

Ribbon 是以拦截器的方式『参与』到 RestTemplate 的请求发送功能中的。

当我们 RestTemplate 执行请求操作时,就会被 Ribbon 的拦截器拦截。Ribbon 通过 request.getURI() 方法求能获得 RestTemplate 所发出的请求的 URI,不过此时 URI 中的内容是目标服务的 application-name 而非 IP 。

由于我们的项目作为 Nacos Client 能够从 Nacos Server 上拉取到 application-name 所对应的一个(或多个)IP 地址及端口信息,因此,Ribbon 会根据某种『规则』(算法)获取到目标服务的真实的访问路径。

最后,Ribbon 放行 RestTemplate,让它向目标服务继续发起请求(并获取返回)

#3. 负载均衡策略和 IRule 接口

前面提到过的『规则』、『算法』就是『负载均衡策略』。

Ribbon 内置了 8 种负载均衡策略(其实是 7 种),它们都直接或间接实现了 IRule 接口:

其中常见的有:

策略说明
RandomRule 随机策略。随机选择目标服务的实例。
RoundRobinRule 默认 轮询策略。按顺序循环选择目标服务的实例。
WeightedResponseTimeRule 根据响应时间分配一个 Weight(权重),响应时间越长,Weight 越小,被选中的可能性越低。

这个策略以前版本中被称作 ResponseTimeWeightedRule 。
BestAvailablRule 这种策略下,Ribbon 会观测、统计目标服务的各个实例的并发量。

当再次发起对目标服务的访问时,Ribbon 会先过滤掉因为多次访问故障而被标记为 Error 的 实例。然后选择一个并发量(ActiveRequestCount)最小的实例发起访问。

俗话说就是:先去掉不能干活的,然后在能干活的里面找一个最闲的。

说明

上述 4 种策略简单高效,使用较多。而 AvailabilityFilteringRule 和 ZoneAvoidanceRule 策略需要结合断路、超时等参数配置,使用起来比较复杂,容易进坑,所以使用较少。

Ribbon 默认的负载均衡策略是:轮询 。如果我们想调整一下负载均衡策略,可以通过如下的配置。在『服务消费者』的服务中(即使用 RestTemplate 发起请求的一方),做 Ribbon 负载均衡策略的调整。

目前最简单的方式就是:

xxx-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Copied!

注意

以上配置的仅针对 application-name 为 xxx-service 的微服务有效。即,若你要调用多个不同的微服务,你要对它们一一配置。

#4. Ribbon 的饥饿加载

默认情况下,服务消费方调用服务提供方接口的时候,第一次请求会慢一些,甚至会超时,而之后的调用就没有问题了。

这是因为 Ribbon 进行客户端负载均衡的 Client 并不是在服务启动的时候就初始化好的,而是在调用的时候才会去创建相应的 Client,所以第一次调用的耗时不仅仅包含发送HTTP请求的时间,还包含了创建 RibbonClient 的时间,这样一来如果创建时间速度较慢,同时设置的超时时间又比较短的话,很容易就会出现上面所描述的现象。

你可以通过启用 Ribbon 的饥饿加载(即,立即加载)模式,并指定在项目启动时就要加载的服务:

ribbon:
  eager-load:
    enabled: true   # 开启饥饿加载,即立即加载
    clients: aservice-sms, xxx, xxx  # 需要饥饿加载的服务
Copied!

#5. Ribbon 的超时和超时重试

Ribbon 是有超时设置,以及超时之后的重试功能的。早年,Ribbon 的超时设置和重试设置的配置方式一直在变动,因此有很多『配置无效』的现象,十分诡异。

ribbon:
  restclient:
    enabled: true       # ribbon 和 RestTemplate 整合使用
  ReadTimeout: 1000     # 请求处理的超时时间,默认值 5000 。
  MaxAutoRetries: 2     # 最大重试次数,默认值 0 。
  MaxAutoRetriesNextServer: 2   # 切换实例的重试次数,默认值 1 。
Copied!

Ribbon 的超时重试总次数是

(1 + MaxAutoRetries ) x (1 + MaxAutoRetriesNextServer)
Copied!

#6. Ribbon 整合 RestTemplate 底层原理概述

RestTemplate 有拦截器机制,类似于 Spring MVC 的拦截器机制,在 RestTemplate 去做 “真正的工作” 前,会先执行其所有的拦截器,然后才开始 “干正事” 。

很显然,默认情况下 RestTemplate 没有任何拦截器,而在和 Ribbon 整合后,Ribbon 为 RestTemplate 添加了一个拦截器,其作用就是向 Ribbon 组件 “要” 一个真实可用的 IP 和端口( 根据服务名 “要” ),这样一来,在随后 “干正事” 时,RestTemplate 就可以向目标发起 HTTP 请求,并获取响应。

在 Ribbon 为 RestTemlate 添加的拦截器的工作过程中,Riibon 的负载均衡配置就在这个环节起作用,根据各种配置策略,返回不同实例的服务地址。

posted on 2021-11-29 14:46  迎着风跑  阅读(81)  评论(0编辑  收藏  举报