目录
负载均衡
什么是负载均衡
负载均衡的种类
负载均衡算法
负载均衡算法分类
常见负载均衡算法及实现
1、轮询
2、加权轮询算法
3、负载最低优先(可以从连接数、http请求数、cpu负载等情况来)
4、Hash 类算法
常用负载均衡算法应用场景
什么是Ribbon
什么是Ribbon
用于生产的Ribbon的子模块
ribbon-core:定义负载均衡接口、客户端接口、内置的负载均衡实现等API。
ribbon-eureka :提供eureka客户端实现负载均衡的API。
ribbon-httpclient:对Apache的HttpClient进行封装,该模块提供了含有负载均衡功能的REST客户端。
Ribbon整合Eureka
第一个Ribbon实例
第一个Ribbon实例的架构图
服务注册中心
改造服务提供者
创建PortController类
搭建含有Ribbon的服务消费者
1、创建项目,引入依赖
2、在application.yml文件进行相关配置
3、在项目启动类添加@EnableEurekaClient或@EnableDiscoveryClient注解
4、创建配置类
5、创建Service类
6、创建Controller类
测试运行
Ribbon的工作原理
微服务架构中,负载均衡是一个必不可少的技术。负载均衡决定着整个服务集群的性能和稳定。Spring Cloud体系中加入了Netflix公司的很多优秀产品,其中一个就是实现客户端负载均衡的Ribbon。
负载均衡
什么是负载均衡
负载均衡是高可用网络基础架构的一个关键组成部分,有了负载均衡,我们通常可以将我们的应用服务器部署多台,然后通过负载均衡将用户的请求分发到不同的服务器用来提高网站、应用、数据库或其他服务的性能以及可靠性。
负载均衡的种类
负载均衡分为硬件负载均衡和软件负载均衡两种,具体介绍如下:
硬件负载均衡的解决方案就是直接在服务器和外部网络间安装负载均衡设备,通常这种设备称为负载均衡器。由专门的设备完成专门的任务,独立于操作系统,整体性能得到大量提高,加上多样化的负载均衡策略,智能化的流量统计,可达到最佳的负载均衡效果。
软件负载均衡的解决方案是指在一台或多台服务器相应的操作系统上安装一个或多个附加软件来实现负载均衡,如DNS Load Balance,CheckPoint Firewall-1 ConnectControl等,它的优点是基于特定环境,配置简单,使用灵活,成本低廉,可以满足一般的负载均衡需求。
负载均衡算法
负载均衡算法分类
任务平分类 负载均衡系统将收到的任务平均分配给服务器进行处理,这里的“平均”可以是绝对数量的平均,也可以是比例或者权重上的平均。负载均衡类 负载均衡系统根据服务器的负载来进行分配,这里的负载并不一定是通常意义上我们说的“CPU 负载”,而是系统当前的压力,可以用 CPU 负载来衡量,也可以用连接数、I/O 使用率、网卡吞吐量等来衡量系统的压力。性能最优类 负载均衡系统根据服务器的响应时间来进行任务分配,优先将新任务分配给响应最快的服务器。Hash 类 负载均衡系统根据任务中的某些关键信息进行 Hash 运算,将相同 Hash 值的请求分配到同一台服务器上。常见的有源地址 Hash、目标地址 Hash、session id hash、用户 ID Hash 等。
常见负载均衡算法及实现
1、轮询
负载均衡系统收到请求后,按照顺序轮流分配到服务器上。轮询是最简单的一个策略,无须关注服务器本身的状态。
2、加权轮询算法
负载均衡系统根据服务器权重进行任务分配,这里的权重一般是根据硬件配置进行静态配置的,加权轮询是轮询的一种特殊形式,其主要目的就是为了解决不同服务器处理能力有差异的问题。加权轮询算法是在轮询算法基础上,给每个节点赋予一个权重,从而使每个节点被访问到的概率不同,权重大的节点被访问的概率就高,权重小的节点被访问的概率就小。
3、负载最低优先(可以从连接数、http请求数、cpu负载等情况来)
负载最低优先的算法解决了轮询算法中无法感知服务器状态的问题,由此带来的代价是复杂度要增加很多。比如:最少活跃连接算法,顾名思义就是每一次访问都选择连接数最少的节点。因为不同节点处理请求的速度不同,使得同一个服务消费者同每一个节点的连接数都不相同。连接数大的节点,可以认为是处理请求慢,而连接数小的节点,可以认为是处理请求快。所以在挑选节点时,可以以连接数为依据,选择连接数最少的节点访问。但是最少连接数优先的算法要求负载均衡系统统计每个服务器当前建立的连接,其应用场景仅限于负载均衡接收的任何连接请求都会转发给服务器进行处理,否则如果负载均衡系统和服务器之间是固定的连接池方式,就不适合采取这种算法。例如,LVS 可以采取这种算法进行负载均衡,而一个通过连接池的方式连接 MySQL 集群的负载均衡系统就不适合采取这种算法进行负载均衡。负载最低优先算法基本上能够比较完美地解决轮询算法的缺点,因为采用这种算法后,负载均衡系统需要感知服务器当前的运行状态。当然,其代价是复杂度大幅上升负。载最低优先算法如果本身没有设计好,或者不适合业务的运行特点,算法本身就可能成为性能的瓶颈,或者引发很多莫名其妙的问题。所以负载最低优先算法虽然效果看起来很美好,但实际上真正应用的场景反而没有轮询(包括加权轮询)那么多。
4、Hash 类算法
负载均衡系统根据任务中的某些关键信息进行 Hash 运算,将相同 Hash 值的请求分配到同一台服务器上,这样做的目的主要是为了满足特定的业务需求。例如: (1)源地址 Hash 将来源于同一个源 IP 地址的任务分配给同一个服务器进行处理,适合于存在事务、会话的业务。 (2)ID Hash 将某个 ID 标识的业务分配到同一个服务器中进行处理,这里的 ID 一般是临时性数据的 ID(如 session id)。 (3)一致性hash 一致性hash算法,是通过某个hash函数,把同一个来源的请求都映射到同一个节点上。一致性hash算法最大的特点就是同一个来源的请求,只会映射到同一个节点上,可以说是具有记忆功能。只有当这个节点不可用时,请求才会被分配到相邻的可用节点上。
常用负载均衡算法应用场景
轮询算法:各个服务节点被访问的概率也基本相同,也主要应用在各个服务节点性能差异不大的情况下。加权轮询算法:在轮询算法基础上的改进,可以通过给每个节点设置不同的权重来控制访问的概率,因此主要被用在服务节点性能差异比较大的情况。比如经常会出现一种情况,因为采购时间的不同,新的服务节点的性能往往要高于旧的节点,这个时候可以给新的节点设置更高的权重,让它承担更多的请求,充分发挥新节点的性能优势。负载最低优先:由于复杂度和不可控制原因,实际应用还是很少的hash类算法:因为它能够保证同一个客户端的请求始终访问同一个服务节点,所以适合服务端节点处理不同客户端请求差异较大的场景。比如服务端缓存里保存着客户端的请求结果,如果同一客户端一直访问一个服务节点,那么就可以一直从缓存中获取数据。
什么是Ribbon
什么是Ribbon
Ribbon是Netflix开源的一款用于客户端负载均衡的软件工具,它在集群中为各个客户端的通信提供了支持,有助于控制HTTP和TCP客户端的行为,提供了很多负载均衡的算法,例如轮询,随机等,同时也可以实现自定义的算法。 在Spring Cloud 构建的微服务中,Ribbon作为服务消费者的负载均衡器,有两种使用方式,一种是与RestTemplate相结合,另一种是与Feign相结合,Feign已经默认集成了Ribbon。
用于生产的Ribbon的子模块
Ribbon包含很多子模块,但很多子模块没有用于生产环境,目前用于生产的Ribbon的子模块具体如下:
ribbon-core:定义负载均衡接口、客户端接口、内置的负载均衡实现等API。
ribbon-eureka :提供eureka客户端实现负载均衡的API。
ribbon-httpclient:对Apache的HttpClient进行封装,该模块提供了含有负载均衡功能的REST客户端。
Ribbon整合Eureka
在Spring Cloud 中,当Ribbon和Eureka配合使用时,Ribbon可从Eureka Server中获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提供者实例。Ribbon整合Eureka的结构示例如下图所示。
在上图中,搭建了一个Eureka服务器,三个服务提供者以及一个含有Ribbon的服务消费者。三个服务提供者向Eureka服务器注册服务,当多个URL向服务调用者发起请求时,基于Ribbon的负载均衡器能够有效地将请求分摊到不同的机器上。
第一个Ribbon实例
第一个Ribbon实例的架构图
服务注册中心
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
spring:
application:
name: eureka-server
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
instance:
hostname: localhost
改造服务提供者
创建PortController类
在项目eureka-provider和eureka-provider-another中新建controller包,并创建PortController类,该类能够返回当前项目的端口号。
@RestController
public class Controller {
@Value("${server.port}")
private String port;
@RequestMapping("port")
public String getPort(){
return "Hello World, I'm from port:"+port;
}
}
eureka-provider和eureka-provide-another配置
#eureka-provider
spring:
application:
name: eureka-provide
server:
port: 7006
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
--------------------------------------------------------------------------
#eureka-provider-another
spring:
application:
name: eureka-provide
server:
port: 7007
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
搭建含有Ribbon的服务消费者
1、创建项目,引入依赖
使用Spring Initializr方式创建一个名称为eureka-ribbon-client的Spring Boot项目添加Eureka Client、Ribbon和Web的依赖。
2、在application.yml文件进行相关配置
在全局配置文件application.yml进行相关配置,包括指定应用名称、端口号、服务注册地址等信息。
spring:
application:
name: eureka-ribbon-client
server:
port: 8764
eureka:
client:
serviceUrl:
defaultZone: http://localhost:7000/eureka/
3、在项目启动类添加@EnableEurekaClient或@EnableDiscoveryClient注解
在项目启动类EurekaRibbonClientApplication上添加@EnableEurekaClient或@EnableDiscoveryClient注解开启Eureka Client功能。
@SpringBootApplication
@EnableDiscoveryClient
public class EurekaRibbonConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaRibbonConsumerApplication.class, args);
}
}
4、创建配置类
新建config包,并在该包下创建RibbonConfig类,该类注入restTemplate的Bean,并在这个Bean中加上@LoadBalanced注解。
@Configuration
public class RibbonConfig {
@Bean //注入restTemplate()方法返回的RestTemplate对象
@LoadBalanced //使RestTemplate具备了负载均衡的能力
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
return restTemplateBuilder.build();
}
}
5、创建Service类
新建service包,并在该包下创建一个RibbonService类,在该类的hi()方法中使用restTemplate调用eureka-client的API接口。
@Service
public class RibbonService {
@Autowired
RestTemplate restTemplate;
public String hi(){
return restTemplate.getForObject("http://eureka-provide/port",String.class);
}
}
6、创建Controller类
新建controller包,并在该包下创建一个RibbonController类,在该类上添加@RsetController注解,将RibbonController 标注为一个Controller类。在类中写一个hi ()方法,调用RibbonService的hi()方法。
@RestController
public class RibbonController {
@Autowired
RibbonService ribbonService;
@RequestMapping("/hi")
public String hi(){
return ribbonService.hi();
}
}
测试运行
为了演示使用Ribbon的服务消费者负载均衡的效果,使用浏览器多次访问http://localhost:8764/hi,浏览器页面访问效果如图1和图2所示。
Ribbon的工作原理
前面我们使用Ribbon实现负载均衡时,基本用法是注入一个RestTemplate,并使用@LoadBalanced注解标注RestTemplate,从而使RestTemplate具备负载均衡的能力。
当Spring容器启动时,使用@LoadBalanced注解修饰的RestTemplate会被添加拦截器,拦截器中使用了LoadBalancerClient处理请求,从而达到负载均衡的目的。那么LoadBalancerClient内部是如何做到的呢?接下来我们通过源码分析的方式来剖析Ribbon负载均衡的工作原理。
介绍AbstractLoadBalancerRule抽象类
RoundRobinRule:系统默认的规则,通过简单的轮询服务列表来选择服务器,其他的规则在很多情况下,仍然使用 RoundRobinRule。
AvailabilityFilteringRule:该规则会忽略以下服务器:
无法连接的服务器:在默认情况下,如果 3 次连接失败,该服务器将会被置为 “短路”的状态,该状态将持续 30 秒,如果再次连接失败,“短路”状态的持续时间将会以几何级增加。可以通过修改niws.loadbalancer..connectionFailureCountThreshold 属性,来配置连接失败的次数。
并发数过高的服务器:如果连接到该服务器的并发数过高,也会被这个规则忽略,可以通过修改.ribbon.ActiveConnectionsLimit 属性来设定最高并发数。
WeightedResponseTimeRule:为每个服务器赋予一个权重值,服务器的响应时间越长,该权重值就是越少,这个规则会随机选择服务器,这个权重值有可能会决定服务器的选择。
ZoneAvoidanceRule:该规则以区域、可用服务器为基础,进行服务器选择。使用 Zone 对服务器进行分类,可以理解为机架或者机房。
BestAvailableRule:忽略“短路”的服务器,并选择并发数较低的服务器。
RandomRule:顾名思义,随机选择可用的服务器。
RetryRule:含有重试的选择逻辑,如果使用 RoundRobinRule 选择服务器无法连接,那么将会重新选择服务器。
推荐阅读
发表评论