Nginx 与 LVS 软件负载均衡对比及 Java 项目实践
软件负载均衡常见方案:Nginx(7 层负载均衡)与 LVS(Linux 内核 4 层负载均衡)。
本文梳理两者区别、选型方法、与 Java 项目搭配使用方式,以及 Nginx「插件」机制如何实现业务定制化。
一、先搞懂:4 层与 7 层是什么意思
这里的「层」指的是OSI 七层网络模型。负载均衡器工作在哪一层,决定了它能「看懂」多少报文内容,也决定了它的能力和性能上限。
| OSI 层 | 名称 | 典型协议 | 负载均衡器能看到什么 |
|---|---|---|---|
| 4 | 传输层(Transport Layer) | TCP / UDP | 只能看到 IP + 端口 |
| 7 | 应用层(Application Layer) | HTTP / HTTPS / DNS / SMTP | 能看到完整的报文内容(URL、Header、Cookie、Body) |
1. 4 层负载均衡(LVS 所在层)
- 工作在传输层,只看 IP + 端口,不关心应用层协议内容。
- 报文转发效率高,因为它不需要解析 HTTP 报文体、不关心你是 GET 还是 POST。
- 典型动作:客户端访问
VIP:80,LVS 根据调度算法把 TCP 连接转给后端某台真实服务器(Real Server),后端服务器处理后再直接回包(DR 模式下甚至不经 LVS 回包)。
2. 7 层负载均衡(Nginx 所在层)
- 工作在应用层,能看懂 HTTP 报文。
- 因此可以基于 URL 路径、Host 域名、Header、Cookie 等做更细粒度的路由分流。
- 典型动作:
/api/*转给后端 A 组 Java 服务,/static/*转给静态资源服务器,/admin/*做鉴权后再转给后端 B 组。
一句话记忆:4 层看门牌号(IP+端口),7 层看信件内容(HTTP 报文)。
二、Nginx 简介
Nginx 是一款高性能的HTTP 反向代理 / Web 服务器,同时也能做 4 层的 stream 代理(1.9.0 之后支持stream模块)。
核心特点:
- 事件驱动 + 非阻塞 I/O,单机可扛数万到十万级并发连接,内存占用低。
- 既是Web 服务器(直接返回静态文件),又是反向代理(转发到后端应用),还能做负载均衡。
- 配置灵活,支持 upstream 负载均衡算法、健康检查、限流、缓存、HTTPS、重写等。
- 生态丰富,可通过模块(module)扩展功能。
典型负载均衡配置示例:
upstream java_backend { # 负载均衡算法:轮询(默认)/ ip_hash / least_conn / weighted ip_hash; # 会话保持,同一 IP 固定到同一后端 server 10.0.0.11:8080 weight=3; # Java 节点 1,权重 3 server 10.0.0.12:8080 weight=2; # Java 节点 2,权重 2 server 10.0.0.13:8080 backup; # 备用节点,主力全挂才启用 keepalive 32; # 到后端的长连接池 } server { listen 80; server_name api.example.com; # 7 层路由:按路径分流 location /api/ { proxy_pass http://java_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /static/ { root /data/www; # 静态资源 Nginx 直接处理 expires 30d; } }支持的负载均衡算法:
| 算法 | 说明 |
|---|---|
| 轮询(默认) | 依次分给各后端 |
weight加权轮询 | 按权重分配,机器性能不同时使用 |
ip_hash | 按 client IP 哈希,实现会话保持 |
least_conn | 优先分给当前连接数最少的后端 |
random/consistent_hash(第三方) | 随机 / 一致性哈希 |
三、LVS 简介
LVS(Linux Virtual Server)是章文嵩博士发起的开源项目,集成在 Linux 内核中(ipvs模块),是纯粹的4 层负载均衡器。
核心特点:
- 内核态运行,不在用户态转发数据,转发性能极高,单机可达百万级 PPS / 十万级并发。
- 只做 4 层转发,不解析应用层内容,无法基于 URL/Header 分流。
- 调度器本身相对"傻",但稳定、可靠、吞吐大。
- 常与
keepalived配合实现高可用(VIP 漂移 + 健康检查 + LVS 规则管理)。
LVS 的三种工作模式(面试高频):
| 模式 | 工作原理 | 回包路径 | 优劣 |
|---|---|---|---|
| NAT | LVS 修改报文目标 IP 转给后端,后端回包也经 LVS 改源 IP | 必须经过 LVS | 简单,但 LVS 容易成瓶颈 |
| DR(Direct Routing,直连路由) | LVS 只改 MAC 地址,IP 不变;后端直接把回包回给客户端 | 不经 LVS 回包 | 性能最高,要求 LVS 与后端在同一二层网络 |
| TUN(隧道) | LVS 对原 IP 报文再封装一层 IP 隧道,后端解封装后直连回客户端 | 不经 LVS 回包 | 可跨网段,但要求后端支持 IP 隧道 |
LVS 调度算法:rr(轮询)、wrr(加权轮询)、lc(最少连接)、wlc(加权最少连接)、sh(源地址哈希)、lblc(基于局部性的最少连接)等。
四、Nginx vs LVS 核心区别
| 维度 | Nginx | LVS |
|---|---|---|
| 工作层级 | 主要 7 层(也支持 4 层 stream) | 纯 4 层 |
| 运行位置 | 用户态进程 | Linux 内核态(ipvs) |
| 转发性能 | 高(万级~十万级并发) | 极高(百万级 PPS) |
| 路由能力 | 可按 URL/Host/Header/Cookie 精细分流 | 只按 IP+端口 |
| 功能丰富度 | 高(代理、缓存、限流、TLS、重写、模块扩展) | 低(只做转发) |
| 配置难度 | 较易,热加载nginx -s reload | 较复杂,常配合 keepalived |
| 适用场景 | 中小流量 + 需要应用层策略 | 超大流量 + 入口转发 |
| 本质定位 | 应用层网关 | 流量分发器 |
一句话对比:LVS 是"高速公路收费站",只管车往哪个口分流,吞吐大但不懂车里装了什么;Nginx 是"分拣中心",能拆箱看内容、按内容分流,吞吐略低但功能丰富。
五、项目里如何选择
选择依据本质是看你的流量规模和是否需要应用层策略:
单选 Nginx 就够的场景
- 流量中等(单机万级并发以内)。
- 需要按 URL、域名、Header 分流(例如前后端分离、灰度发布、多租户路由)。
- 需要 TLS 卸载、静态资源缓存、限流鉴权等应用层能力。
- 大多数中小型 Java Web 项目的入口层。
单选 LVS 就够的场景
- 流量极大,入口只需把 TCP 连接均匀分到后端,无需应用层策略(例如 MySQL 读写分离的读请求分发、纯 TCP 服务的入口)。
- 对性能和稳定性要求极致,功能上够用就行。
两者结合(最常见的生产架构)
真正的高并发生产系统几乎都是LVS 在前、Nginx 在后的组合:
客户端 │ ▼ LVS(4 层,扛大流量,做入口分发 + VIP 高可用) │ ▼ Nginx 集群(7 层,做应用层路由、TLS 卸载、缓存、限流) │ ▼ Java 应用集群(Spring Boot / Tomcat)- LVS 负责"扛量":把海量连接均匀分发给多个 Nginx 节点,并用 keepalived 保证入口 VIP 高可用。
- Nginx 负责"干细活":TLS 卸载、按路径分流、限流熔断、静态资源直出,再代理到后端 Java 服务。
- Java 应用聚焦业务逻辑,不再操心流量分发。
决策口诀:要应用层策略 → Nginx;要扛海量流量 → LVS;两者都要 → LVS + Nginx 叠加。
六、搭配 Java 项目如何使用
典型部署拓扑
外网用户 │ ▼ [LVS + Keepalived] ← VIP 漂移,保证入口高可用 │ ▼ [Nginx × N] ← TLS 卸载、7 层路由、限流、静态资源 │ ▼ [Java 应用 × N] ← Spring Boot 内嵌 Tomcat,监听 8080 │ ▼ [数据库 / 缓存 / MQ]Java 端需要注意的点
获取真实客户端 IP
Nginx 代理后,Java 应用拿到的remoteAddr是 Nginx 的 IP。需要从X-Forwarded-For/X-Real-IP头取,Nginx 端要正确设置(见上文proxy_set_header)。会话保持(Session 粘性)
- 无状态服务(推荐):Java 应用设计成无状态,Session 存 Redis,任何节点都能处理请求。
- 有状态兜底:Nginx 用
ip_hash或基于 Cookie 的粘性(sticky模块)把同一用户固定到同一节点。
上游健康检查
- Nginx 开源版默认是被动检查(请求失败才标记 down)。需要主动健康检查可用
nginx_upstream_check_module第三方模块,或用商业版 Nginx Plus。 - Java 端建议暴露
/actuator/health(Spring Boot Actuator),供健康检查探活。
- Nginx 开源版默认是被动检查(请求失败才标记 down)。需要主动健康检查可用
长连接与连接池
- Nginx 到后端开启
keepalive,Java 端 Tomcat 连接器相应调大maxConnections/ acceptCount,避免连接排队。
- Nginx 到后端开启
优雅停机
- Java 应用发版时先在 Nginx 把该节点设为
down或从注册中心摘除,等流量排空再停 JVM,避免请求被打断。
- Java 应用发版时先在 Nginx 把该节点设为
一个最小可用配置
Nginx:
upstream java_app { least_conn; server 10.0.0.11:8080 max_fails=3 fail_timeout=30s; server 10.0.0.12:8080 max_fails=3 fail_timeout=30s; keepalive 32; } server { listen 443 ssl http2; server_name api.example.com; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; location /api/ { proxy_pass http://java_app; proxy_http_version 1.1; proxy_set_header Connection ""; 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 https; } }Spring Boot 关键配置(application.yml):
server:tomcat:max-connections:10000# 配合 Nginx 长连接上调accept-count:200threads:max:400management:endpoint:health:probes:enabled:true# 暴露健康检查供 Nginx/LVS 探活endpoints:web:exposure:include:health,info七、关于「Nginx 插件」—— 它指的是什么?如何实现定制化?
这句话里的"插件"泛指Nginx 的模块(module)扩展机制。Nginx 本身是模块化架构,绝大部分功能(即使是http、ssl、gzip这些基础能力)都是通过模块实现的。当内置模块满足不了业务需求时,就靠"插件/模块"来定制。
模块的三种类型
| 类型 | 说明 | 示例 |
|---|---|---|
| 官方内置模块 | 编译时--with-xxx开启 | http_ssl、http_v2、http_gzip_static、stream |
| 第三方开源模块 | 社区贡献,需编译进 Nginx | nginx_upstream_check_module(主动健康检查)、headers-more-nginx-module(改 Header)、lua-nginx-module(OpenResty 核心) |
| 动态模块 | Nginx 1.9.11+ 支持,无需重新编译主程序,load_module加载 | 官方及部分第三方模块提供.so |
用模块实现业务定制化的常见场景
Lua 脚本嵌入业务逻辑(最强大的方式)
通过ngx_http_lua_module(或直接用OpenResty——内置 LuaJIT + 大量库的 Nginx 发行版),在 Nginx 内用 Lua 写业务代码:- 请求到达 Java 前,先在 Nginx 做鉴权、限流、参数校验、灰度路由。
- 直接访问 Redis/MySQL,把热点数据在网关层缓存,Java 应用只处理核心业务。
location /api/order { access_by_lua_block { local token = ngx.req.get_headers()["X-Token"] if not token or not check_token_in_redis(token) then ngx.exit(ngx.HTTP_UNAUTHORIZED) end -- 按 userId 灰度:尾号 < 10 走新版本 local uid = ngx.req.get_uri_args()["uid"] if tonumber(uid) % 100 < 10 then ngx.var.upstream = "java_v2" else ngx.var.upstream = "java_v1" end } proxy_pass http://$upstream; }主动健康检查
内置只做被动检查。引入nginx_upstream_check_module后可周期性主动探测后端 Java 节点的/actuator/health,挂掉的节点自动摘除,恢复后自动加回。改写请求/响应头
headers-more-nginx-module可批量增删改 Header,例如给所有响应统一加安全头、给请求注入租户标识。自定义访问控制 / 风控
结合 Lua + Redis 实现滑动窗口限流、IP 黑名单、接口防刷等,在流量进入 Java 之前就拦掉。协议适配
通过stream模块代理 MySQL/Redis 等非 HTTP 的 TCP 服务;通过grpc模块代理 gRPC,给 Java 微服务做流量入口。
如何落地一个定制模块
- 优先用 OpenResty:它自带 Lua 能力和大量库,是做 Nginx 业务定制的首选方案,无需自己折腾编译。
- 编译第三方模块:用
--add-module=path静态编译,或--add-dynamic-module=path编成.so后load_module加载。 - 动态加载(1.9.11+):模块以
.so形式放好,在nginx.conf顶部load_module modules/ngx_http_xxx.so;即可,升级 Nginx 不必重编。
一句话总结:所谓"Nginx 插件"就是 Nginx 的模块机制;通过模块(尤其是 OpenResty 的 Lua 模块)可以在不侵入 Java 代码的前提下,把鉴权、限流、灰度、缓存等横切逻辑下沉到网关层,实现业务的定制化。
八、总结
| 问题 | 结论 |
|---|---|
| 4 层 vs 7 层 | 4 层看 IP+端口(LVS),7 层看 HTTP 报文(Nginx) |
| 选 Nginx 还是 LVS | 要应用层策略选 Nginx;要扛海量流量选 LVS;两者都要则 LVS 在前、Nginx 在后 |
| 与 Java 搭配 | LVS 做高可用入口 → Nginx 做 7 层网关 → Java 集群做业务;注意真实 IP 透传、健康检查、长连接、优雅停机 |
| Nginx 插件 | 指 Nginx 模块机制,配合 OpenResty/Lua 可在网关层实现鉴权、限流、灰度等定制化,不侵入 Java 业务代码 |