思考:客户端负载均衡和服务器负载均衡有什么区别?
一、为啥微服务离不开负载均衡?
咱们先从一个常见场景说起:假设你做了个电商项目,“订单服务” 要调用 “商品服务” 查库存。一开始商品服务就 1 个实例,没问题;但用户多了,1 个实例扛不住,你扩容到 3 个 —— 这时候问题来了:订单服务该给哪个商品服务实例发请求?
并且不可以随便发,万一某个实例已经快满了,再发请求就会超时;也不能只发一个,其他实例闲得慌。这时候 “负载均衡” 就派上用场了:它就像个 “智能调度员”,把请求合理分配给各个实例,既避免单点过载,又能充分利用资源。
在 Spring Cloud 生态里,这个 “调度员” 有两种存在形式:一种在调用方(客户端),一种在统一入口(服务端)。
二、客户端负载均衡
客户端负载均衡,典型代表是 Spring Cloud 里的 Ribbon 和 Spring Cloud LoadBalancer。它的核心逻辑是:调用服务的一方(客户端),自己决定该发请求给哪个服务实例。
1. 原理:
举个具体例子:订单服务(客户端)调用商品服务(服务端),流程是这样的:
订单服务 → 注册中心(Nacos/Eureka)→ 获取商品服务实例列表 → 客户端负载均衡组件选实例 → 直接调用
- 拉取实例列表:订单服务启动时,会从注册中心(比如 Nacos)拉取商品服务的所有实例地址(比如
192.168.1.100:8081
、192.168.1.101:8081
),存在本地缓存。 - 选择实例:当订单服务要调用商品服务时,本地的负载均衡组件(比如 Ribbon)会根据预设策略(比如轮询、加权随机),从缓存的实例列表里挑一个。
- 直接调用:订单服务直接向选中的商品服务实例发请求,中间没有额外的 “中间商”。
2. Spring Cloud 实战
其实在 Spring Cloud 里用客户端负载均衡特别简单,尤其是结合 OpenFeign(声明式调用)的时候,几乎零配置:
第一步:加依赖(以 Spring Cloud LoadBalancer 为例)
<!-- OpenFeign 依赖(自带 LoadBalancer 集成) -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Spring Cloud LoadBalancer 依赖 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
第二步:写 OpenFeign 接口
// 商品服务的 Feign 接口
@FeignClient(value = "product-service") // 服务名(注册中心里的名字)
public interface ProductFeignClient {// 调用商品服务的查库存接口@GetMapping("/product/stock/{productId}")Integer getStock(@PathVariable("productId") Long productId);
}
第三步:直接使用
@Service
public class OrderService {@Autowiredprivate ProductFeignClient productFeignClient;// 创建订单时查库存public void createOrder(Long productId) {// 这里调用 Feign 接口时,LoadBalancer 会自动做负载均衡Integer stock = productFeignClient.getStock(productId);if (stock <= 0) {throw new RuntimeException("商品库存不足!");}// ... 其他创建订单逻辑}
}
就这么简单!Spring Cloud LoadBalancer 会自动帮你做实例选择,默认是轮询策略,想改策略也容易,比如改加权随机,只需加个配置类就行。
3. 客户端负载均衡的优缺点
优点:
- 无额外网络开销:客户端直接调用服务实例,不用经过中间转发,延迟低。
- 去中心化:没有集中的 “调度中心”,就算某个客户端出问题,不影响其他服务,系统更抗造。
- 灵活:不同客户端可以用不同策略,比如订单服务调用商品服务用轮询,用户服务调用商品服务用最少连接。
缺点:
- 客户端变复杂:每个服务作为 “客户端” 时,都要集成负载均衡组件,增加了一点点开发和维护成本(不过 Spring Cloud 已经帮我们封装得很好了,这点影响不大)。
- 策略难统一:如果想给所有客户端改策略,得一个个改配置(比如从轮询改最少连接),不如服务端集中管理方便。
三、服务端负载均衡
服务端负载均衡,典型代表是 Nginx、HAProxy,还有硬件级的 F5。它的核心逻辑是:所有请求先到一个统一的 “入口”(负载均衡器),再由这个入口把请求转发给具体的服务实例。
1. 原理:
还是拿电商举例,这次是用户通过 App 访问 “订单服务”,流程是这样的:
用户 App → Nginx(负载均衡器)→ 订单服务实例 1/2/3
- 请求集中入口:用户 App 发请求时,不是直接找订单服务,而是发给 Nginx(比如
api.youreshop.com
指向 Nginx 的地址)。 - Nginx 选实例:Nginx 配置里存了订单服务的所有实例地址,它会根据策略(比如轮询、IP 哈希)选一个实例。
- 转发请求:Nginx 把请求转发给选中的订单服务实例,实例处理完后,再通过 Nginx 把响应返回给 App(也可以直接返回,看配置)。
2. 服务端负载均衡的优缺点
优点:
- 对客户端透明:客户端(比如 App)不用关心后端有多少实例,只需记住 Nginx 的地址就行,开发简单。
- 集中管理:策略改起来方便,比如想给订单服务加权重,只需改 Nginx 配置,不用动任何服务。
- 功能丰富:Nginx 不仅能做负载均衡,还能做静态资源缓存、限流、WAF 防护(防黑客攻击),一个组件顶多个用。
缺点:
- 单点风险:如果 Nginx 挂了,所有请求都进不来,整个系统就瘫了(不过可以搞 Nginx 集群,主从备份,解决单点问题)。
- 额外网络开销:请求要经过 “App → Nginx → 服务实例” 两次网络跳转,比客户端负载均衡多一点延迟(但 Nginx 性能极强,一般情况下影响不大)。
- 性能瓶颈:如果请求量特别大(比如每秒几十万次),Nginx 可能会成为瓶颈(这时候可以用 HAProxy 或者硬件 F5,性能更强)。
四、核心对比
对比维度 | 客户端负载均衡(Ribbon/LoadBalancer) | 服务端负载均衡(Nginx/HAProxy) |
---|---|---|
调度位置 | 调用方(客户端服务内部) | 统一入口(负载均衡器) |
网络开销 | 一次跳转(客户端→服务实例) | 两次跳转(客户端→负载均衡器→服务实例) |
单点风险 | 无(去中心化) | 有(需做集群备份) |
策略管理 | 分散配置(每个客户端独立) | 集中配置(改负载均衡器就行) |
客户端依赖 | 需集成负载均衡组件 | 无(客户端只认负载均衡器地址) |
适用场景 | 微服务内部服务间调用 | 外部请求(App/Web)接入微服务 |
典型组件 | Ribbon、Spring Cloud LoadBalancer | Nginx、HAProxy、F5 |
五、总结
负载均衡没有 “绝对最好” 的方案,只有 “最合适” 的场景。
- 如果是微服务内部服务间调用,优先用客户端负载均衡(Spring Cloud LoadBalancer),快、灵活、无单点。
- 如果是外部请求接入微服务,优先用服务端负载均衡(Nginx),好管理、能防护、对客户端友好。
- 复杂项目就两者结合,外部 Nginx 扛入口,内部 LoadBalancer 提效率,这样的架构既稳定又高效。