微服务架构 | 4.1 基于 Ribbon 的负载均衡详解

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
注册配置 MSE Nacos/ZooKeeper,182元/月
简介: Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡的工具;提供客户端的软件负载均衡算法和服务调用;

前言

参考资料
《Spring Microservices in Action》
《Spring Cloud Alibaba 微服务原理与实战》
《B站 尚硅谷 SpringCloud 框架开发教程 周阳》

Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡的工具;提供客户端的软件负载均衡算法和服务调用;


1. Ribbon 基础知识

1.1 Ribbon 是什么

  • Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡的工具;提供客户端的软件负载均衡算法和服务调用;
  • Ribbon 客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出 Load Balancer(简称LB)后面所有的机器,Ribbon 会自动基于某种规则(如简单轮询,随机连接等)去连接这些机器;
  • 可以很容易使用Ribbon实现自定义的负载均衡算法;

1.2 与 Ribbon 交互的三种级别层次

  • Spring DiscoveryClient:提供了对 Ribbon 和 Ribbon 中缓存的注册服务的最低层次访问;
  • 启用了 RestTemplate 的 Spring DiscoveryClient
  • Netflix Feign 客户端

1.3 Ribbon在工作时分成两步

Ribbon 架构图

  • 第一步先选择 EurekaServer,它优先选择在同一个区域内负载较少的 server;

第二步再根据用户指定的策略,在从 server 取到的服务注册列表中选择一个地址;

1.4 服务的提供者与消费者

  • 提供者:服务提供者将自己注册进注册中心,让消费者发现;在本例中有多个提供者给消费者提供服务;
  • 消费者:消费者使用服务发现,找到提供者并调用提供者服务;在本例中只有一个消费者在多个提供者中选出一个为自己服务;

1.5 Ribbon 核心组件 IRule

IRule 负载均衡

  • 根据特定算法中从服务列表中选取一个要访问的服务;
  • 定义了负载均衡的方式;
  • 有以下几种负载均衡的实现方式:

    • RoundRobinRule:轮询;
    • RandomRule:随机;
    • RetryRule:先按照 RoundRobinRule 的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务;
    • WeightedResponseTimeRule:对 RoundRobinRule 的扩展,响应速度越快的实例选择权重越大,越容易被选择;
    • BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务;
    • AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例;
    • ZoneAvoidanceRule:默认规则,复合判断 server 所在区域的性能和 server 的可用性选择服务器;


2. 服务消费者获取提供者的三个层次示例

2.1 引入 pom.xml 依赖

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
  • 如果使用 Eureka 作为注册中心,则不用引入该依赖,因为 Eureka 的依赖里包含 ribbon 相关依赖 jar 包;

2.2 使用 Spring DiscoveryClient 查找服务实例

2.2.1 在主程序类上添加注解

  • @EnableDiscoveryClient:表明可以被注册中心发现。是 Sring Cloud 的触发器,其作用是使应用程序能够使用 DiscoveryClient 和 Ribbon 库;

2.2.2 使用 DiscoveryClient 查找信息

在服务消费者的 client 包下;
@Component
public class ProviderDiscoveryClient {

    //自动注入 DiscoveryClient 类,该类用于与 Ribbon 交互
    @Autowired
    private DiscoveryClient discoveryClient;

    public Provide getProvide(String providerId) {
        RestTemplate restTemplate = new RestTemplate();
        //获取服务提供者的所有实例列表,ServiceInstance 用于保存关于服务的特定实例(包括主机名、端口荷 URL)
        List<ServiceInstance> instances = discoveryClient.getInstances("provider-instance-name");

        if (instances.size()==0) return null;
        //检索要调用的服务端点
        String serviceUri = String.format("%s/providers/%s",instances.get(0).getUri().toString(), providerId);

        //使用标准的 Spring REST 模板类去调用服务
        ResponseEntity< provider > restExchange =
                restTemplate.exchange(
                        serviceUri,
                        HttpMethod.GET,
                        null, Provider.class, providerId);
        
        return restExchange.getBody();
    }
}
  • 这种方法存在以下问题:

    • 没有利用 Ribbon 的客户端负载均衡,调用哪个服务实例需要开发人员定义;
    • 开发人员必须构建一个用来调用服务的 URL;
    • 实例化 ResTemplate 类,不符合 Spring IoC 规范;
  • 结合本篇《5. 本地负载均衡器的实现(消费者)》即可用到客户端负载均衡,即:开发人员定义了本地负载均衡器来实现了负载均衡;

2.3 使用带有 Ribbon 功能的 Spring RestTemplate 调用服务

2.3.1 在主程序类上添加注解

@SpringBootApplication //只需要这个注解即可
public class Application {
  @LoadBalanced //告诉 Spring Cloud 创建一个支持 Ribbon 的 RestTemplate
  @Bean
  public RestTemplate getRestTemplate(){
      return new RestTemplate();
  }
  public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
  }
}
  • Spring Cloud 早期版本中,RestTemplate 类默认自动支持 Ribbon;
  • 自从 Spring Cloud 发布 Angel 版本后,Spring Cloud 中的 RestTemplate 不再支持 Ribbon;
  • 因此,后续版本必须使用 @LoadBalanced 注解显式标注,才能将 Ribbon 和 RestTemplate 一起使用;
  • *RestTemplate 不一定放在主程序类里;也可以在 config 包下新建一个 ApplicationContextConfig 配置类,将 RestTemplate 放在该类里:
@Configuration
public class ApplicationContextConfig{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

2.3.2 使用 Ribbon 的 RestTemplate 来调用服务

在服务消费者的 client 包下;
@Component
public class ProviderRestTemplateClient {
    //自动注入即可,不用实例化
    @Autowired
    RestTemplate restTemplate;

    public Provider getProvider(String providerId){
        ResponseEntity<Provider> restExchange =
                restTemplate.exchange(
                        //使用 Eureka 服务 ID 来构建目标 URL
                        "http://provider-instance-name/providers/{providerId}",
                        HttpMethod.GET,
                        null, Provider.class, providerId);
        return restExchange.getBody();
    }
}
  • 通过使用 RestTemplate 类,Ribbon 将在所有服务实例之间轮询负载均衡所有请求;

2.4 使用 Netflix Feign 客户端调用服务

Feign 相关知识将在下篇《4.2 基于 Feign 与 OpenFeign 的服务接口调用》中说明,这里仅把重点放在与上述两种调用提供者服务的区别与对比;

2.4.1 在主程序类上添加注解

@EnableFeignClients:表示启用 Feign 客户端;

2.4.2 定义用于调用服务提供者的 Feign 接口

@FeignClient("provider-instance-name") //标识服务为 feign 的客户端
public interface ProviderFeignClient {
    //定义端点的路径和动作
    @RequestMapping( 
            method= RequestMethod.GET,
            value="/providers/{providerId}",
            consumes="application/json")
    //定义传入端点的参数,该方法可以由客户端调用以触发组织服务        
    Provider getProvider(@PathVariable("providerId") String providerId);
}
  • 要是用 ProviderFeignClient 类,开发人员只需要自动装配并使用它即可;


3. 通过 java 配置类自定义负载均衡算法示例(消费者服务)

指切换默认的负载均衡算法,切换后的仍为现成的(与本地负载均衡器有所区别,本地负载均衡器要自己实现);

3.1 编写配置类

  • 注意:自定义配置类不能放在 @ComponentScan 所扫描的当前包下以及子包下,否则自定义的配置类会被所有的Ribbon客户端所共享,达不到自定义的目的;
  • @ComponentScan 注解被封装到主启动类上的 @SpringBootApplication 注解。其默认扫描主启动类所在包及其子包,因此我们要返回上一级目录新建一个 myRule 目录存放我们自定义的负载均衡配置类;

自定义负载均衡算法的文件目录结构

@Configuration
public class MySelfRule {
    @Bean
    public IRule myRule(){
        return new RandomRule();//定义为随机
    }
}

3.2 主启动类上添加注解

  • @RibbonClient(name = "provider-instance-name" ,configuration=MySelfRule.class):表示使用自定义负载均衡算法;

    • name:指定服务提供者的实例名称;
    • configuration:指定需要使用哪个配置类的负载均衡;
    • 表示 provider 服务使用 MySelfRule 对应的 Ribbon 配置;
  • 同样,需要对 RestTemplate 类用 @LoadBalanced 注解显示声明;


4. 通过配置自定义负载均衡算法示例(消费者服务)

指切换默认的负载均衡算法,切换后的仍为现成的(与本地负载均衡器有所区别,本地负载均衡器要自己实现);

4.1 修改 bootstrap.yml 配置文件

  • 上述 java 配置类的效果等价于下面这样的配置文件:
#服务提供者的实例名称
provider-instance-name:
  ribbon:
    #代表 Ribbon 使用的负载均衡策略,属性的值为:IRule 的实现类
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    
    #其他可用的配置属性
    # NFLoadBalancerClassName : 配置 ILoadBalancer 的实现类
    # NFLoadBalancerPingClassName : 配置 IPing 的实现类
    # NIWSServerListClassName: 配置 ServerList 的实现类
    # NIWSServerListFilterClassName: 配置 ServerListtFilter 的实现类

4.2 主程序类

  • 不需要 @RibbonClient 注解;
  • 同样,需要对 RestTemplate 类用 @LoadBalanced 注解显示声明;


5. 本地负载均衡器的实现(消费者)

本地负载均衡器不同于自定义负载均衡算法;前者的负载均衡算法需要自己手动实现,后者只是切换成另一种现成的负载均衡算法;

5.1 不使用 RestTemplate

  • 即主程序类不需要对 RestTemplate 类用 @LoadBalanced 注解显示声明;
  • 可以删去也可以注释 @LoadBalanced 注解;

5.2 定义负载均衡接口

可以新建一个包,专门存放我们自己写的负载均衡算法;
public interface LoadBalancer{
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}

5.3 实现负载均衡接口

  • 可以根据业务要求写不同的负载均衡算法,这里仅提供一种示例;
  • 该示例实现了一种较为简单的原子性的负载均衡算法;
@Component
public class MyLB implements LoadBalancer{
    private AtomicInteger atomicInteger = new AtomicInteger(0);
    public final int getAndIncrement(){
        int current;
        int next;

        do {
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0 : current + 1;
        }while(!this.atomicInteger.compareAndSet(current,next));
        System.out.println("*****第几次访问,次数next: "+next);
        return next;
    }

    //负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标  ,每次服务重启动后rest接口计数从1开始。
    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances){
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}

5.4 在 controller 接口中使用本地负载均衡器

  • 类似于本篇《2.2 使用 Spring DiscoveryClient 查找服务实例》;
  • 不同之处在于 2.2 没有负载均衡功能,这里在 2.2 的基础上,开发人员自己定义了本地负载均衡器,不使用 Ribbon 提供的负载均衡,故《5.1 不使用 RestTemplate》中提到的不用对 RestTemplate 类使用 @LoadBalanced 注解显示声明
@RestController
public class OrderController{
    //服务提供者示例的名字
    public static final String PAYMENT_URL = "http://provider-instance-name";
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private LoadBalancer loadBalancer;
    @Resource
    private DiscoveryClient discoveryClient;
 
    @GetMapping(value = "/provider/mylb")
    public String getProviderLB(){
        //获取服务提供者的所有实例列表
        List<ServiceInstance> instances = discoveryClient.getInstances("provider-instance-name");
        if(instances == null || instances.size() <= 0){
            return null;
        }
        //使用本地负载均衡器选出提供者服务
        ServiceInstance serviceInstance = loadBalancer.instances(instances);
        URI uri = serviceInstance.getUri();
        return restTemplate.getForObject(uri+"/provider/mylb",String.class);
    }
}



相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
相关文章
|
4月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
5月前
|
弹性计算 负载均衡 网络协议
阿里云SLB深度解析:从流量分发到架构优化的技术实践
本文深入探讨了阿里云负载均衡服务(SLB)的核心技术与应用场景,从流量分配到架构创新全面解析其价值。SLB不仅是简单的流量分发工具,更是支撑高并发、保障系统稳定性的智能中枢。文章涵盖四层与七层负载均衡原理、弹性伸缩引擎、智能DNS解析等核心技术,并结合电商大促、微服务灰度发布等实战场景提供实施指南。同时,针对性能调优与安全防护,分享连接复用优化、DDoS防御及零信任架构集成的实践经验,助力企业构建面向未来的弹性架构。
483 76
|
9月前
|
弹性计算 API 持续交付
后端服务架构的微服务化转型
本文旨在探讨后端服务从单体架构向微服务架构转型的过程,分析微服务架构的优势和面临的挑战。文章首先介绍单体架构的局限性,然后详细阐述微服务架构的核心概念及其在现代软件开发中的应用。通过对比两种架构,指出微服务化转型的必要性和实施策略。最后,讨论了微服务架构实施过程中可能遇到的问题及解决方案。
|
5月前
|
负载均衡 算法 关系型数据库
大数据新视界--大数据大厂之MySQL数据库课程设计:MySQL集群架构负载均衡故障排除与解决方案
本文深入探讨 MySQL 集群架构负载均衡的常见故障及排除方法。涵盖请求分配不均、节点无法响应、负载均衡器故障等现象,介绍多种负载均衡算法及故障排除步骤,包括检查负载均衡器状态、调整算法、诊断修复节点故障等。还阐述了预防措施与确保系统稳定性的方法,如定期监控维护、备份恢复策略、团队协作与知识管理等。为确保 MySQL 数据库系统高可用性提供全面指导。
|
5月前
|
Cloud Native Serverless 流计算
云原生时代的应用架构演进:从微服务到 Serverless 的阿里云实践
云原生技术正重塑企业数字化转型路径。阿里云作为亚太领先云服务商,提供完整云原生产品矩阵:容器服务ACK优化启动速度与镜像分发效率;MSE微服务引擎保障高可用性;ASM服务网格降低资源消耗;函数计算FC突破冷启动瓶颈;SAE重新定义PaaS边界;PolarDB数据库实现存储计算分离;DataWorks简化数据湖构建;Flink实时计算助力风控系统。这些技术已在多行业落地,推动效率提升与商业模式创新,助力企业在数字化浪潮中占据先机。
338 12
|
9月前
|
Java 开发者 微服务
从单体到微服务:如何借助 Spring Cloud 实现架构转型
**Spring Cloud** 是一套基于 Spring 框架的**微服务架构解决方案**,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。
740 71
从单体到微服务:如何借助 Spring Cloud 实现架构转型
|
8月前
|
负载均衡 算法
架构学习:7种负载均衡算法策略
四层负载均衡包括数据链路层、网络层和应用层负载均衡。数据链路层通过修改MAC地址转发帧;网络层通过改变IP地址实现数据包转发;应用层有多种策略,如轮循、权重轮循、随机、权重随机、一致性哈希、响应速度和最少连接数均衡,确保请求合理分配到服务器,提升性能与稳定性。
1819 11
架构学习:7种负载均衡算法策略
|
9月前
|
运维 监控 持续交付
微服务架构解析:跨越传统架构的技术革命
微服务架构(Microservices Architecture)是一种软件架构风格,它将一个大型的单体应用拆分为多个小而独立的服务,每个服务都可以独立开发、部署和扩展。
2539 36
微服务架构解析:跨越传统架构的技术革命
|
8月前
|
人工智能 安全 Java
微服务引擎 MSE:打造通用的企业级微服务架构
微服务引擎MSE致力于打造通用的企业级微服务架构,涵盖四大核心内容:微服务技术趋势与挑战、MSE应对方案、拥抱开源及最佳实践。MSE通过流量入口、内部流量管理、服务治理等模块,提供高可用、跨语言支持和性能优化。此外,MSE坚持开放,推动云原生与AI融合,助力企业实现无缝迁移和高效运维。
315 1