1、Ribbon背景介绍
Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单来说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。
现在的很多RPC框架,向Duobbo、JSF都是采用类似思想进行实现的。
2、常见负载均衡方案
目前主流的负载均衡方案可分成两类。
2.1 集中式负载均衡
一种是集中式LB, 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx, 由该设施负责把访问请求通过某种策略转发至服务的提供方。
2.2 进程式负载均衡
另一种是进程内LB,将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon就属于后者,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
3、Ribbon的核心组件
Ribbon 主要有6大功能组件:LoadBalance、ServerList、Rule、Ping、ServerListFilter、ServerListUpdater。
3.1 负载均衡器 LoadBalancer
用于管理负载均衡的组件。初始化的时候通过加载 YMAL 配置文件创建出来的。
3.2 服务列表 ServerList
ServerList 主要用来获取所有服务的地址信息,并保存到本地。根据获取服务信息的方式不同,又分为静态存储和动态存储。
※静态存储
从配置文件中获取服务节点列表并存储到本地。
※动态存储
从注册中心获取服务节点列表并存储到本地。
3.3 服务列表过滤 ServerListFilter
将获取到的服务列表按照过滤规则过滤。通过 Eureka 的分区规则对服务实例进行过滤。
比较服务实例的通信失败数和并发连接数来剔除不够健康的实例,根据所属区域过滤出同区域的服务实例。
3.4 服务列表更新 ServerListUpdater
服务列表更新就是 Ribbon 会从注册中心获取最新的注册表信息。是由这个接口 ServerListUpdater 定义的更新操作。而它有两个实现类,也就是有两种更新方式:
1) 通过定时任务进行更新。由这个实现类 PollingServerListUpdater 做到的。
2)利用 Eureka 的事件监听器来更新。由这个实现类 EurekaNotificationServerListUpdater 做到的。
3.5 心跳检测 Ping
IPing 接口类用来检测哪些服务可用。如果不可用了,就剔除这些服务。实现类主要有这几个:PingUrl、PingConstant、NoOpPing、DummyPing、NIWSDiscoveryPing。心跳检测策略对象 IPingStrategy,默认实现是轮询检测。
3.6 负载均衡策略 Rule
Ribbon 的负载均衡策略和之前讲过的负载均衡策略有部分相同,见第4节,看下Ribbon有哪些负载均衡策略。
4、Ribbon提供的主要负载均衡策略介绍
4.1 简单轮询负载均衡(RoundRobin)
以轮询的方式依次将请求调度不同的服务器,即每次调度执行i = (i + 1) mod n,并选出第i台服务器。轮流依次请求不同的服务器。优点是无需记录当前所有连接的状态,无状态调度;
4.2 随机负载均衡 (Random)
随机选择状态为UP的Server。随机选择服务器。适合并发比较大的场景。
4.3 加权响应时间负载均衡 (WeightedResponseTime)
每个服务按响应时长自动分配权重,响应时间越长,权重越低,被选中的概率越低;
4.4 区域感知轮询负载均衡(ZoneAvoidanceRule)
更倾向于选择发出调用的服务所在的托管区域内的服务,降低延迟,节省成本。Spring Cloud Ribbon 中默认的策略;区域感知负载均衡(ZoneAvoidanceRule):更倾向于选择发出调用的服务所在的托管区域内的服务,降低延迟,节省成本。Spring Cloud Ribbon 中默认的策略;
4.5 重试负载均衡(RetryRule)
通过轮询均衡策略选择一个服务器,如果请求失败或响应超时,可以选择重试当前服务节点,也可以选择其他节点;
4.6 高可用均衡(BestAvailableRule)
忽略请求失败的服务器,尽量找并发比较低的服务器。注意:这种会给服务器集群带来成倍的压力;
5、Spring Cloud Ribbon是什么
既然有了Ribbon, 怎么又出来一个Spring Cloud Ribbon呢?
Spring Cloud是基于Netflix Ribbon实现的一套客户端负载均衡的工具。它是一个基于HTTP和TCP的客户端负载均衡器。它可以通过在客户端中配置ribbonServerList来设置服务端列表去轮询访问以达到均衡负载的作用。
通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,无需单独部署。
6、准备工作
6.1 创建3个服务
本次项目示例,改造第一篇文章中的项目,使用spring-cloud-eureka-service作为服务注册中心,spring-cloud-eureka-client,复制3分,项目名称依次修改:
spring-cloud-eureka-client-1;
spring-cloud-eureka-client-2;
spring-cloud-eureka-client-3;
分别添加各自依赖,
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
6.2 添加启动类和Controller
在3个项目中:分别添加各自启动类和Controller类,并使用ribbon做均衡需要测试需要使用到。
代码示例(spring-cloud-eureka-client-1 为例),
//启动类
package com.xintu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient//代表自己是一个服务提供方
public class SpringCloudEurekaClient1App { //其他两个类名为:SpringCloudEurekaClient2App 、SpringCloudEurekaClient3App
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaClient1App.class, args);
}
}
// controller
package com.xintu.springcloud.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/xintu")
public class HelloWorldController {
@RequestMapping("/hello")
public String helloWorld(String s){
System.out.println("传入的值为:"+s);
return "传入的值为-1:"+s; //其他两个改为传入的值为-2,传入的值为-3
}
}
6.3 修改YML配置文件
在3个项目中,分别添加yml并修改server: port:端口依次为8711,8712,8713
#指定应用名称
spring:
application:
name: eureka-service
server:
port: 8711 # 服务提供方
# 指定当前eureka客户端的注册地址,
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://${eureka.instance.hostname}:8700/eureka
7、添加Ribbon消费者
7.1 添加依赖
新建 module, 命名为springcloud-ribbon-consumer,
在pom文件中,添加如下依赖,
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.xintu.springcloud</groupId>
<artifactId>springcloud-common</artifactId> //这个为7.2节的module
<version>0.0.1-SNAPSHOT</version>
</dependency>
7.2 添加公共负载平衡module(提高模块的代理复用性)
新建module, 命名为springcloud-common。
package com.xintu.springcloud.configuration;
import com.netflix.loadbalancer.BestAvailableRule;
import com.netflix.loadbalancer.IRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* 配置负载均衡模板
*/
@Configuration
public class RestTemplateConfiguration {
/**
* 公共负载均衡
* @return
*/
@LoadBalanced //b表示开启负载均衡
@Bean
public RestTemplate restTemplate(){
return new RestTemplate(simpleClientHttpRequestFactory());
}
/**
* 自定义配置ribbon负载均衡算法
* @return
*/
@Bean
public IRule myRule(){
//return new RoundRobinRule();//轮询
//return new RetryRule();//重试
return new BestAvailableRule();
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory
= new SimpleClientHttpRequestFactory();
//设置超时时间
factory.setReadTimeout(5000);//ms
factory.setConnectTimeout(15000);//ms
return factory;
}
}
7.3 创建启动类和controller类
// 启动类
package com.xintu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author XinTu
* @classname SpringCloudRibbonConsumerApp
* @description TODO
* @date 2023年03月25日 14:30
*/
@SpringBootApplication
@EnableDiscoveryClient //向服务注册中心注册
public class SpringCloudRibbonConsumerApp {
public static void main(String[] args) {
SpringApplication.run(SpringCloudRibbonConsumerApp.class, args);
}
}
// controller类
package com.xintu.springcloud.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/xintu")
public class RibbonConsumerController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/hello")
public String helloWorld(){
return restTemplate.getForEntity("http://eureka-service/xintu/hello?s=123", String.class).getBody(); //eureka-service 这个为前面三个服务名,无需加ip和端口
}
}
7.4 添加YML配置完整配置 application.yml如下,
#指定应用名称
spring:
application:
name: ribbon-consumer
server:
port: 8866 # 消费方端口
# 指定当前eureka客户端的注册地址,
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://${eureka.instance.hostname}:8700/eureka
7.5 测试服务
项目启动顺序:
1、springcloud-config-server
2、spring-cloud-eureka-client-1
3、spring-cloud-eureka-client-2
4、spring-cloud-eureka-client-3
5、springcloud-ribbon-consumer
启动该工程后,先访问服务注册中心,查看服务是否都已注册成功:
确认没问题后,开始验证,http://localhost:8866/xintu/hello。可以在浏览器频繁刷新这个地址,会发先返回内容有所不同。说明负载均衡是生效的。
8、各种负载均衡对比
Nginx与Ribbon负载均衡的区别
8.1 服务器端负载均衡Nginx
nginx是客户端所有请求统一交给nginx,由nginx进行实现负载均衡请求转发,属于服务器端负载均衡。
既请求有nginx服务器端进行转发。
8.3 客户端负载均衡Ribbon
Ribbon是从eureka注册中心服务器端上获取服务注册信息列表,缓存到本地,让后在本地实现轮训负载均衡策略。
既在客户端实现负载均衡。
应用场景的区别:
Nginx适合于服务器端实现负载均衡 比如Tomcat ,Ribbon适合与在微服务中RPC远程调用实现本地服务负载均衡,比如Dubbo、SpringCloud中都是采用本地负载均衡。
以上!