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

从底层逻辑到实战落地:服务端与客户端负载均衡器的深度拆解

在分布式系统架构中,负载均衡器就像 “交通指挥官”,决定着请求如何分配到后端服务节点。随着微服务、云原生技术的普及,服务端负载均衡器(如 Nginx)和客户端负载均衡器(如 Spring Cloud LoadBalancer)的应用场景越来越广泛,但很多开发者仅停留在 “会用” 层面,对两者的底层差异、选型逻辑和实战细节理解不深。本文将从定义、原理、算法、实战四个维度,用通俗语言讲透核心差异,搭配可直接运行的代码案例和架构图,帮你既夯实基础又解决实际问题。

一、先搞懂基础:什么是负载均衡器?

在分布式系统中,单个服务节点的处理能力有限(如 CPU、内存、网络带宽),当请求量超过节点承载上限时,会出现响应延迟、服务宕机等问题。负载均衡器(Load Balancer) 的核心作用是:将海量请求 “均匀” 分配到多个后端服务节点,实现 “削峰填谷”,同时保证服务的高可用、高并发和扩展性。

从 “决策位置” 来看,负载均衡器主要分为两类:

  • 服务端负载均衡器:决策逻辑在独立的 “中间服务” 中(如 Nginx、HAProxy),客户端无需感知后端节点,所有请求先发送到负载均衡器,再由其转发。
  • 客户端负载均衡器:决策逻辑在 “客户端代码” 中(如 Spring Cloud LoadBalancer),客户端先获取后端所有节点列表,再自行决定将请求发送到哪个节点。

为了更直观理解两者的定位,先看一张架构对比图:

从架构图可见,两者的核心差异在于 “负载均衡决策的执行位置” 和 “是否依赖服务注册中心”。接下来,我们分别深入拆解两者的底层逻辑。

二、服务端负载均衡器:集中式的 “交通枢纽”

服务端负载均衡器是最传统、最广泛的负载均衡方案,本质是一个 “集中式调度中心”。客户端完全无需关心后端节点,只需将请求发送到负载均衡器,剩下的转发逻辑全由负载均衡器处理。

2.1 核心原理:三步完成请求转发

服务端负载均衡器的工作流程可拆解为 “接收请求→选择节点→转发请求” 三步,具体逻辑如下:

关键特点:

  1. 集中式决策:所有请求的转发逻辑由单个(或集群)负载均衡器统一处理,客户端无感知。
  2. 无需服务注册中心:后端节点列表通常配置在负载均衡器中(如 Nginx 的upstream),无需额外组件。
  3. 存在单点风险:单个负载均衡器故障会导致整个服务不可用,因此生产环境需部署集群(如 Nginx+Keepalived)。

2.2 典型组件:Nginx 与 HAProxy

服务端负载均衡器的主流组件有 Nginx(轻量、高性能)和 HAProxy(支持 TCP/UDP,适合复杂场景),两者均开源且稳定,以下为实战配置案例。

2.2.1 Nginx 实战:HTTP 请求负载均衡

Nginx 是目前最流行的服务端负载均衡器,支持 HTTP/HTTPS 协议,常用负载均衡算法包括轮询、权重、IP 哈希等。以下是基于 Nginx 1.25.3(最新稳定版)的配置案例,实现对 3 个后端 Java 服务的负载均衡。

1. 安装 Nginx

# 以CentOS 8为例
yum install -y nginx
systemctl start nginx
systemctl enable nginx

2. 配置负载均衡(/etc/nginx/nginx.conf)

# 全局配置
worker_processes auto; # 自动匹配CPU核心数
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;events {worker_connections 1024; # 每个worker进程的最大连接数
}http {include /etc/nginx/mime.types;default_type application/octet-stream;# 日志格式log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log /var/log/nginx/access.log main;sendfile on;keepalive_timeout 65;# 后端服务节点列表(upstream是Nginx负载均衡的核心模块)upstream java-service {# 1. 轮询(默认):按顺序依次转发server 192.168.1.101:8080;server 192.168.1.102:8080;server 192.168.1.103:8080;# 2. 权重(weight):权重越高,接收请求越多(适合节点性能不均场景)# server 192.168.1.101:8080 weight=5;# server 192.168.1.102:8080 weight=3;# server 192.168.1.103:8080 weight=2;# 3. IP哈希(ip_hash):同一IP的请求始终转发到同一节点(解决会话共享问题)# ip_hash;# 4. 健康检查:失败3次后标记为宕机,30秒后重试max_fails 3;fail_timeout 30s;}# 虚拟主机配置server {listen 80;server_name localhost;# 所有请求转发到upstream配置的节点列表location / {proxy_pass http://java-service;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;}}
}

3. 验证配置并启动

# 检查配置文件语法
nginx -t
# 重新加载配置(无需重启服务)
nginx -s reload

4. 测试负载均衡效果在 3 个后端节点的8080端口部署一个简单的 Java 接口(返回当前节点 IP),通过curl http://localhost多次请求,观察返回的 IP 是否符合配置的算法(如轮询则依次返回 101、102、103)。

2.2.2 HAProxy 实战:TCP 协议负载均衡

HAProxy 支持 HTTP 和 TCP 协议,适合需要对数据库(如 MySQL 主从)、消息队列(如 RabbitMQ)进行负载均衡的场景。以下是基于 HAProxy 2.9.6(最新稳定版)的 TCP 负载均衡配置,实现对 2 个 MySQL 节点的读写分离。

1. 安装 HAProxy

yum install -y haproxy
systemctl start haproxy
systemctl enable haproxy

2. 配置负载均衡(/etc/haproxy/haproxy.cfg)

# 全局配置
globallog         127.0.0.1 local2 infochroot      /var/lib/haproxypidfile     /var/run/haproxy.pidmaxconn     4000user        haproxygroup       haproxydaemon# 默认配置
defaultsmode                    tcp # 协议模式:tcp(适用于MySQL)log                     globaloption                  tcplogoption                  dontlognulloption                  redispatchretries                 3timeout queue           1mtimeout connect         10stimeout client          1mtimeout server          1mtimeout check           10smaxconn                 3000# 监控页面(可通过http://localhost:8080/haproxy?stats访问)
listen statsbind :8080stats uri /haproxy?statsstats auth admin:123456 # 用户名密码# MySQL读写分离配置:读请求转发到从库,写请求转发到主库
listen mysql-writebind :3306 # 对外暴露3306端口(与MySQL默认端口一致)server mysql-master 192.168.1.201:3306 check inter 1000 rise 2 fall 3 # 主库(写)listen mysql-readbind :3307server mysql-slave1 192.168.1.202:3306 check inter 1000 rise 2 fall 3 # 从库1(读)server mysql-slave2 192.168.1.203:3306 check inter 1000 rise 2 fall 3 # 从库2(读)

3. 测试 MySQL 负载均衡

  • 写请求:通过mysql -h localhost -P 3306 -u root -p连接,执行INSERT语句,数据会写入主库(192.168.1.201)。
  • 读请求:通过mysql -h localhost -P 3307 -u root -p连接,执行SELECT语句,请求会轮询转发到从库 202 和 203。

2.3 服务端负载均衡器的优缺点

优点缺点
客户端无感知:无需修改客户端代码,接入成本低存在性能瓶颈:所有请求需经过负载均衡器,高并发下可能成为瓶颈
配置集中:后端节点变更只需修改负载均衡器配置,无需重启客户端单点风险:单个负载均衡器故障会导致服务不可用,需额外部署集群(如 Keepalived)
支持多种协议:可对 HTTP、TCP、UDP 等协议进行负载均衡无法感知客户端状态:无法根据客户端的网络延迟、负载情况动态调整转发策略
自带健康检查:无需额外开发,负载均衡器可自动过滤宕机节点转发延迟:多一次网络跳转(客户端→负载均衡器→后端节点),增加毫秒级延迟

三、客户端负载均衡器:分布式的 “自主决策”

客户端负载均衡器是微服务架构下的产物,核心是 “将负载均衡逻辑嵌入客户端代码”。客户端先从服务注册中心(如 Nacos、Eureka)获取后端节点列表,再通过内置算法选择节点,直接发送请求,无需经过中间负载均衡器。

3.1 核心原理:四步完成自主决策

客户端负载均衡器的工作流程比服务端多一步 “获取节点列表”,具体逻辑如下:

关键特点:

  1. 分布式决策:每个客户端独立执行负载均衡逻辑,无集中式瓶颈。
  2. 依赖服务注册中心:节点列表动态从注册中心获取,支持服务自动发现(如节点新增 / 下线后,客户端无需重启)。
  3. 低延迟:客户端直接与后端节点通信,减少一次网络跳转,降低延迟。

3.2 典型组件:Spring Cloud LoadBalancer 与 Dubbo LoadBalancer

客户端负载均衡器的主流组件有 Spring Cloud LoadBalancer(Spring Cloud 官方推荐,替代 Ribbon)和 Dubbo LoadBalancer(Dubbo 框架内置),以下为基于 JDK 17、Spring Boot 3.2.0(最新稳定版)的实战案例。

3.2.1 前置准备:搭建服务注册中心(Nacos)

客户端负载均衡器依赖服务注册中心,这里选择 Alibaba Nacos 2.3.2(最新稳定版)作为注册中心,步骤如下:

  1. 下载并启动 Nacos
# 下载Nacos(Linux版)
wget https://github.com/alibaba/nacos/releases/download/2.3.2/nacos-server-2.3.2.tar.gz
# 解压
tar -zxvf nacos-server-2.3.2.tar.gz
# 启动Nacos(单机模式)
cd nacos/bin
sh startup.sh -m standalone
  1. 访问 Nacos 控制台打开浏览器访问http://localhost:8848/nacos,默认用户名 / 密码为nacos/nacos,登录后即可管理服务。
3.2.2 Spring Cloud LoadBalancer 实战:微服务接口调用

Spring Cloud LoadBalancer(SCL)是 Spring Cloud 官方提供的客户端负载均衡器,与 Spring Boot、Nacos 无缝集成,支持轮询、随机、权重等算法。以下实现 “订单服务(客户端)调用用户服务(后端节点)” 的负载均衡案例。

1. 项目结构

spring-cloud-lb-demo/
├── order-service(客户端,集成SCL)
└── user-service(后端服务,部署3个节点)

2. 后端服务:user-service(提供用户查询接口)

2.1 依赖配置(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>user-service</artifactId><version>1.0.0</version><name>user-service</name><dependencies><!-- Spring Boot Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Nacos服务注册与发现 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>2023.0.0.0</version> <!-- 与Spring Boot 3.2匹配的最新版 --></dependency><!-- Lombok(@Slf4j) --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version><scope>provided</scope></dependency><!-- Swagger3(接口文档) --><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version></dependency><!-- Spring工具类(ObjectUtils、StringUtils) --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>6.1.1</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>3.2.0</version><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build>
</project>
2.2 配置文件(application.yml)
server:port: ${PORT:8081} # 端口可通过环境变量指定,方便部署多节点spring:application:name: user-service # 服务名(注册到Nacos的名称)cloud:nacos:discovery:server-addr: localhost:8848 # Nacos注册中心地址# Swagger3配置
springdoc:api-docs:path: /api-docsswagger-ui:path: /swagger-ui.html
2.3 启动类(UserServiceApplication.java)
package com.example.userservice;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;/*** 用户服务启动类* 作者:ken*/
@SpringBootApplication
@EnableDiscoveryClient // 开启服务注册与发现
@OpenAPIDefinition(info = @Info(title = "用户服务接口文档",version = "1.0.0",description = "提供用户查询、新增等接口")
)
public class UserServiceApplication {public static void main(String[] args) {SpringApplication.run(UserServiceApplication.class, args);}
}
2.4 控制器(UserController.java)
package com.example.userservice.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;/*** 用户控制器* 作者:ken*/
@RestController
@RequestMapping("/user")
@Slf4j
@Tag(name = "用户接口", description = "用户查询相关接口")
public class UserController {@Value("${server.port}")private String serverPort; // 当前服务端口(用于区分不同节点)/*** 根据用户ID查询用户信息* @param userId 用户ID(不能为空)* @return 包含用户ID和当前服务端口的字符串*/@GetMapping("/{userId}")@Operation(summary = "查询用户信息", description = "根据用户ID查询用户,返回当前处理请求的服务端口")public String getUserById(@Parameter(description = "用户ID", required = true)@PathVariable String userId) {// 校验用户ID非空(遵循阿里巴巴开发手册:字符串判空用StringUtils.hasText)StringUtils.hasText(userId, "用户ID不能为空");String result = String.format("用户ID:%s,处理节点端口:%s", userId, serverPort);log.info("查询用户信息:{}", result);return result;}
}
2.5 部署多节点

通过指定不同端口,启动 3 个 user-service 节点:

# 节点1:端口8081
java -jar user-service-1.0.0.jar --PORT=8081
# 节点2:端口8082
java -jar user-service-1.0.0.jar --PORT=8082
# 节点3:端口8083
java -jar user-service-1.0.0.jar --PORT=8083

启动后,登录 Nacos 控制台(http://localhost:8848/nacos),在 “服务管理→服务列表” 中可看到user-service有 3 个实例,状态为 “健康”。

3. 客户端:order-service(集成 SCL 调用 user-service)

3.1 依赖配置(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>order-service</artifactId><version>1.0.0</version><name>order-service</name><dependencies><!-- Spring Boot Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Nacos服务注册与发现 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>2023.0.0.0</version></dependency><!-- Spring Cloud LoadBalancer(客户端负载均衡) --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId><version>4.1.0</version> <!-- 与Spring Boot 3.2匹配的最新版 --></dependency><!-- RestTemplate(用于调用HTTP接口) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><!-- Lombok(@Slf4j) --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version><scope>provided</scope></dependency><!-- Swagger3 --><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version></dependency><!-- Spring工具类 --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>6.1.1</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>3.2.0</version><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build>
</project>
3.2 配置文件(application.yml)
server:port: 8090 # 订单服务端口spring:application:name: order-servicecloud:nacos:discovery:server-addr: localhost:8848loadbalancer:nacos:enabled: true # 启用Nacos作为负载均衡的服务发现源clients:user-service: # 针对user-service的负载均衡配置nlb:enabled: falsehealth-check:enabled: true # 启用健康检查interval: 5000 # 健康检查间隔(5秒)
3.3 配置 RestTemplate(集成 SCL)
package com.example.orderservice.config;import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;/*** RestTemplate配置类(集成Spring Cloud LoadBalancer)* 作者:ken*/
@Configuration
public class RestTemplateConfig {/*** 创建RestTemplate实例,并添加@LoadBalanced注解启用客户端负载均衡* @return RestTemplate实例*/@Bean@LoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}
}
3.4 订单服务控制器(OrderController.java)
package com.example.orderservice.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;/*** 订单控制器(调用用户服务)* 作者:ken*/
@RestController
@RequestMapping("/order")
@Slf4j
@Tag(name = "订单接口", description = "订单相关接口,包含调用用户服务的逻辑")
public class OrderController {@Autowiredprivate RestTemplate restTemplate;// 用户服务的服务名(无需写IP和端口,SCL会自动解析为具体节点)private static final String USER_SERVICE_URL = "http://user-service/user/%s";/*** 根据订单ID查询订单(内部调用用户服务)* @param orderId 订单ID* @param userId 用户ID* @return 订单信息+用户服务处理节点*/@GetMapping("/{orderId}/user/{userId}")@Operation(summary = "查询订单及用户信息", description = "根据订单ID和用户ID查询,内部调用用户服务")public String getOrderWithUser(@Parameter(description = "订单ID", required = true)@PathVariable String orderId,@Parameter(description = "用户ID", required = true)@PathVariable String userId) {// 校验参数StringUtils.hasText(orderId, "订单ID不能为空");StringUtils.hasText(userId, "用户ID不能为空");// 调用用户服务(使用服务名而非IP,SCL会自动进行负载均衡)String userResult = restTemplate.getForObject(String.format(USER_SERVICE_URL, userId),String.class);String result = String.format("订单ID:%s,%s", orderId, userResult);log.info("查询订单信息:{}", result);return result;}
}
3.5 启动类(OrderServiceApplication.java)
package com.example.orderservice;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;/*** 订单服务启动类* 作者:ken*/
@SpringBootApplication
@EnableDiscoveryClient
@OpenAPIDefinition(info = @Info(title = "订单服务接口文档",version = "1.0.0",description = "提供订单查询接口,内部调用用户服务")
)
public class OrderServiceApplication {public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);}
}
3.6 测试负载均衡效果
  1. 启动 order-service(端口 8090)。
  2. 通过 Swagger3 接口文档测试:访问http://localhost:8090/swagger-ui.html,找到/order/{orderId}/user/{userId}接口,多次调用(如 orderId=1001,userId=2001)。
  3. 观察返回结果:处理节点端口会依次为 8081、8082、8083(SCL 默认使用轮询算法),说明负载均衡生效。
3.2.3 Dubbo LoadBalancer 实战:RPC 接口负载均衡

Dubbo 是阿里巴巴开源的 RPC 框架,内置了客户端负载均衡器,支持轮询、随机、一致性哈希等算法。以下实现 “商品服务(客户端)调用库存服务(后端节点)” 的 RPC 负载均衡案例,基于 Dubbo 3.3.0(最新稳定版)。

1. 项目结构

dubbo-lb-demo/
├── product-service(客户端,Dubbo消费者)
└── stock-service(后端服务,Dubbo提供者,部署2个节点)

2. 后端服务:stock-service(Dubbo 提供者)

2.1 依赖配置(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>stock-service</artifactId><version>1.0.0</version><name>stock-service</name><dependencies><!-- Spring Boot Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Dubbo Spring Boot Starter --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>3.3.0</version></dependency><!-- Nacos注册中心适配Dubbo --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-nacos</artifactId><version>3.3.0</version></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version><scope>provided</scope></dependency><!-- Swagger3 --><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>3.2.0</version><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build>
</project>
2.2 配置文件(application.yml)
server:port: ${PORT:8084}spring:application:name: stock-servicedubbo:application:name: stock-serviceregistry:address: nacos://localhost:8848 # 注册中心地址protocol:name: dubbo # 协议名称port: ${DUBBO_PORT:20880} # Dubbo服务端口(默认20880)scan:base-packages: com.example.stockservice.service # 扫描Dubbo服务实现类
2.3 Dubbo 服务接口(StockService.java)
package com.example.stockservice.service;/*** 库存服务接口(Dubbo服务)* 作者:ken*/
public interface StockService {/*** 根据商品ID查询库存* @param productId 商品ID* @return 库存数量+处理节点端口*/String getStockByProductId(String productId);
}
2.4 Dubbo 服务实现类(StockServiceImpl.java)
package com.example.stockservice.service.impl;import com.example.stockservice.service.StockService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;/*** 库存服务实现类(Dubbo服务提供者)* 作者:ken*/
@DubboService(version = "1.0.0") // 标记为Dubbo服务,指定版本
@Slf4j
public class StockServiceImpl implements StockService {@Value("${server.port}")private String serverPort;@Value("${dubbo.protocol.port}")private String dubboPort;/*** 查询商品库存* @param productId 商品ID(不能为空)* @return 库存信息*/@Overridepublic String getStockByProductId(String productId) {StringUtils.hasText(productId, "商品ID不能为空");// 模拟库存数量(随机100-200之间)int stock = (int) (Math.random() * 100 + 100);String result = String.format("商品ID:%s,库存数量:%d,处理节点(HTTP端口:%s,Dubbo端口:%s)",productId, stock, serverPort, dubboPort);log.info("查询库存:{}", result);return result;}
}
2.5 启动类(StockServiceApplication.java)
package com.example.stockservice;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;/*** 库存服务启动类* 作者:ken*/
@SpringBootApplication
@OpenAPIDefinition(info = @Info(title = "库存服务接口文档",version = "1.0.0",description = "Dubbo库存服务提供者")
)
public class StockServiceApplication {public static void main(String[] args) {SpringApplication.run(StockServiceApplication.class, args);}
}
2.6 部署多节点

启动 2 个 stock-service 节点,指定不同的 HTTP 端口和 Dubbo 端口:

# 节点1:HTTP端口8084,Dubbo端口20880
java -jar stock-service-1.0.0.jar --PORT=8084 --DUBBO_PORT=20880
# 节点2:HTTP端口8085,Dubbo端口20881
java -jar stock-service-1.0.0.jar --PORT=8085 --DUBBO_PORT=20881

登录 Nacos 控制台,在 “服务管理→服务列表” 中可看到providers:com.example.stockservice.service.StockService:1.0.0有 2 个实例。

3. 客户端:product-service(Dubbo 消费者)

3.1 依赖配置(pom.xml)

与 stock-service 一致,只需确保 Dubbo 和 Nacos 依赖版本匹配。

3.2 配置文件(application.yml)
server:port: 8091spring:application:name: product-servicedubbo:application:name: product-serviceregistry:address: nacos://localhost:8848consumer:timeout: 3000 # 调用超时时间(3秒)loadbalance: roundrobin # 负载均衡算法(roundrobin=轮询,random=随机,consistenthash=一致性哈希)
3.3 引入 Dubbo 服务接口(需与提供者一致)

直接复制 stock-service 的StockService.java接口到 product-service 的相同包路径下(com.example.stockservice.service)。

3.4 商品服务控制器(ProductController.java)
package com.example.productservice.controller;import com.example.stockservice.service.StockService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;/*** 商品控制器(调用库存服务)* 作者:ken*/
@RestController
@RequestMapping("/product")
@Slf4j
@Tag(name = "商品接口", description = "商品相关接口,内部调用库存服务(Dubbo)")
public class ProductController {// 引用Dubbo服务(version需与提供者一致)@DubboReference(version = "1.0.0")private StockService stockService;/*** 根据商品ID查询商品及库存* @param productId 商品ID* @return 商品信息+库存信息*/@GetMapping("/{productId}/stock")@Operation(summary = "查询商品及库存", description = "根据商品ID查询,内部调用Dubbo库存服务")public String getProductWithStock(@Parameter(description = "商品ID", required = true)@PathVariable String productId) {StringUtils.hasText(productId, "商品ID不能为空");// 调用Dubbo库存服务(负载均衡由Dubbo自动处理)String stockResult = stockService.getStockByProductId(productId);String result = String.format("商品查询成功,%s", stockResult);log.info("查询商品及库存:{}", result);return result;}
}
3.5 启动类(ProductServiceApplication.java)
package com.example.productservice;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;/*** 商品服务启动类* 作者:ken*/
@SpringBootApplication
@OpenAPIDefinition(info = @Info(title = "商品服务接口文档",version = "1.0.0",description = "Dubbo库存服务消费者")
)
public class ProductServiceApplication {public static void main(String[] args) {SpringApplication.run(ProductServiceApplication.class, args);}
}
3.6 测试负载均衡效果

访问http://localhost:8091/swagger-ui.html,调用/product/{productId}/stock接口(如 productId=3001),多次请求后观察返回结果:处理节点会在 8084(Dubbo 20880)和 8085(Dubbo 20881)之间轮询,说明 Dubbo LoadBalancer 生效。

3.3 客户端负载均衡器的优缺点

优点缺点
无集中式瓶颈:每个客户端独立决策,支持高并发客户端侵入性:需在客户端集成负载均衡逻辑(如引入 SCL、Dubbo 依赖)
低延迟:直接与后端节点通信,减少一次网络跳转配置分散:每个客户端需单独配置负载均衡算法,变更成本高
动态感知节点:通过服务注册中心实时获取节点列表,支持自动扩缩容健康检查依赖客户端:需客户端自行实现或依赖框架的健康检查逻辑
支持个性化算法:可根据客户端需求自定义负载均衡策略(如基于延迟的算法)客户端版本依赖:需确保所有客户端的负载均衡组件版本一致,避免兼容性问题

四、核心差异对比:一张表讲透关键区别

服务端与客户端负载均衡器的差异体现在多个维度,以下从 “决策位置”“依赖组件”“性能” 等 10 个核心维度进行对比,帮你快速理清两者的定位:

对比维度服务端负载均衡器(如 Nginx)客户端负载均衡器(如 SCL)
决策位置独立的中间服务(集中式)客户端代码(分布式)
依赖组件无需服务注册中心(节点列表配置在负载均衡器)必须依赖服务注册中心(如 Nacos、Eureka)
网络跳转2 次(客户端→负载均衡器→后端节点)1 次(客户端→后端节点)
性能瓶颈有(所有请求经过负载均衡器,需部署集群)无(分布式决策,水平扩展)
客户端侵入性无(客户端无需修改代码)有(需集成负载均衡组件)
配置管理集中式(修改负载均衡器配置即可)分散式(每个客户端需单独配置)
健康检查负载均衡器自带(如 Nginx 的max_fails客户端或框架实现(如 SCL 的健康检查)
适用场景1. 非微服务架构(如传统单体服务集群)2. 对客户端无侵入要求的场景3. TCP/UDP 协议负载均衡(如 MySQL、RabbitMQ)1. 微服务架构(如 Spring Cloud、Dubbo)2. 高并发、低延迟要求的场景3. 需要动态扩缩容的场景
典型组件Nginx、HAProxy、F5(硬件)Spring Cloud LoadBalancer、Dubbo LoadBalancer、Ribbon(已淘汰)
故障影响负载均衡器集群故障会导致整个服务不可用单个客户端故障仅影响自身,不影响其他客户端

五、选型建议:什么时候用哪种?

负载均衡器的选型没有 “绝对正确”,需结合业务场景、架构特点和性能需求综合判断,以下为具体选型建议:

5.1 优先选服务端负载均衡器的场景

  1. 传统单体服务集群:如 Java 单体服务部署多个节点,客户端为 Web 前端或移动端,无需侵入客户端代码,Nginx 是最佳选择。
  2. TCP/UDP 协议负载均衡:如 MySQL 主从读写分离、RabbitMQ 集群负载均衡,HAProxy 支持 TCP 协议,比客户端负载均衡器更合适。
  3. 对客户端无侵入要求的场景:如第三方服务调用(无法修改第三方客户端代码),只能通过服务端负载均衡器转发请求。
  4. 需要统一流量控制的场景:如限流、熔断、SSL 卸载(HTTPS 转 HTTP),Nginx 可通过插件(如 ngx_http_limit_req_module)统一实现,无需在每个客户端开发。

5.2 优先选客户端负载均衡器的场景

  1. 微服务架构:如 Spring Cloud、Dubbo 微服务,服务节点动态扩缩容频繁,客户端需实时获取节点列表,SCL/Dubbo LoadBalancer 更适配。
  2. 高并发、低延迟要求的场景:如秒杀系统、实时交易系统,客户端直接与后端节点通信,减少一次网络跳转,降低延迟。
  3. 需要个性化负载均衡算法的场景:如基于客户端网络延迟、节点负载的动态算法,客户端负载均衡器可自定义策略(如 SCL 可实现ReactorServiceInstanceLoadBalancer接口)。
  4. 分布式部署的客户端集群:如多个微服务客户端(订单、商品、支付)调用同一后端服务,客户端负载均衡器可实现分布式决策,避免集中式瓶颈。

5.3 混合使用场景

在复杂架构中,服务端与客户端负载均衡器可混合使用,实现 “多层负载均衡”,例如:

  • 场景:Spring Cloud 微服务架构,前端 Web 请求先经过 Nginx(服务端负载均衡),再由微服务客户端通过 SCL 调用后端服务(客户端负载均衡)。
  • 优势:Nginx 负责前端请求的限流、SSL 卸载和第一层负载均衡,SCL 负责微服务间的动态负载均衡,兼顾 “无侵入” 和 “低延迟”。

六、常见问题与避坑指南

在实际使用中,负载均衡器可能会遇到 “会话共享”“健康检查失效” 等问题,以下为常见问题及解决方案:

6.1 服务端负载均衡器:Nginx 会话共享问题

问题描述:使用 Nginx 的轮询算法时,同一用户的多次请求可能转发到不同节点,导致会话(Session)丢失(如登录状态失效)。

解决方案

  1. IP 哈希算法:在 Nginx 的upstream中配置ip_hash,同一 IP 的请求始终转发到同一节点(适合客户端 IP 固定的场景,如企业内部系统)。
  2. 会话共享组件:使用 Redis、Memcached 等分布式缓存存储 Session,所有节点共享 Session 数据(适合公网场景,如电商网站)。
    • 示例:Spring Boot 集成 Redis 实现 Session 共享(需引入spring-session-data-redis依赖)。

6.2 客户端负载均衡器:SCL 健康检查失效

问题描述:后端节点宕机后,SCL 仍会将请求转发到宕机节点,导致调用失败。

解决方案

  1. 启用 SCL 健康检查:在application.yml中配置spring.cloud.loadbalancer.health-check.enabled=true,SCL 会定期检查节点健康状态,过滤宕机节点。
  2. 配合 Nacos 健康检查:在 Nacos 中启用服务健康检查(默认开启),宕机节点会被 Nacos 标记为 “不健康”,SCL 从 Nacos 获取节点列表时会自动排除。

6.3 服务端负载均衡器:Nginx 高并发瓶颈

问题描述:单台 Nginx 在高并发下(如 10 万 QPS)会出现 CPU、内存占用过高,导致响应延迟。

解决方案

  1. 部署 Nginx 集群:使用 Keepalived 实现 Nginx 主从切换,避免单点故障,同时通过 DNS 轮询将请求分发到多台 Nginx。
  2. 优化 Nginx 配置
    • 调整worker_processes为 CPU 核心数(如worker_processes 8)。
    • 增大worker_connections(如worker_connections 10240)。
    • 启用gzip压缩,减少网络传输量。

七、总结

服务端负载均衡器是 “集中式的交通枢纽”,适合传统架构和无侵入场景;客户端负载均衡器是 “分布式的自主决策”,适配微服务和低延迟需求。两者没有 “优劣之分”,只有 “场景之别”。

在实际项目中,建议结合架构特点选择合适的负载均衡器,必要时可混合使用,实现 “多层负载均衡”,兼顾性能、可用性和扩展性。

http://www.dtcms.com/a/532247.html

相关文章:

  • 笔试强训(十三)
  • 基于罗伊适应模型的产后抑郁家庭护理干预研究综述​
  • Bright Data 抓取浏览器API实战:助力高效完成定向大规模数据稳定采集
  • 合肥市做效果图的网站最适合新人的写作网站
  • 关于做视频网站的一些代码南京网站制作公司怎么样
  • 6分钟制作TikTok游戏领域热门短视频分析AI Agent
  • 自由通讯的魔法:Go从零实现UDP/P2P 聊天工具
  • Cortex-M3-STM32F1 开发:(十二)HAL 库开发 ➤ SysTick 系统滴答定时器
  • go-ethereum core之以太网虚拟机EVM
  • 自己怎么免费做网站网站开发 合同
  • 网站如何做脚注一般使用的分辨率的显示密度是多少dpi )
  • 嵌入式开发中ln命令使用指南
  • C++模板进阶及特化实战指南
  • zenm自己做网站淮北建设网
  • 网站title keyword descriptionwordpress 分类筛选
  • 网站系统设计目标企业融资方案范本
  • 《AI 应用层革命(二)——从应用到生态:当智能体开始重塑世界》
  • 使用 Python 元类与属性实现惰性加载:Effective Python 第47条
  • 环广西世巡赛开战!维乐Senso Prime 坐垫助你竞速
  • DeepSeek讲“南辕北辙”者的志向
  • 做网站在线视频如何添加湘潭网站seo
  • 智能文本抽取:通过OCR、自然语言处理等多项技术,将非结构化文档转化为可读、可分析的数据资产
  • 许昌哪个网站做苗木网站建设怎么让百度搜索到
  • 代码训练LeetCode(49)插入区间
  • wordpress做游戏网站国家新闻大事
  • 【Macos】安装 macFUSE 和 SSHFS 实现在 Finder 中挂载服务器目录
  • 【高并发服务器】十、Connection连接管理模块设计与实现
  • 内网网站建设流程高佣联盟做成网站怎么做
  • Canvas 复杂交互步骤:从事件监听 to 重新绘制全流程
  • 【js】class中constructor如何接收动态值,如timeRange