博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringCloud Ribbon 负载均衡
阅读量:2421 次
发布时间:2019-05-10

本文共 10367 字,大约阅读时间需要 34 分钟。

目录

 

服务端负载均衡与客户端负载均衡

假设user-server要调用order-server:user-server -> order-server

是user-server向order-server发起调用请求,user-server是客户端,order-server是服务端,提供服务。

  • 如果在客户端(消费者)获取服务端(提供者)的节点列表、选择一个节点来使用,即在客户端实现的负载均衡,称为客户端负载均衡;
  • 如果用nginx之类的反向代理服务端,客户端向nginx发起请求,nginx通过负载均衡算法转发给服务端的某个节点,即在服务端实现的负载均衡,称为服务端负载均衡。

eureka client、nacos client、zuul、gateway都内置了ribbon,以实现客户端的负载均衡。

 

springcloud的服务调用流程

eg. user-service的某个节点要调用order-service

1、向内置的ribbon发起调用order-service的负载均衡请求

2、ribbon查询缓存中有没有order-service的节点列表,有就直接使用,没有就从注册中心拉取

3、ribbon使用指定的负载均衡算法从节点列表中选取一个节点

4、user-service向返回的order-service节点发起调用

 

对于一个提供者服务,ribbon会在缓存中维护2个List:

  • 该服务所有节点的信息列表,从注册中心拉取,后续ribbon会轮询注册中心此节点列表是否有更新,如果有更新会从注册中心重新获取拉取。
  • 该服务有效节点的信息列表:ribbon会将拉取到的节点列表复制一份作为有效节点列表,定时检测有效节点列表中的节点是否还有效,若无效则从有效节点列表中删除该节点。

调用提供者时,ribbon先从本地缓存的该服务可用节点列表中获取一个节点,如果列表中没有可用的节点,再从注册中心更新该服务的节点列表。

 

ribbon内置的负载均衡策略(7种)

1、RoundRobinRule  轮询(默认策略)

轮询适合节点性能都差不多的情况。从前往后依次轮询节点列表中的每个节点,谁空闲就调用谁。
 

2、RetryRule  重试

先轮询,如果未获取到节点,则在指定的时间内(默认500ms)重试。
 

3、RandomRule  随机

4、BestAvailableRule  最可用,选择负载最小的节点

5、AvailabilityFilteringRule  可用过滤

先过滤掉处于断路状态(断路器打开)、负载很大的节点,再使用轮询。

6、ZoneAvoidanceRule  根据大区性能、节点可用性综合筛选

7、WeightedResponseTimeRule  权重响应时间

根据节点的平均响应时间计算权重,响应快的权重大,被选中的机率就越大。

 

设置负载均衡策略

eureka client的依赖中已经包含了ribbon的依赖,不用额外添加依赖。

负载均衡策略是在消费者中设置的,有2种方式

 

配置文件方式

#设置调用order-service的负载均衡策略order-service:  ribbon:    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

 

代码方式

@SpringBootApplication@EnableEurekaClientpublic class UserServiceApplication {
//如果直接使用RestTemplate进行服务调用,需要加上此方法 @LoadBalanced @Bean public RestTemplate restTemplate() {
return new RestTemplate(); } //设置负载均衡策略 @Bean public RandomRule getRule(){
return new RandomRule(); } public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args); }}

配置文件方式设置的负载均衡策略只对指定的服务调用有效,代码设置方式对所有的服务调用都有效。

如果要对所有服务调用都设置相同的负载均衡策略,使用代码设置方式;如果要针对各个提供者设置不同的负载均衡策略,使用yml配置方式。

一般使用默认的轮询即可。

 

自定义负载均衡策略

通用示例

import com.netflix.client.config.IClientConfig;import com.netflix.loadbalancer.AbstractLoadBalancerRule;import com.netflix.loadbalancer.ILoadBalancer;import com.netflix.loadbalancer.Server;import java.util.List;/** * 自定义的负载均衡策略 * 继承AbstractLoadBalancerRule,重写choose()方法 */public class MyRule extends AbstractLoadBalancerRule {
@Override public void initWithNiwsConfig(IClientConfig iClientConfig) {
} /** * @param o ribbon读取到的目标服务的所有节点的信息,会自动传入。很多时候都是ribbon缓存的数据,可能含有无效的节点 * @return 选择使用的节点 */ @Override public Server choose(Object o) {
//获取所要调用服务的负载均衡器 ILoadBalancer loadBalancer = this.getLoadBalancer(); //获取目标服务的所有节点,可能包含无效节点 List
allList = loadBalancer.getAllServers(); //获取目标服务的所有可用节点 List
upList = loadBalancer.getReachableServers(); for (Server server : upList) {
//有效且空闲 if (server.isAlive() && server.isReadyToServe()) {
return server; } } return null; }}

配置方式和内置策略的配置方式相同。

相比于eureka,nacos提供了更多的属性、方法,用nacos做注册中心可以自定义更多的负载均衡策略,比如优先调用当前集群中的服务、调用指定版本的服务。

 

nacos做注册中心之优先调用当前集群中的服务

此处的集群可以理解为数据中心,比如按地域划分为华东、东北、西南…每个区域都建立全套服务器,eg. tomcat集群中包含多个user-server节点、order-server节点,user-server调用order-server时优先调用当前区域集群中的order集群节点。

spring:  application:    name: user-server  cloud:    nacos:      discovery:        server-addr: 127.0.0.1:8848        #部署服务时需要给节点指定所属的集群        cluster-name: SW
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;import com.alibaba.cloud.nacos.NacosServiceManager;import com.alibaba.cloud.nacos.ribbon.NacosServer;import com.alibaba.nacos.api.exception.NacosException;import com.alibaba.nacos.api.naming.NamingService;import com.alibaba.nacos.api.naming.pojo.Instance;import com.alibaba.nacos.client.naming.core.Balancer;import com.netflix.client.config.IClientConfig;import com.netflix.loadbalancer.AbstractLoadBalancerRule;import com.netflix.loadbalancer.BaseLoadBalancer;import com.netflix.loadbalancer.Server;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.util.CollectionUtils;import java.util.List;import java.util.Objects;import java.util.stream.Collectors;/** * 自定义的负载均衡策略 * 继承AbstractLoadBalancerRule,重写choose()方法 */@Slf4jpublic class LocalClusterRule extends AbstractLoadBalancerRule {
@Autowired private NacosDiscoveryProperties discoveryProperties; @Override public void initWithNiwsConfig(IClientConfig iClientConfig) {
} /** * @param o ribbon读取到的目标服务的所有节点的信息,会自动传入。很多时候都是ribbon缓存的数据,可能含有无效的节点 * @return 选择使用的节点 */ @Override public Server choose(Object o) {
NacosServiceManager nacosServiceManager = new NacosServiceManager(); NamingService namingService = nacosServiceManager.getNamingService(discoveryProperties.getNacosProperties()); //获取当前服务节点的集群名称 String clusterName = discoveryProperties.getClusterName(); //获取被调服务的负载均衡器 BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer(); //被调服务的名称 String name = loadBalancer.getName(); try {
//通过被调服务的名称筛选出所有健康的被调服务 List
allInstance = namingService.selectInstances(name, true); if (CollectionUtils.isEmpty(allInstance)){
log.error("注册中心没有可用的{}服务实例!",name); return null; } //过滤出与当前服务在同一集群中的被调服务 List
localInstance = allInstance.stream().filter(instance -> Objects.equals(instance.getClusterName(), clusterName) ).collect(Collectors.toList()); if (CollectionUtils.isEmpty(localInstance)){
log.warn("当前集群{}中没有可用的{}服务,将跨集群调用该服务",clusterName,name); }else{
allInstance = localInstance; } //使用nacos提供的方法随机选择一个节点 Instance instance = ExtendBalancer.getHostByRandomWeight(allInstance); //可用直接输出instanceId,instanceId中包含了ip、port、所属集群、所属分组、服务名称 log.info("选择使用的{}服务实例是{}集群的{}:{}",name,instance.getClusterName(),instance.getIp(),instance.getPort()); return new NacosServer(instance); } catch (NacosException e) {
log.error("ribbon使用自定义的负载均衡策略获取被调服务{}时发生异常",name); e.printStackTrace(); } return null; }}/** * Balancer的getHostByRandomWeight()权限时protected,继承改为public暴露出去 */class ExtendBalancer extends Balancer {
public static Instance getHostByRandomWeight(List
hosts){
return Balancer.getHostByRandomWeight(hosts); }}

 

nacos做注册中心之调用指定版本的服务

spring:  application:    name: user-server  cloud:    nacos:      discovery:        server-addr: 127.0.0.1:8848        metadata:          #指定当前服务版本          version: 1.0          #指定被调服务版本          targetVersion: 1.0
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;import com.alibaba.cloud.nacos.NacosServiceManager;import com.alibaba.cloud.nacos.ribbon.NacosServer;import com.alibaba.nacos.api.exception.NacosException;import com.alibaba.nacos.api.naming.NamingService;import com.alibaba.nacos.api.naming.pojo.Instance;import com.alibaba.nacos.client.naming.core.Balancer;import com.netflix.client.config.IClientConfig;import com.netflix.loadbalancer.AbstractLoadBalancerRule;import com.netflix.loadbalancer.BaseLoadBalancer;import com.netflix.loadbalancer.Server;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.util.CollectionUtils;import java.util.List;import java.util.Map;import java.util.Objects;import java.util.stream.Collectors;/** * 自定义的负载均衡策略 * 继承AbstractLoadBalancerRule,重写choose()方法 */@Slf4jpublic class VersionRule extends AbstractLoadBalancerRule {
@Autowired private NacosDiscoveryProperties discoveryProperties; @Override public void initWithNiwsConfig(IClientConfig iClientConfig) {
} /** * @param o ribbon读取到的目标服务的所有节点的信息,会自动传入。很多时候都是ribbon缓存的数据,可能含有无效的节点 * @return 选择使用的节点 */ @Override public Server choose(Object o) {
NacosServiceManager nacosServiceManager = new NacosServiceManager(); NamingService namingService = nacosServiceManager.getNamingService(discoveryProperties.getNacosProperties()); //获取目标服务版本 Map
metadata = discoveryProperties.getMetadata(); String targetVersion = metadata.get("targetVersion"); //获取被调服务的负载均衡器 BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer(); //被调服务的名称 String name = loadBalancer.getName(); try {
//通过被调服务的名称筛选出所有健康的被调服务 List
allInstance = namingService.selectInstances(name, true); if (CollectionUtils.isEmpty(allInstance)){
log.error("注册中心没有可用的{}服务实例!",name); return null; } //过滤出指定版本的被调服务实例 List
versionInstance = allInstance.stream().filter(instance -> Objects.equals(instance.getMetadata().get("version"), targetVersion) ).collect(Collectors.toList()); if (CollectionUtils.isEmpty(versionInstance)){
log.error("注册中心没有{}服务的{}版本实例)!", name, targetVersion); return null; } //使用nacos提供的方法随机选择一个节点 Instance instance = ExtendBalancer.getHostByRandomWeight(versionInstance); //可用直接输出instanceId,instanceId中包含了ip、port、所属集群、所属分组、服务名称 log.info("选择使用的{}服务实例是{}集群的{}:{}",name,instance.getClusterName(),instance.getIp(),instance.getPort()); return new NacosServer(instance); } catch (NacosException e) {
log.error("ribbon使用自定义的负载均衡策略获取被调服务{}时发生异常",name); e.printStackTrace(); } return null; }}/** * Balancer的getHostByRandomWeight()权限时protected,继承改为public暴露出去 */class ExtendBalancer extends Balancer {
public static Instance getHostByRandomWeight(List
hosts){
return Balancer.getHostByRandomWeight(hosts); }}

 

ribbon的饥饿加载

ribbon从注册中心获取提供者节点列表时,默认使用懒加载:第一次调用服务提供者时,才会从注册中心加载该服务提供者的节点列表,初次调用某个服务有点慢。

可以设置为饥饿加载,在应用(消费者)启动时就加载服务提供者节点列表

ribbon:  eager-load:    #开启饥饿加载。默认false    enabled: true    #指定要饥饿加载哪些服务提供者,其余提供者仍使用懒加载方式    clients: order-server,msg-server

转载地址:http://gzhlb.baihongyu.com/

你可能感兴趣的文章
Hadoop 3.0 新特性原理及架构分析-CSDN公开课-专题视频课程
查看>>
3小时掌握数据挖掘-CSDN公开课-专题视频课程
查看>>
Web 全栈全端技术体系与软件四层结构-CSDN公开课-专题视频课程
查看>>
AI学习挑战直播课:成功案例分享及行业趋势分析-CSDN公开课-专题视频课程
查看>>
【UI/UE设计师】banner设计原则-CSDN公开课-专题视频课程
查看>>
自然语言处理实战——LSTM情感分析-CSDN公开课-专题视频课程
查看>>
Gin使用的json包
查看>>
Gin的路由
查看>>
如何安全地退出goroutine
查看>>
context.Context
查看>>
优先队列
查看>>
redis深度历险学习笔记--基础与应用篇
查看>>
单链表翻转
查看>>
检查表达式中的括号是否匹配
查看>>
一道关于 goroutine 的面试题
查看>>
信号量的使用方法
查看>>
Redis 缓存穿透、击穿、雪崩
查看>>
RabbitMQ(1): docker-compose安装rabbitmq及简单使用Hello World
查看>>
利用序列化实现对象的拷贝
查看>>
is-a,has-a,like-a是什么
查看>>