当前位置: 首页 > news >正文

[高可用/负载均衡] Ribbon LoadBalancer: 开源的客户端式负载均衡框架

0 序言

  • 某项目上,原先为自建的数据库集群提供了负载均衡IP服务器(简称: ELB IP Server),客户端的数据库请求URL都统一走ELB IP。但随着业务量的增长,识别到一个严峻的现实:
  • 其一,考虑到未来的业务增长情况,云厂商提供的 ELB IP Server 云服务的入网带宽必将完全无法满足本项目的诉求。
  • 其二,云厂商提供的 ELB IP Server 的费用较为昂贵,实在是不划算。

除了入网带宽的使用量较高外,云厂商ELB 服务提供的其他方面的资源指标,使用量均极低(有浪费钱的嫌疑)。

  • 为此开始尝试:取消服务端式负载均衡器,自行实现客户端式的负载均衡器

经过一番研究,开源的、支持Java、与spring生态框架独立/解耦的、负载均衡器 Ribbon,成为个人的首选。

即:笔者此时的诉求之一是,不需要引入spring框架,与其解耦。

1 概述:Ribbon LoadBalancer: 开源负载均衡器

负载均衡的概念

  • 负载均衡是一种通过【分发请求】来优化服务器资源利用率提高系统性能的技术。

它在微服务架构中尤为重要,常见的负载均衡方式包括服务端负载均衡客户端负载均衡

image

  • 服务端负载均衡是指请求首先被发送到【负载均衡服务器】,然后由该服务器根据【负载均衡算法】(如轮询、最小连接数等)将【请求分发】到后端服务器进行处理。
  • 常见的服务端负载均衡工具包括: 硬件设备(如F5)和软件(如Nginx、LVS)。
  • 这种方式的优点是: 客户端无感知、无需关心负载均衡的逻辑,所有的均衡操作都由服务端完成。

image

  • 客户端负载均衡则是由客户端直接从服务注册中心(如Nacos、Eureka)获取服务列表,并根据负载均衡算法选择目标服务器进行请求分发

Spring Cloud中的Ribbon为例,客户端通过RestTemplate触发负载均衡。
客户端负载均衡的特点无需额外的负载均衡服务器(例如: ELB IP Server),分发逻辑完全由客户端实现。

image

  • 两者的主要区别在于:负载均衡的实现位置。

服务端负载均衡依赖于专门的负载均衡服务器,而客户端负载均衡则由客户端自行完成分发逻辑。

客户端式负载均衡方案的实现原理

  1. ​服务发现客户端​,从注册中心获取服务实例列表并缓存。
  2. 客户端请求被 ​负载均衡拦截器​ 截获(如 @LoadBalanced 标记的 RestTemplate/WebClient)。

org.springframework.cloud.client.loadbalancer.LoadBalanced
org.springframework.web.client.RestTemplate

  1. 拦截器调用 ​LoadBalancerClient

org.springframework.cloud.client.loadbalancer.LoadBalancerClient
org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient

4.​ LoadBalancerClient​ 调用底层的负载均衡器 (Ribbon / SCL) 选择一个实例。
5. 负载均衡器根据 ​负载均衡策略​ 从可用实例列表中选择一个目标实例。
6. 请求最终被​转发到选定的实例。

关键问题:负载均衡与请求客户端、连接池的集成

  • Ribbon 等本身只是一个客户端负载均衡器,它负责从服务列表里挑一台机器,把原请求 URL 中的“服务名”替换成这台服务器的真实 IP+端口。

真正发出 HTTP 请求的是下游的 HTTP 客户端

  • 若还想把“连接池”能力加进来,就是把这个客户端换成支持池化的实现(OkHttp / Apache HttpClient),并让它复用 Ribbon 等负载均衡框架已挑好的地址。

Ribbon LoadBalancer: 开源的客户端式负载均衡框架

  • Ribbon
  • Ribbon有很多子模块,官方文档中说明,目前 Netflix 公司主要用于生产环境的 Ribbon 子模块如下:
  • ribbon-core:Ribbon 的核心API。
  • ribbon-loadbalancer:可以独立使用或与其他模块一起使用的负载均衡器 API。
  • ribbon-eureka:Ribbon 结合 注册中心 Eureka 客户端的 API,为负载均衡器提供动态服务注册列表信息。

image

  • Ribbon LoadBalancer
  • 起源于 ​Netflix OSS,曾是 Spring Cloud ​默认的客户端负载均衡解决方案。
  • Ribbon 是一个独立的、较为成熟的库,被广泛集成到 Spring Cloud Netflix 组件(如 Zuul、Feign)中。
  • 已进入维护模式,Netflix 官方不再积极开发新功能。
  • 技术架构与依赖​
  • 非响应式 (阻塞式):​​ 核心 API 基于线程池和阻塞调用,在响应式编程场景下兼容性较差。
  • 依赖较重:​​ 包含大量 Netflix 的内部组件 (如 Archaius 配置系统),包体积和复杂度较高。
  • 独立的负载均衡器:​​ 需要额外的客户端负载均衡器实现 (如 RibbonLoadBalancerClient)。

springcloud 与 ribbon 整合

此小节旨在解释 spring cloud 项目中,如何与 ribbon 集成。ribbon 也可完全独立于 spring 项目,独立运行。

Ribbon 与 RestTemplate 整合使用

  • Spring Cloud 构建的微服务系统中,Ribbon 作为服务消费者的负载均衡器,有2种使用方式:
  • 一种是和 RestTemplate 相结合;
  • 另一种是和 Feign 相结合。
  • 那么,Spring Cloud框架中,Ribbon (负载均衡器) 是如何与 Spring 的 RestTemplate / WebClient 集成的?

下面用一张图来看看 RestTemplate 基于 Ribbon 的远程调用:

image

from : https://www.cnblogs.com/chiangchou/p/ribbon-1.html

  • RestTemplate 本身是不具备【负载均衡】的能力的。
1 RestTemplate 是 Spring Resources 中一个访问第三方 RESTful API 接口的网络请求框架,用于执行HTTP请求。
2 其暴露了一系列的模板方法API,便于操作底层的HTTP客户端库,如JDK的HttpURLConnection、Apache HttpComponents等。
3 RestTemplate 是用来消费 REST 服务的,所以 RestTemplate 的主要方法都与 REST 的 Http协议的一些方法紧密相连,例如 HEAD、GET、POST、PUT、DELETE 和 OPTIONS 等方法。
这些方法在 RestTemplate 类对应的方法为 headForHeaders()、getForObject()、postForObject()、put() 和 delete() 等。4 RestTemplate通常作为【共享组件】使用,其配置不支持【并发修改】,因此通常在【启动时】准备好配置。如果需要,可以在启动时创建多个配置不同的RestTemplate实例。这些实例可以使用相同的底层`ClientHttpRequestFactory`,如果它们需要共享HTTP客户端资源。
  • 如果 RestTemplate 未使用 @LoadBalanced 标记,就通过服务名的形式来调用,必然会报错。
  • @LoadBalanced 标记后,调用 RestTemplateREST 方法就会通过【负载均衡】的方式通过一定的负载策略【路由】到某个【服务实例】上。

此时,其底层负责负载均衡的组件就是 Ribbon

springcloud 、注册中心 eureka 、负载均衡器 ribbon 三者的整合

  • 与 eureka 整合到 springcloud 类似,springcloud 提供了对应的 spring-cloud-starter-netflix-eureka-client(server) 依赖包
  • ribbon 则整合到了 spring-cloud-starter-netflix-ribbon 中。

一般也不需要单独引入 ribbon 的依赖包,spring-cloud-starter-netflix-eureka-client 中已经依赖了 spring-cloud-starter-netflix-ribbon
因此,我们引入了 spring-cloud-starter-netflix-eureka-client 就可以使用 Ribbon 的功能了。

image

springcloud 、注册中心 nacos 、负载均衡器 ribbon 三者的整合

略,类同 eureka 。

客户端式负载均衡框架的同类竞品项目

  • ​Spring Cloud LoadBalancer (SCL)
  • Spring 官方在 ​Spring Cloud Hoxton (2020年)​​ 推出,旨在替代 Ribbon。
  • 是 ​Spring Cloud Commons​ 项目的一部分,与 Spring 生态集成度更高。
  • 目前作为 ​Spring Cloud 官方推荐的负载均衡解决方案,持续更新迭代。

spring项目中,若同时引了 spring-cloud-starter-netflix-ribbonspring-cloud-loadbalancer 会冲突,用 spring.cloud.loadbalancer.ribbon.enabled=false 可回退到 Ribbon

  • 技术架构与依赖​
  • ​响应式优先:​​ 核心接口 ReactiveLoadBalancer 基于 ​Project Reactor​(Reactor Core),天然支持响应式编程,同时对阻塞式调用提供适配。
  • 轻量级:​​ 源码简洁,依赖少 (spring-cloud-starter-loadbalancer),启动更快。
  • Spring原生集成:​​ 与 Spring 框架深度集成(如 Environment、BeanFactory),配置管理更简单。(既是优点,也是缺点)

Maven依赖

<!-- 客户端式负载均衡器 https://github.com/Netflix/ribbon | https://mvnrepository.com/artifact/com.netflix.ribbon/ribbon -->
<dependency><groupId>com.netflix.ribbon</groupId><artifactId>ribbon</artifactId><!-- 2.7.18 --><version>${ribbon.version}</version>
</dependency>
<dependency><groupId>com.netflix.ribbon</groupId><artifactId>ribbon-core</artifactId><version>${ribbon.version}</version>
</dependency>
<dependency><groupId>com.netflix.ribbon</groupId><artifactId>ribbon-loadbalancer</artifactId><version>${ribbon.version}</version>
</dependency>

2 Ribbon LoadBalancer 核心 API

  • IClientConfig:Ribbon 客户端配置类,默认实现是 DefaultClientConfigImpl。
  • IRule:负载均衡策略规则组件,默认实现是 ZoneAvoidanceRule。
  • IPing:判断 Server 是否存活,默认实现是 DummyPing,永远都是返回 true。
  • ServerList:获取 Server 的组件,默认实现类为 ConfigurationBasedServerList,从配置文件获取。
  • ServerListUpdater:Server 列表更新组件,默认实现类为 PollingServerListUpdater。
  • ServerListFilter:过滤可用的 Server 列表,默认实现类为 ZonePreferenceServerListFilter。
  • RibbonLoadBalancerContext:负载均衡客户端。
  • RetryHandler:重试处理器,默认实现类为 DefaultLoadBalancerRetryHandler。

IClientConfig : 客户端配置

  • com.netflix.client.config.IClientConfig : 管理客户端配置的核心接口,它的默认实现类是 DefaultClientConfigImpl

可以看到在创建 IClientConfig 时,设置了 Ribbon 客户端默认的连接和读取超时时间为 1 秒,例如读取如果超过1秒,就会返回超时,这两个一般需要根据实际情况来调整。

import com.netflix.client.config.IClientConfig;
import com.netflix.client.config.CommonClientConfigKey;@Bean
@ConditionalOnMissingBean
public IClientConfig ribbonClientConfig() {DefaultClientConfigImpl config = new DefaultClientConfigImpl();// 加载配置config.loadProperties(this.name);// 连接超时默认 1 秒config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);// 读取超时默认 1 秒config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);return config;
}
  • com.netflix.client.config.CommonClientConfigKey

这个类定义了 Ribbon 客户端相关的所有配置的键常量,可以通过这个类来看有哪些配置。

https://github.com/Netflix/ribbon/blob/v2.7.18/ribbon-core/src/main/java/com/netflix/client/config/CommonClientConfigKey.java

  • 进入到 DefaultClientConfigImpl,可以看到 CommonClientConfigKey 中的每个配置都对应了一个默认值。

在加载配置的时候,如果用户没有定制配置,就会使用默认的配置。

https://github.com/Netflix/ribbon/blob/v2.7.18/ribbon-archaius/src/main/java/com/netflix/client/config/DefaultClientConfigImpl.java
https://github.com/Netflix/ribbon/blob/v2.7.18/ribbon-core/src/test/java/com/netflix/client/config/DefaultClientConfigImplTest.java

也可以在配置文件中定制配置,例如配置超时和重试:

# 全局配置
ribbon:# 客户端读取超时时间ReadTimeout: 3000# 客户端连接超时时间ConnectTimeout: 3000# 默认只重试 GET,设置为 true 时将重试所有类型,如 POST、PUT、DELETEOkToRetryOnAllOperations: false# 重试次数MaxAutoRetries: 1# 最多重试几个实例MaxAutoRetriesNextServer: 1# 只针对 demo-producer 客户端
demo-producer:ribbon:# 客户端读取超时时间ReadTimeout: 5000# 客户端连接超时时间ConnectTimeout: 3000

IRule : 均衡策略

  • IRule 是最终选择 Server 的策略规则类,核心的接口就是 choose
public interface IRule{// 选择 Serverpublic Server choose(Object key);// 设置 ILoadBalancerpublic void setLoadBalancer(ILoadBalancer lb);// 获取 ILoadBalancerpublic ILoadBalancer getLoadBalancer();
}
  • Ribbon 提供了丰富的负载均衡策略,我们也可以通过配置指定使用某个均衡策略。下面是整个Ribbon提供的 IRule 均衡策略。

image

策略类 命名 描述
RandomRule 随机策略 随机选择 server
RoundRobinRule 轮询策略 按顺序循环选择 server
RetryRule 重试策略 在一个配置时间段内当选择 server 不成功,则一直尝试选择一个可用的 server
BestAvailableRule 最低并发策略 逐个考察 server,如果 server 断路器打开,则忽略,再选择其中并发连接最低的 server
AvailabilityFilteringRule 可用过滤策略 过滤掉一直连接失败并被标记为 circuit tripped 的 server,过滤掉那些高并发连接的 server(active connections 超过配置的阈值)
ResponseTimeWeightedRule 响应时间加权策略 根据 server 的响应时间分配权重。响应时间越长,权重越低,被选择到的概率就越低;响应时间越短,权重越高,被选择到的概率就越高。这个策略很贴切,综合了各种因素,如:网络、磁盘、IO 等,这些因素直接影响着响应时间
ZoneAvoidanceRule 区域权衡策略 综合判断 server 所在区域的性能和 server 的可用性轮询选择 server,并且判定一个 AWS Zone 的运行性能是否可用,剔除不可用的 Zone 中的所有 server

例如: RandomRule => com.netflix.loadbalancer.RandomRule

IPing : 服务检查

  • com.netflix.loadbalancer.IPing :

用于定期检查 Server 的可用性的,它只提供了一个接口,用来判断 Server 是否存活:

package com.netflix.loadbalancer;public interface IPing {boolean isAlive(Server var1);
}
  • IPing 也提供了多种策略可选。
    image

image

下面是整个 IPing 体系结构:

image

ServerList : 获取服务列表

  • ServerList 提供了2个接口: 一个是第一次获取 Server 列表,一个是更新 Server 列表

其中 getUpdatedListOfServers 会每被 Loadbalancer 隔 30 秒调一次来更新 allServerList。

public interface ServerList<T extends Server> {public List<T> getInitialListOfServers();/*** Return updated list of servers. This is called say every 30 secs* (configurable) by the Loadbalancer's Ping cycle*/public List<T> getUpdatedListOfServers();
}
  • ServerList 也提供了多种实现

ServerList 体系结构如下:

image

image

ServerListFilter : 过滤服务

  • ServerListFilter 提供了一个接口用来过滤出可用的 Server。
public interface ServerListFilter<T extends Server> {public List<T> getFilteredListOfServers(List<T> servers);
}

image

ServerListUpdater :服务列表更新

  • ServerListUpdater 有多个接口,最核心的就是 start 开启定时任务调用 updateAction 来更新 allServerList
public interface ServerListUpdater {/*** an interface for the updateAction that actually executes a server list update*/public interface UpdateAction {void doUpdate();}/*** start the serverList updater with the given update action* This call should be idempotent.*/void start(UpdateAction updateAction);
}
  • 默认有两个实现类:

image

ILoadBalancer : 负载均衡器

  • ILoadBalancer 是负载均衡选择服务的核心接口,主要提供了如下的获取Server列表和根据客户端名称选择Server的接口。
public interface ILoadBalancer {// 添加Serverpublic void addServers(List<Server> newServers);// 根据key选择一个Serverpublic Server chooseServer(Object key);// 获取存活的Server列表,返回 upServerListpublic List<Server> getReachableServers();// 获取所有Server列表,返回 allServerListpublic List<Server> getAllServers();
}

image

Z 案例实践

CASE 实现客户端式负载均衡器(快速入门版)

package com.knowdata.framework.study.ribbon.lb;import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.List;import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.RoundRobinRule;
import com.netflix.loadbalancer.Server;/** @description Ribbon 负载均衡框架的快速入门示例* @updateTime 2025/09/14 16:39*/
public class RibbonQuickStartTest {public static void main(String[] args) throws Exception {// 定义目标服务器列表List<Server> serverList = Arrays.asList(new Server("localhost", 8086),new Server("localhost", 8086),new Server("localhost", 8086));// 创建(客户端式)负载均衡器BaseLoadBalancer loadBalancer = new BaseLoadBalancer();loadBalancer.setServersList(serverList);// 配置负载均衡策略(可选,默认为轮询)loadBalancer.setRule(new RoundRobinRule());// 其他策略示例:// loadBalancer.setRule(new RandomRule());// loadBalancer.setRule(new WeightedResponseTimeRule());// 模拟多次请求,查看负载均衡效果for (int i = 0; i < 10; i++) {Server server = loadBalancer.chooseServer(null);System.out.println("第 " + (i + 1) + " 次请求,选中服务器: " + server.getHostPort());sendRequest(server);}}private static void sendRequest(Server server) {try {URL url = new URL("http://" + server.getHost() + ":" + server.getPort() + "/api/hello");HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");int responseCode = connection.getResponseCode();System.out.println("响应码: " + responseCode);connection.disconnect();} catch (IOException e) {System.err.println("请求失败: " + e.getMessage());}}
}

Y 推荐文献

  • Ribbon
  • https://github.com/Netflix/ribbon
  • https://mvnrepository.com/artifact/com.netflix.ribbon/ribbon
  • SpringCloud 源码系列(4)—— 负载均衡 Ribbon(上) - 博客园/bojiangzhou 【推荐】

  • SpringCloud 源码系列(5)—— 负载均衡 Ribbon(下) - 博客园/bojiangzhou 【推荐】

  • [HTTP/Spring] RestTemplate : Spring的HTTP网络请求框架 - 博客园/千千寰宇

X 参考文献

  • Ribbon和LoadBalance-负载均衡 - 技术栈
http://www.wxhsa.cn/company.asp?id=4070

相关文章:

  • 梦话周记
  • 【电机控制】无刷电机结构阐述---磁极数、槽数
  • 金刚怒目是我哭
  • nginx使用默认端口80作为服务端口
  • 机器学习和推荐算法顶级会议和期刊
  • java使用mysql
  • 2025年医疗行业API安全最佳实践与深度案例分析:从理论到全面落地
  • 2026 NOI 做题记录(二)
  • lc1027-最长等差数列
  • 13
  • np.zeros函数
  • Langchain之让LLM拥有记忆
  • 25.9.14
  • .net PublishSingleFile 打包程序提取
  • 实用指南:Java类加载机制
  • C 语言注释
  • 扫描线
  • C语言中的查找与排序算法整理
  • k8s练习
  • css-2
  • AtCoder Beginner Contest 423 ABCDEF 题目解析
  • numpy中的shape属性
  • mac 查看fat32磁盘
  • 使用Smart-Doc为Java项目生成gRPC API文档
  • 数字时钟用的什么字体
  • Python数据分析零基础完整课程大纲(详细版)【202509第1版】 - 指南
  • 详细介绍:uni-app 根据用户不同身份显示不同的tabBar
  • VSTO QQ群 61840693 解散通知【新群193203228 】
  • kettle从入门到精通 第107课 ETL之kettle json_input 一个点号引发的血案
  • 【2024-2025第二学期】助教工作学期总结