一、 什么是客户端负载均衡?
在理解 Ribbon 之前,需要先区分服务端负载均衡(如 Nginx)和客户端负载均衡:
- 服务端负载均衡:客户端请求先发给 Nginx,由 Nginx 根据算法转发给后端服务。
- 客户端负载均衡:客户端(服务消费者)自己维护一份可用服务实例的清单,在发送请求前,自己通过负载均衡算法选出一个实例,直接向该实例发起请求。
二、 Ribbon 实现客户端负载均衡的核心原理
Ribbon 的核心是通过几个接口组件配合工作来实现的:
ServerList(服务列表获取) Ribbon 会从服务注册中心(如 Eureka、Consul)或者本地配置中拉取目标服务的所有可用实例列表。拉取后,会将列表缓存在本地,并定期(默认30秒)更新。ServerListFilter(服务列表过滤) 通过ServerList拿到所有实例后,Ribbon 会根据配置的过滤条件(如区域感知 ZoneAffinity)过滤掉一部分不符合要求的实例,提高请求效率。IRule(负载均衡策略) 这是最核心的组件。Ribbon 提供了多种负载均衡算法,默认是轮询(RoundRobinRule)。其他常用的还有:RandomRule:随机RetryRule:重试(先轮询,失败后重试其他节点)WeightedResponseTimeRule:根据响应时间加权BestAvailableRule:选择并发请求最小的实例
Ping(心跳检测) Ribbon 会在后台启动一个定时任务(PingTask),定期去 Ping 服务列表中的实例,判断其是否存活,如果发现某实例不可用,会将其从本地缓存列表中剔除。ILoadBalancer(负载均衡器) 这是整个流程的调度者。它整合了上述的ServerList、ServerListFilter、IRule和Ping,对外提供chooseServer()方法,供客户端调用以获取一个具体的服务实例。
三、 Ribbon 的工作流程(执行步骤)
- 初始化/更新:Ribbon 启动时,通过
ServerList从 Eureka 拉取服务实例列表,并缓存在本地。 - 拦截请求:当使用
@LoadBalanced注解修饰的RestTemplate发起请求时(如http://user-service/api/user/1),Ribbon 的拦截器会拦截该请求。 - 选择实例:拦截器调用
ILoadBalancer的chooseServer()方法。 - 执行算法:
ILoadBalancer调用配置的IRule(如轮询),结合Ping机制维护的存活列表,选出一个具体的服务实例(如192.168.1.100:8080)。 - 替换并请求:将原 URL 中的服务名(
user-service)替换为真实的 IP 和端口,发起最终的 HTTP 请求。 - 后台维护:后台线程持续更新服务列表并剔除死节点。
四、 代码层面的体现
在 Spring Cloud 中,只需在 RestTemplate 的 Bean 上加上 @LoadBalanced 注解,Spring 就会自动为这个 RestTemplate 注入 Ribbon 的拦截器,从而开启客户端负载均衡功能:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
(注:在较新的 Spring Cloud 版本中,Ribbon 已进入维护状态,官方推荐使用 Spring Cloud LoadBalancer 替代,但核心的实现思想和架构设计是相通的。)