CodeAshen's blog CodeAshen's blog
首页
  • Spring Framework

    • 《剖析Spring5核心原理》
    • 《Spring源码轻松学》
  • Spring Boot

    • Spring Boot 2.0深度实践
  • Spring Cloud

    • Spring Cloud
    • Spring Cloud Alibaba
  • RabbitMQ
  • RocketMQ
  • Kafka
  • MySQL8.0详解
  • Redis从入门到高可用
  • Elastic Stack
  • 操作系统
  • 计算机网络
  • 数据结构与算法
  • 云原生
  • Devops
  • 前端
  • 实用工具
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
  • Reference
GitHub (opens new window)

CodeAshen

后端界的小学生
首页
  • Spring Framework

    • 《剖析Spring5核心原理》
    • 《Spring源码轻松学》
  • Spring Boot

    • Spring Boot 2.0深度实践
  • Spring Cloud

    • Spring Cloud
    • Spring Cloud Alibaba
  • RabbitMQ
  • RocketMQ
  • Kafka
  • MySQL8.0详解
  • Redis从入门到高可用
  • Elastic Stack
  • 操作系统
  • 计算机网络
  • 数据结构与算法
  • 云原生
  • Devops
  • 前端
  • 实用工具
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
  • Reference
GitHub (opens new window)
  • 剖析Spring5核心原理

  • Spring源码轻松学

  • Spring Boot 2.0深度实践

  • Spring-Cloud

  • Spring-Cloud-Alibaba

    • 第01章-简介
    • 第02章-Nacos服务发现
    • 第03章-Ribbon负载均衡
      • 3.1 负载均衡介绍
        • 3.1.1 服务端负载均衡
        • 3.1.2 客户端负载均衡
      • 3.2 Ribbon介绍
        • 3.2.1 Ribbon简介
        • 3.2.2 Ribbon组成
        • 3.2.3 Ribbon负载均衡规则
      • 3.3 Nacos整合Ribbon
      • 3.4 配置Ribbon
        • 3.4.1 负载均衡配置方式
        • 3.4.2 全局负载均衡配置
        • 3.4.3 Ribbon其他配置
        • 3.4.4 饥饿加载
      • 3.5 拓展:基于Nacos配置自定义负载均衡规则
        • 3.5.1 自定义基于nacos权重的IRule
        • 3.5.2 实现基于nacos权重,并且尽量调用同名集群下的服务
        • 3.5.3 基于元数据匹配的IRule
    • 第04章-Feign声明式HTTP客户端
    • 第05章-Sentinel集群容错
    • 第06章-RocketMQ消息队列
    • 第07章-Spring Cloud Gateway网关
    • 第08章-认证授权
    • 第09章-Nacos配置管理
    • 第10章-Sletuh调用链
  • Spring
  • Spring-Cloud-Alibaba
CodeAshen
2023-02-10
目录

第03章-Ribbon负载均衡

# 3.1 负载均衡介绍

负载均衡的两种方式

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

# 3.1.1 服务端负载均衡

image-20210129112535162

客户端请求服务端的nginx,nginx通过负载均衡算法决定将请求转发到哪台server

# 3.1.2 客户端负载均衡

image-20210129112654601

在调用端获取server列表,根据负载均衡算法决定去请求哪一台server

# 3.2 Ribbon介绍

# 3.2.1 Ribbon简介

Ribbon是Netflix开源的客户端侧负载均衡器。

引入Ribbon后架构将演进成如下所示:

image-20210129114015711

# 3.2.2 Ribbon组成

接口 作用 默认值
IClientConfig 读取配置 DefaultclientconfigImpl
IRu1e 负载均衡规则,选择实例 ZoneAvoidanceRule
IPing 筛选掉ping不通的实例 Dummyping
ServerList 交给Ribbon的实例列表 Ribbon:ConfigurationBasedServerList
Spring cloud Alibaba:NacosServerList
ServerListFilter 过滤掉不符合条件的实例 ZonePreferenceServerListFilter
ILoadBalancer Ribbon的入口 ZoneAwareLoadBalancer
ServerListUpdater 更新交给Ribbon的List的策略 PollingserverListupdater

# 3.2.3 Ribbon负载均衡规则

Ribbon内置有如下负载均衡规则:

规则名称 特点
AvailabilityFiteringRule 过滤掉一直连接失败的被标记为circuit tripped的后端Server,并过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个Server的运行状态
BestAvailableRule 选择一个最小的并发请求的Server,逐个考察Server,如果Server被tripped了,则跳过
RandomRule 随机选择一个Server
ResponseTimeWeightedRule 已废弃,作用同WeightedResponseTimeRule
RetryRule 对选定的负载均衡策略机上重试机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的server
RoundRobinRule 轮询选择,轮询index,选择index对应位置的Server
WeightedResponseTimeRule 根据响应时间加权,响应时间越长,权重越小,被选中的可能性越低
ZoneAvoidanceRule(默认) 复合判断Server所Zone的性能和Server的可用性选择Server,在没有Zone的环境下,类似于轮询(RoundRobinRule)

# 3.3 Nacos整合Ribbon

  1. 添加依赖

    spring-cloud-starter-alibaba-nacos-discovery 子依赖中有Ribbon,不需要再手动添加依赖。

  2. 写注解

    @Bean
    @LoadBalanced  //负载均衡注解
    public RestTemplate restTemplate() {
        RestTemplate template = new RestTemplate();
        return template;
    }
    
    1
    2
    3
    4
    5
    6
  3. 使用服务名访问

    private final RestTemplate restTemplate;
        
    public ShareDTO findById(Integer id) {
        // 直接通过spring.application.name指定的服务名user-center访问服务
        UserDTO userDTO = restTemplate.getForObject("http://user-center/users/{userId}", UserDTO.class, userId);
        // ······
    }
    
    1
    2
    3
    4
    5
    6
    7

# 3.4 配置Ribbon

# 3.4.1 负载均衡配置方式

方式一、Java代码方式配置

  1. 配置IRule

    /**
     * Ribbon负载均衡规则,此配置类必须不能被Spring注解扫描到,因为存在父子上下文问题
     * 如果此配置被Spring扫描到,则会变成全局配置,使用ribbon请求任意服务都会使用本规则
     */
    @Configuration
    public class RibbonConfig {
        @Bean
        public IRule ribbonRule() {
            return new RandomRule();
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  2. Java代码配置调用服务和访问规则

    /**
     * 使用java代码的方式配置ribbon
     * 配置负载均衡的服务,和负载均衡策略
     * 表示请求user-center服务时,会使用RibbonConfig指定的策略进行负载均衡
     */
    @Configuration
    @RibbonClient(name = "user-center", configuration = RibbonConfig.class)
    public class UserCenterRibbonConfig {
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

方式二、属性配置方式

配置格式:<clientName>.ribbon.NFLoadBalancerRuleClassName

如下配置之后,和上述Java代码配置效果相同,请求user-center时,会使用NFLoadBalancerRuleClassName指定的负载均衡规则

# 属性配置Ribbon负载均衡规则
user-center: 
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
1
2
3
4

两种配置方式对比

配置方式 优点 缺点
代码配置 基于代码,更加灵活 有小坑(父子上下文)
线上修改得重新打包、发布
属性配置 易上手
配置更加直观
线上修改无需重新打包、发布
优先级更高
极端场景下没有代码配置方式灵活

# 3.4.2 全局负载均衡配置

上一节配置方式是对指定的服务配置负载均衡规则,如果想对所有的服务都使用相同的负载均衡规则,可以使用全局负载均衡配置。有以下两种配置方式:

  • 方式一:让CempenentScan上下文重叠(强烈不建议使用)

  • 方式二:Java编码方式

    @Configuration
    @RibbonClients(defaultConfiguration = RibbonConfig.class)
    public class UserCenterRibbonConfig {
    }
    
    1
    2
    3
    4

# 3.4.3 Ribbon其他配置

配置格式:<clientName>.ribbon.<如下配置>

  • NFLoadBalancerClassName:ILoadBalancer 实现类
  • NFLoadBalancerRuleClassName:IRule实现类
  • NFLoadBalancerPingclassName:IPing 实现类
  • NIWSServerListclassName:ServerList实现类
  • NIWSServerListFilterClassName:ServerListFilter实现类

# 3.4.4 饥饿加载

ribbon默认懒加载,第一次请求远程服务才会加载,导致第一次访问慢,可以开启饥饿加载:

ribbon:
  eager-load:
    enabled: true
    clients: user-center,content-center
1
2
3
4

# 3.5 拓展:基于Nacos配置自定义负载均衡规则

# 3.5.1 自定义基于nacos权重的IRule

/**
 * 自定义基于Nacos权重的ribbon IRule
 * 自定义IRule可以实现IRule接口或继承AbstractLoadBalancerRule抽象类
 * 权重可以在nacos server控制台设置
 */
@Slf4j
public class NacosWeightedRule extends AbstractLoadBalancerRule {
    
    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;
    
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }

    @Override
    public Server choose(Object key) {
        try {
            // 获取负载均衡器
            BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
            // 获取请求的服务名
            String name = loadBalancer.getName();
            // 拿到服务发现相关的API
            NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
            // nacos client自动通过基于权重的负载均衡算法,选择一个服务实例
            Instance instance = namingService.selectOneHealthyInstance(name);
            log.info("选择实例是 {}:{}", instance.getIp(), instance.getPort());
            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("获取服务实例出错");
            return null;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

参考:nacos权重负载均衡实现 (opens new window)

# 3.5.2 实现基于nacos权重,并且尽量调用同名集群下的服务

/**
 * 自定义IRule,尽量调用同集群下的服务,并且基于权重
 */
@Slf4j
public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;
    
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }

    @Override
    public Server choose(Object key) {
        try {
            // 拿到配置文件中的集群名称 Shanghai
            String clusterName = nacosDiscoveryProperties.getClusterName();
            // 想要请求的微服务的名称
            BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
            String name = loadBalancer.getName();
            // 拿到服务发现的相关API
            NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();

            // 1. 找到指定服务的所有实例 A
            List<Instance> instances = namingService.selectInstances(name, true);
            // 2. 过滤出相同集群下的所有实例 B
            List<Instance> sameClusterInstances = instances.stream()
                    .filter(instance -> Objects.equals(instance.getClusterName(), clusterName))
                    .collect(Collectors.toList());
            // 3. 如果B是空,就用A
            List<Instance> instancesToBeChosen = new ArrayList<>();
            if (CollectionUtils.isEmpty(sameClusterInstances)) {
                instancesToBeChosen = instances;
                log.warn("发生跨集群的调用, name = {}, clusterName = {}, instances = {}",
                        name, clusterName, instances);
            } else {
                instancesToBeChosen = sameClusterInstances;
            }
            // 4. 基于权重的负载均衡算法,返回1个实例
            Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToBeChosen);
            log.info("选择的实例是 port = {}, instance = {}", instance.getPort(), instance);
            
            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("获取服务实例出错", e);
            return null;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

# 3.5.3 基于元数据匹配的IRule

参考:[扩展Ribbon支持基于元数据的版本管理](

编辑 (opens new window)
上次更新: 2023/06/04, 12:34:19
第02章-Nacos服务发现
第04章-Feign声明式HTTP客户端

← 第02章-Nacos服务发现 第04章-Feign声明式HTTP客户端→

最近更新
01
第01章-RabbitMQ导学
02-10
02
第02章-入门RabbitMQ核心概念
02-10
03
第03章-RabbitMQ高级特性
02-10
更多文章>
Theme by Vdoing | Copyright © 2020-2023 CodeAshen | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式