【Spring Cloud系列】- Ribbon详解与实战(下)

本文涉及的产品
网络型负载均衡 NLB,每月750个小时 15LCU
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
简介: 【Spring Cloud系列】- Ribbon详解与实战(下)

四、如何自定义Ribbon负载均衡策略

4.1 自定义算法

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import org.springframework.stereotype.Component;
import com.netflix.loadbalancer.Server;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class RandomRule_ZY extends AbstractLoadBalancerRule {
    //定义一个原子类,以保证原子性
    private AtomicInteger atomicInteger =new AtomicInteger(0);
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null){
            return null;
        }
        //用于统计获取次数,当达到一定数量就不再去尝试
        int count = 0;
        Server server = null;
        //当服务还没获取到,并且尝试没有超过8次
        while (server == null && count++ < 8){
            //获取服务
            List<Server> allServers = lb.getAllServers();
            List<Server> reachableServers = lb.getReachableServers();
            int allServersSize = allServers.size();
            int reachableServersSize = reachableServers.size();
            //如果获取的服务list都为0就返回null
            if(allServersSize == 0 || reachableServersSize == 0){
                return  null;
            }
            //获取服务下标
            int next = getServerIndex(allServersSize);
            //获取服务
            server = reachableServers.get(next);
            //如果服务为空直接跳过下面的
            if (server == null){
                continue;
            }
            //如果获取到的这个服务是活着的就返回
            if (server.isAlive()){
                return server;
            }
            //如果获取到的服务不是空,但是不是存活状态,需要重新获取
            server=null;
        }
        //最后这里可能会返回null
        return  server;
    }
    //获取服务下标,为了保证原子性,使用了CAS
    public int getServerIndex(int allServersSize){
        //自旋锁
        for (;;) {
            //获取当前值
            int current = this.atomicInteger.get();
            //设置期望值
            int next = (current + 1) % allServersSize;
            //调用Native方法compareAndSet,执行CAS操作
            if (this.atomicInteger.compareAndSet(current, next))
                //成功后才会返回期望值,否则无线循环
                return next;
        }
    }
    public Server choose(Object key) {
        return choose(getLoadBalancer(),key);
    }
}

4.2 自定义配置类

@Configuration
//针对全局修改
//@RibbonClients(defaultConfiguration = MyRuleConfig.class)
//针对某个服务修改
@RibbonClient(name = "nacos-app-a", configuration = MyRuleConfig.class)
public class MyRuleConfig {
    @Bean
    public IRule rule() {
      //返回上面自定义的规则类
        return new RandomRule_ZY();

4.3 设置加载自定义Ribbon配置类

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "Client",configuration = MyRuleConfig.class)
public class GoyeerCloudRibbonAppliction {
    public static void main(String[] args){
        SpringApplication.run(GoyeerCloudRibbonAppliction.class);
    }
}

五、LoadBalancer–负载均衡器的核心

Ribbon实现负载均衡是通过LoadBalancer注解来给RestTemplate标记,来实现负载均衡。LoadBalancer是如何实现负载均衡哪?查看源码我们可以看到有一个接口LoadBalancerClinet来实现的。

5.1 什么是LoadBalancerClient

LoadBalancerClient 是 SpringCloud 提供的一种负载均衡客户端,Ribbon 负载均衡组件内部也是集成了 LoadBalancerClient 来实现负载均衡。

5.2 LoadBalancerClient原理

LoadBalancerClinet在初始化时会通过Euraka Clinet向Eureka服务端获取所有的服务实例的注册信息并缓存到本地,并且每10秒向EurakaClinet发送“Ping”请求,来判断服务的可用性。如果服务的可用性发生了改变或者服务数量和之前的不一致,则更新或重新拉取最新到本地。在得到最新服务注册信息后,ILoadBalancer根据IRule的策略进行负载均衡(默认策略为轮询)。

当使用LoadBalancerClient进行远程调用的负载均衡时,LoadBalancerClient先通过目标服务名在本地服务注册清单中获取服务提供方的某个实例,如多个服务器节点,LoadBalancerClient会通过choose()方法获取到多个节点中一个服务,拿到服务的信息之后取出服务IP信息,就可以得到完整的想要访问的IP地址和端口号,最后通过RestTempate访问具体的服务信息。

5.3 LoadBabancerClient源码解析

5.3.1 LoadBalancerClient 类图

LoadBalancerClient 是 Spring Cloud 提供的一个非常重要的接口,它继承ServiceInstanceChooser 接口,该接口的实现类是 RibbonLoadBalanceClient,它们之间的关系如下图所示:

5.3.2 LoadBalancerClient 接口源码

public interface LoadBalancerClient extends ServiceInstanceChooser 
{
  <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
    <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
    URI reconstructURI(ServiceInstance instance, URI original);
}

可以发现 LoadBalancerClient 接口继承了 ServiceInstanceChooser 接口,包含两个方法:executereconstructURI

  • execute() 用来执行Request请求
  • reconstructURI() 用来重构URL

5.3.3 ServiceInstanceChooser 接口源码

public interface ServiceInstanceChooser 
{
  ServiceInstance choose(String serviceId);
}

ServiceInstanceChooser 接口中的主要方法为 choose(),该方法用于根据服务的名称 serviceId 来选择其中一个服务实例,即根据 serviceId 获取ServiceInstance。

5.3.4 RibbonLoadBalanceClient 实现类源码

LoadBalancerClient 的实现类 RibbonLoadBalanceClient,它用来执行最终的负载均衡请求。其中,RibbonLoadBalanceClient 的一个 choose() 方法用于选择具体的服务实例,其内部是通过 getServer() 方法交给 ILoadBalancer 完成的。

  • choose(),用来选择具体的服务实例
@Override
public ServiceInstance choose(String serviceId) {
  return choose(serviceId, null);
}
/**
 * New: Select a server using a 'key'.
 * @param serviceId of the service to choose an instance for
 * @param hint to specify the service instance
 * @return the selected {@link ServiceInstance}
 */
public ServiceInstance choose(String serviceId, Object hint) {
  Server server = getServer(getLoadBalancer(serviceId), hint);
  if (server == null) {
   return null;
  }
  return new RibbonServer(serviceId, server, isSecure(server, serviceId),serverIntrospector(serviceId).getMetadata(server));
}
  • getServer(),获取实例
protected Server getServer(ILoadBalancer loadBalancer) {
   return getServer(loadBalancer, null);
}
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
   if (loadBalancer == null) {
     return null;
   }
   // 最终通过 loadBalancer 去做服务实例的选择。
   // Use 'default' on a null hint, or just pass it on?
   return loadBalancer.chooseServer(hint != null ? hint : "default");
}
5.3.5 BaseLoadBalancer 源码
5.3.6 IRule 接口源码
public interface IRule{ 
    public Server choose(Object key);
    public void setLoadBalancer(ILoadBalancer lb);
    public ILoadBalancer getLoadBalancer();    
}

IRule 接口定义了3个方法,分别是:choosesetLoadBalancergetLoadBalancer

  • choose() 是用来选择实例的
  • setLoadBalancer()用来设置负载均衡规则
  • getLoadBalancer()获取负载均衡规则

实现IRule有个类,分别定义不同的负载均衡规则:

  • 随机策略 RandomRule
  • 轮询策略 RoundRobinRule
  • 重试策略 RetryRule
  • 可用过滤策略 PredicateBaseRule
  • 响应时间权重策略 WeightedRespinseTimeRule
  • 并发量最小可用策略 BestAvailableRule
  • 区域权重策略 ZoneAvoidanceRule
5.3.7 ILoadBalancer 源码

ILoadBalancer 是一个接口,该接口定义了一系列实现负载均衡的方法,LoadBalancerClient 的实现类 RibbonLoadBalanceClient 也将负载均衡的具体实现交给了 ILoadBalancer 来处理,ILoadBalancer 通过配置 IRule、IPing 等,向 EurekaClient 获取注册列表信息,默认每10秒向 EurekaClient 发送一次 “ping”,进而检查是否需要更新服务的注册列表信息。最后,在得到服务注册列表信息后,ILoadBalancer 根据 IRule 的策略进行负载均衡。

查看 BaseLoadBalancer 和 DynamicServerListLoadBalancer 源码,默认情况下实现了以下配置:

  1. IClientConfig clientConfig:用于配置负载均衡客户端,默认实现类是 DefaultClientConfigImpl。
  2. IRule rule:用于配置负载均衡的策略,默认使用的是 RoundRobinRule 轮询策略。
  3. IPing ping:用于检查当前服务是否有响应,从而判断当前服务是否可用,默认实现类是 DummyPing,该实现类的 isAlive() 方法返回值是 true,默认所有服务实例都是可用的。
  4. ServerList serverList:用于获取所有 Server 注册列表信息。通过跟踪源码会发现,ServerList 的实现类是 DiscoveryEnabledNIWSServerList,该类定义的 obtainServersViaDiscovery() 方法是根据 eurekaClientProvider.get() 方法获取 EurekaClient,再根据 EurekaClient 获取服务注册列表信息。EurekaClient 的实现类是DiscoveryClient,DiscoveryClient 具有服务注册、获取服务注册列表等功能。
  5. ServerListFilter filter:定义了根据配置过滤或者动态获取符合条件的服务列表,默认实现类是 ZonePreferenceServerListFilter,该策略能够优先过滤出与请求调用方处于同区域的服务实例。

六、Ribbon的配置参数

控制参数 说明 默认值
<service-name>.ribbon.NFLoadBalancerPingInterval Ping定时任务周期 30s
service-name>.ribbon.NFLoadBalancerMaxTotalPingTime Ping超时时间 2s
<service-name>.ribbon.NFLoadBalancerRuleClassName IRule实现类 RoundRobinRule,基于轮询调度算法规则选择服务实例
<service-name>.ribbon.NFLoadBalancerPingClassName IPing实现类 DummyPing,直接返回true
<service-name>.ribbon.NFLoadBalancerClassName 负载均衡器实现类 2s
<service-name>.ribbon.NIWSServerListClassName ServerList实现类 ConfigurationBasedServerList,基于配置的服务列表
<service-name>.ribbon.ServerListUpdaterClassName 服务列表更新类 PollingServerListUpdater
<service-name>.ribbon.NIWSServerListFilterClassName 服务实例过滤器 2s
<service-name>.ribbon.NIWSServerListFilterClassName 服务实例过滤器 2s
<service-name>.ribbon.ServerListRefreshInterval 服务列表刷新频率 2s
<service-name>.ribbon.NFLoadBalancerClassName 自定义负载均衡器实现类 2s

七、总结

Ribbon是Spring cloud的核心,负载微服务内负载调用;Ribbon可以脱离Spring Cloud的单独使用。

Ribbon是微服务整个微服务组件最复杂的一环,控制流程上为保证服务的高可用性,有比较多的细节参数控制,在使用的过程中需要深入理清每个环节的处理机制,使之发挥稳定且高效的作用。



相关实践学习
小试牛刀,一键部署电商商城
SAE 仅需一键,极速部署一个微服务电商商城,体验 Serverless 带给您的全托管体验,一起来部署吧!
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
6天前
|
人工智能 自然语言处理 前端开发
Spring AI与DeepSeek实战二:打造企业级智能体
本文介绍如何基于Spring AI与DeepSeek模型构建企业级多语言翻译智能体。通过明确的Prompt设计,该智能体能自主执行复杂任务,如精准翻译32种ISO标准语言,并严格遵循输入格式和行为限制。代码示例展示了如何通过API实现动态Prompt生成和翻译功能,确保服务的安全性和可控性。项目已开源,提供更多细节和完整代码。 [GitHub](https://github.com/zlt2000/zlt-spring-ai-app) | [Gitee](https://gitee.com/zlt2000/zlt-spring-ai-app)
85 11
|
13天前
|
人工智能 Java API
Spring AI与DeepSeek实战一:快速打造智能对话应用
在 AI 技术蓬勃发展的今天,国产大模型DeepSeek凭借其低成本高性能的特点,成为企业智能化转型的热门选择。而Spring AI作为 Java 生态的 AI 集成框架,通过统一API、简化配置等特性,让开发者无需深入底层即可快速调用各类 AI 服务。本文将手把手教你通过spring-ai集成DeepSeek接口实现普通对话与流式对话功能,助力你的Java应用轻松接入 AI 能力!虽然通过Spring AI能够快速完成DeepSeek大模型与。
301 11
|
25天前
|
JavaScript 前端开发 Java
Jeesite5:Star24k,Spring Boot 3.3+Vue3实战开源项目,架构深度拆解!让企业级项目开发效率提升300%的秘密武器
Jeesite5 是一个基于 Spring Boot 3.3 和 Vue3 的企业级快速开发平台,集成了众多优秀开源项目,如 MyBatis Plus、Bootstrap、JQuery 等。它提供了模块化设计、权限管理、多数据库支持、代码生成器和国际化等功能,极大地提高了企业级项目的开发效率。Jeesite5 广泛应用于企业管理系统、电商平台、客户关系管理和知识管理等领域。通过其强大的功能和灵活性,Jeesite5 成为了企业级开发的首选框架之一。访问 [Gitee 页面](https://gitee.com/thinkgem/jeesite5) 获取更多信息。
Jeesite5:Star24k,Spring Boot 3.3+Vue3实战开源项目,架构深度拆解!让企业级项目开发效率提升300%的秘密武器
|
3月前
|
存储 JavaScript 开发工具
基于HarmonyOS 5.0(NEXT)与SpringCloud架构的跨平台应用开发与服务集成研究【实战】
本次的.HarmonyOS Next ,ArkTS语言,HarmonyOS的元服务和DevEco Studio 开发工具,为开发者提供了构建现代化、轻量化、高性能应用的便捷方式。这些技术和工具将帮助开发者更好地适应未来的智能设备和服务提供方式。
94 8
基于HarmonyOS 5.0(NEXT)与SpringCloud架构的跨平台应用开发与服务集成研究【实战】
|
3月前
|
消息中间件 监控 Java
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
56 6
|
3月前
|
Java 关系型数据库 MySQL
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
92 5
|
3月前
|
缓存 监控 Java
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
73 5
|
4月前
|
JSON Java 测试技术
SpringCloud2023实战之接口服务测试工具SpringBootTest
SpringBootTest同时集成了JUnit Jupiter、AssertJ、Hamcrest测试辅助库,使得更容易编写但愿测试代码。
106 3
|
4月前
|
负载均衡 算法 Java
除了 Ribbon,Spring Cloud 中还有哪些负载均衡组件?
这些负载均衡组件各有特点,在不同的场景和需求下,可以根据项目的具体情况选择合适的负载均衡组件来实现高效、稳定的服务调用。
320 5
|
3月前
|
负载均衡 Java Nacos
常见的Ribbon/Spring LoadBalancer的负载均衡策略
自SpringCloud 2020版起,Ribbon被弃用,转而使用Spring Cloud LoadBalancer。Ribbon支持轮询、随机、加权响应时间和重试等负载均衡策略;而Spring Cloud LoadBalancer则提供轮询、随机及Nacos负载均衡策略,基于Reactor实现,更高效灵活。
218 0