一、什么是负载均衡?
(1)概念:
在基于微服务架构开发的系统里,为了能够提升系统应对高并发的能力,开发人员通常会把具有相同业务功能的模块同时部署到多台的服务器中,并把访问业务功能的请求均摊到这些服务器上,这种将请求均摊的做法便叫做负载均衡。
(2)负载均衡分为硬件负载均衡和软件负载均衡两种:
- 硬件负载均衡的解决方案就是直接在服务器和外部网络间安装负载均衡设备,通常这种设备称为负载均衡器。由专门的设备完成专门的任务,独立于操作系统,整体性能得到大量提高,加上多样化的负载均衡策略,智能化的流量统计,可达到最佳的负载均衡效果。
- 软件负载均衡的解决方案是指在一台或多台服务器相应的操作系统上安装一个或多个附加软件来实现负载均衡,如DNS Load Balance,CheckPoint Firewall-1 ConnectControl等,它的优点是基于特定环境,配置简单,使用灵活,成本低廉,可以满足一般的负载均衡需求。
那么如何才能实现把访问业务功能的请求均摊到这些服务器上呢?这里就要用到Ribbon组件了。
二、什么是Ribbon组件?
简单来说,Ribbon是一个客户端负载均衡器,提供多种负载均衡策略,以便在客户端选择最佳的服务实例。同时也是Spring Cloud Alibaba 体系中负责负载均衡的组件,是一组类库的集合。
它在Spring Cloud体系中扮演着以下几个角色:
(1)客户端负载均衡:
Ribbon在客户端进行负载均衡,而不是依赖服务器端的负载均衡器。
(2)服务调用:
与Eureka等服务发现组件集成,通过服务名称来调用不同的服务实例。可以保留服务结点访问的相关统计信息,这样可以避免向高延迟或低高故障的节点发送请求。
(3)定制策略:
允许用户定制负载均衡策略,如随机策略、轮询策略等。系统便会根据指定的负载均衡算法,从多个服务节点中选取一个结点出来发送请求得到对应的服务。
三、Ribbon的工作原理:
使用Ribbon实现负载均衡时,基本用法是注入一个RestTemplate,并使用@LoadBalanced注解标注RestTemplate,从而使RestTemplate具备负载均衡的能力。
- 当Spring容器启动时,使用@LoadBalanced注解修饰的RestTemplate会被添加拦截器,拦截器中使用了LoadBalancerClient处理请求,从而达到负载均衡的目的。
- LoadBalancerClient是Spring Cloud提供的一个非常重要的接口,它继承ServiceInstanceChooser接口,该接口的实现类是RibbonLoadBalanceClient,它们之间的关系如下图所示。
- LoadBalancerClient提供的两个execute()方法用于执行请求, reconstructURI()方法用于重构URL。
- RibbonLoadBalanceClient是LoadBalancerClient的实现类,它用来执行最终的负载均衡请求。其中,RibbonLoadBalanceClient的一个choose()方法用于选择具体的服务实例,其内部是通过getServer()方法交给ILoadBalancer完成的。
- ILoadBalancer是一个接口,该接口定义了一系列实现负载均衡的方法。ILoadBalancer接口的实现类结果如下图所示。
默认情况下,Ribbon使用的负载均衡策略是轮询,实际上,Ribbon提供了很多负载均衡算法,其中IRule接口就是所有负载均衡算法的父接口。
四、Ribbon在处理负载均衡时的核心组件:
(1)IRule:定义负载均衡规则的接口,不同的实现类提供不同的负载均衡策略。
(2)IPing:用于定期检查服务实例的健康状况,确保请求不会发送到不可用的实例。
(3)ServerList:维护可用服务实例的列表,可以从服务注册中心(如Eureka)动态获取。
(4)ServerListFilter:用于过滤服务实例列表,确保负载均衡器仅考虑健康和可用的实例。
(5)ILoadBalancer:管理服务实例列表,并根据负载均衡规则选择一个实例来处理请求。
五、Ribbon负载均衡策略:
- Round Robin Rule(轮询策略):按顺序循环选择每个服务实例,均衡地分配请求。实现了按照线性轮询的方式依次选择服务的功能。
- Weighted Response Time Rule(权重响应时间策略): 根据每个实例的响应时间和权重进行分配,响应时间越快,服务权重越大,被选中的概率越高,获得的请求也会越多。
- Availability Filtering Rule(可用性过滤策略): 简单来说就是,过滤掉不可用的或并发请求超过阈值的实例,选择健康的实例。使用Availability Predicate过滤由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数超过阀值的服务,然后对剩余的服务列表进行轮询。
- Random Rule(随机策略):该策略实现了从服务清单中随机选择一个服务的功能。
Least Concurrent Connections(最少并发策略):选择当前并发请求最少的实例,减轻高负载实例的压力。
- Best Available Rule(最佳可用策略):用于先过滤掉多次访问故障而处于断路跳闸状态的服务,然后选择一个并发量最小的服务。
- Client Config Enable Round Robin Rule(客户端配置启用轮询策略):该类是一个抽象类,该类本身没有实现什么特殊的处理逻辑,我们也不会直接使用该策略,但是通过BestAvailableRule和继承该策略默认实现了线性轮询,它的内部定义了一个RoundRobinRule策略,
- Zone Avoidance Rule(区域感知策略):它是PredicateBasedRule的具体实现类,其内部通过使用ZoneAvoidancePredicate和AvailabilityPredicate判断是否选择某一个服务,前者用于判断服务所在区域的性能是否可用,后者用于过滤掉连接数过多的服务。
- Predicate Based Rule:继承了ClientConfigEnableRoundRobinRule,其内部会先通过chooseRoundRobinAfterFiltering()方法筛选服务清单,然后以线性轮询的方式从过滤后的服务清单中选择一个服务。
六、实练操作:
Ribbon实现负载均衡:
(1)创建名为ServiceProvider1的服务项目:
添加依赖到pom文件.
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
在resources目录编写application.properties配置文件.
nacos.discovery.server-addr=127.0.0.1:8848 spring.application.name=ServiceProvider server.port=1111
编写启动类,加入注解,说明本项目所包含的服务方法是需要注册到Nacos注册中心.
@EnableDiscoveryClient @SpringBootApplication public class SpringBootApp { public static void main(String[] args) { SpringApplication.run(SpringBootApp.class, args); } }
编写Controller.java控制类,编写对外提供服务方法.
@RestController public class Controller { @RequestMapping("/callServiceByRibbon") public String callServiceByRibbon(){ return "return in Service1."; } }
(2)创建名为ServiceProvider2的服务项目:
修改pom文件.
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
修改application.properties配置文件.
nacos.discovery.server-addr=127.0.0.1:8848 spring.application.name=ServiceProvider server.port=2222
修改控制类的方法.
@RestController public class Controller { @RequestMapping("/callServiceByRibbon") public String callServiceByRibbon(){ return "return in Service2."; } }
(3)创建名为ServiceWithRibbon的maven项目:
添加依赖到pom文件.
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> </dependencies>
在启动类添加注解.
@EnableDiscoveryClient @SpringBootApplication public class SpringBootApp { public static void main(String[] args) { SpringApplication.run(SpringBootApp.class, args); } @LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
修改配置文件.
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 server.port=8080 spring.application.name=ServiceWithRibbon ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RoundRibbonRule
编写controller控制类.
@RestController public class Controller { @Resource private RestTemplate restTemplate; @RequestMapping("/callFuncByRibbon") public String callFuncByRibbon(){ return restTemplate.getForObject("http://ServiceProvider/callServiceByRibbon", String.class); } }