定位
Spring Cloud Gateway 和 Nginx 都是常见的流量入口组件,但它们的定位和能力有本质区别。简单来说:Nginx 是"高性能反向代理服务器",Gateway 是"微服务网关",二者不是替代关系,而是互补协作。
核心定位对比
| 维度 | Nginx | Spring Cloud Gateway |
|---|---|---|
| 定位 | 高性能反向代理/Web服务器 | 微服务 API 网关 |
| 开发语言 | C 语言,独立部署 | Java,依赖 Spring Boot/Cloud |
| 层级 | 最外层(边缘网关/流量网关) | 第二层(业务/微服务网关) |
| 内存占用 | 几十 MB | 300MB 起步(JVM 开销) |
| 单机 QPS | 5~10 万+ | 1~3 万左右 |
一个形象的比喻:Nginx 是"小区大门",管所有进出;Gateway 是"楼栋门禁",管具体单元的精细权限。
能力差异详解
Nginx 擅长的领域
- 静态资源服务:直接提供 HTML/CSS/JS/图片等,支持缓存、gzip 压缩,性能极强
- SSL/TLS 终结:高效处理 HTTPS,支持 TLS 1.3,证书管理方便
- 基础安全防护:配合 fail2ban、WAF 模块,DDoS 防护成熟
- 通用负载均衡:支持多语言后端(Java、Node.js、Python 等),不绑定技术栈
- 极限性能:C 语言实现,事件驱动模型,单机轻松抗住万级 QPS
Gateway 擅长的领域
- 动态路由/服务发现:原生集成 Nacos/Eureka/Consul,自动感知服务上下线,无需重启
- 业务过滤器:用 Java 代码编写 GlobalFilter,开发调试便捷(Nginx 需写 Lua 脚本,学习成本高)
- 微服务治理:集成熔断(Sentinel)、灰度发布、请求聚合、指标埋点等
- 统一认证:JWT 校验、Token 解析等用 Java 实现更灵活
相同功能的不同实现方式
两者都能做负载均衡、限流、鉴权,但实现方式差异很大:
负载均衡
nginx
# Nginx:手动配置服务地址,服务变了需 reload
upstream user-cluster {
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=1;
server 192.168.1.12:8080 backup;
}
yaml
# Gateway:自动从注册中心发现服务,无需手动维护地址
spring:
cloud:
gateway:
routes:
- id: user-route
uri: lb://user-service # lb:// 自动负载均衡
predicates:
- Path=/user/**
鉴权
nginx
# Nginx:简单的请求头检查
location /api/ {
if ($http_authorization = "") {
return 401;
}
proxy_pass http://backend/;
}
java
// Gateway:可以写复杂的 JWT 解析、权限校验逻辑
@Component
public class AuthFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (token == null || !jwtUtil.verify(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
}
性能实测参考
根据压测数据(4核8G 云服务器环境):
| 并发数 | Gateway 平均响应时间 | Nginx 平均响应时间 | 差距 |
|---|---|---|---|
| 100 | 12.3ms | 8.7ms | Nginx 快 41% |
| 500 | 18.9ms | 15.2ms | Nginx 快 24% |
| 1000 | 25.4ms | 28.7ms | Gateway 反超 11% |
| 2000 | 42.1ms | 56.3ms | Gateway 快 25% |
低并发时 Nginx 凭借 C 语言优势领先;高并发时 Gateway 的响应式模型(WebFlux + Netty)反超。
实际架构中如何搭配
生产环境中最常见的做法是 Nginx + Gateway 组合使用:
用户请求
↓
Nginx(最外层)
→ SSL 卸载(HTTPS → HTTP)
→ 静态资源直接返回
→ 基础限流/DDoS 防护
→ 负载均衡到多个 Gateway 实例
↓
Spring Cloud Gateway(第二层)
→ 动态路由(配合 Nacos 服务发现)
→ 统一认证(JWT 校验)
→ 熔断限流(Sentinel)
→ 灰度发布、请求日志
↓
微服务集群(User Service / Order Service / ...)
不同场景的选型建议
| 场景 | 是否需要 Nginx | 推荐方案 |
|---|---|---|
| 中大型项目、QPS > 1万、有公网流量 | 强烈建议保留 | Nginx → Gateway |
| 中小型项目、内部系统、并发不高 | 可以不要 | 直接 Gateway |
| 有大量静态资源(图片、前端打包) | 必须有 | Nginx 负责静态 + Gateway 负责动态 |
| 需要极致防护(DDoS、WAF) | 必须有 | Nginx 做最外层防护 |
📌 总结 :Nginx 胜在性能极致、资源占用低、通用性强 ;Gateway 胜在与微服务生态深度集成、业务扩展灵活。实际项目中二者是互补关系------Nginx 扛入口流量,Gateway 做业务治理,兼顾高效与灵活。
配置
Nginx 和 Spring Cloud Gateway 之间路由规则对齐、避免重复配置以及常见踩坑
请求完整链路
先理清一个请求从浏览器到微服务的完整路径,后续所有配置都围绕这条链路展开:
浏览器 → Nginx(SSL卸载 + 静态资源 + 反向代理)
↓
Gateway(动态路由 + 鉴权 + 限流)
↓
微服务(业务逻辑)
路径前缀对齐:最容易踩坑的地方
这是实际项目中 出现频率最高 的问题。核心原则是:每一层各自剥离自己负责的前缀,不要重复也不要遗漏。
典型三层路径剥离方案
假设前端请求 /app/user/info,最终到达 user-service 的 /info 接口:
| 层级 | 进入路径 | 剥离内容 | 转出路径 |
|---|---|---|---|
| 前端 | /app/user/info |
--- | /app/user/info |
| Nginx | /app/user/info |
剥离 /app |
/user/info |
| Gateway | /user/info |
剥离 /user |
/info |
| 微服务 | /info |
--- | 处理请求 |
Nginx 配置
nginx
# 方案一:proxy_pass 末尾加斜杠,自动剥离 location 前缀
location /app/ {
proxy_pass http://gateway-upstream/; # 注意末尾的 /
}
# 方案二:rewrite 显式剥离
location /app/ {
rewrite ^/app/(.*)$ /$1 break;
proxy_pass http://gateway-upstream;
}
⚠️ 关键细节 :
proxy_pass末尾有没有/决定了是否自动剥离前缀。
| Nginx 配置 | 请求 /gateway/basic/login |
转发到 Gateway 的路径 |
|---|---|---|
location /gateway + proxy_pass http://backend |
/gateway/basic/login |
/gateway/basic/login(未剥离) |
location /gateway/ + proxy_pass http://backend/ |
/gateway/basic/login |
/basic/login(已剥离) |
Gateway 配置
yaml
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/** # 匹配 /user/ 开头的请求
filters:
- StripPrefix=1 # 剥离 /user,转发 /info
多版本/多环境场景
如果路径有两层前缀(如 /v2/order/add),Gateway 需要配置 StripPrefix=2:
yaml
- id: order-service-v2
uri: lb://order-service-v2
predicates:
- Path=/v2/order/**
filters:
- StripPrefix=2 # 剥离 /v2/order,转发 /add
Header 透传:丢失客户端信息
Nginx 作为反向代理时,默认会修改或丢弃部分请求头。如果配置不当,Gateway 和后端微服务拿不到真实的客户端信息。
必须配置的 Header
nginx
location /app/ {
proxy_pass http://gateway-upstream/;
# 透传客户端真实 IP
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 透传原始协议(HTTP/HTTPS)
proxy_set_header X-Forwarded-Proto $scheme;
# 透传原始 Host
proxy_set_header Host $host;
}
三个容易混淆的 Host 变量
| 变量 | 值来源 | 是否带端口 | 适用场景 |
|---|---|---|---|
$http_host |
客户端原始 Host 头 | 带端口 | 需严格保留客户端 Host |
$host |
客户端 Host(去端口)或 server_name |
不带端口 | 常规 Web 应用 |
$proxy_host |
proxy_pass 中的后端地址 |
带端口 | 后端需知道自己真实地址 |
💡 踩坑提醒:如果后端是 Tomcat,它对 Host 头非常敏感------Host 不匹配会直接返回 404。而 Gateway 只认路径不认 Host,所以"直连 Gateway 正常,经过 Nginx 就 404"的问题,大概率是 Host 头配置错误。
自定义 Header 丢失问题
如果项目中有带下划线的自定义 Header(如 api_key_id、request_id),Nginx 默认会静默丢弃 。需要在所有 Nginx 层的 http{} 块中统一开启:
nginx
http {
underscores_in_headers on;
# ...
}
WebSocket 支持:三层都要配
如果项目中有 WebSocket 需求(如实时消息推送),Nginx 和 Gateway 都必须正确配置协议升级,缺一不可。
Nginx 端配置
nginx
# 定义 Upgrade 映射(放在 server 块外面)
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
location /app/ {
proxy_pass http://gateway-upstream/;
proxy_http_version 1.1; # 必须用 HTTP/1.1
proxy_set_header Upgrade $http_upgrade; # 透传 Upgrade 头
proxy_set_header Connection $connection_upgrade; # 协议升级
}
Gateway 端配置
yaml
- id: ws-route
uri: lb:ws://im-service # 注意 lb:ws:// 协议
predicates:
- Path=/im/websocket/**
filters:
- StripPrefix=2
⚠️ 多层 Nginx 场景 :如果前面有多层 Nginx(如 SLB → Nginx → Gateway),每一层都必须配置 WebSocket 的 Upgrade 透传,否则握手会失败。
超时配置:长连接被意外断开
Nginx 和 Gateway 的默认超时时间不同,容易出现"Nginx 还没断,Gateway 已经超时"或反过来导致连接中断。
统一超时配置
nginx
# Nginx 端
location /app/ {
proxy_pass http://gateway-upstream/;
proxy_connect_timeout 10s; # 连接超时
proxy_send_timeout 60s; # 发送超时
proxy_read_timeout 60s; # 读取超时
}
yaml
# Gateway 端(application.yml)
spring:
cloud:
gateway:
httpclient:
connect-timeout: 10000 # 连接超时(毫秒)
response-timeout: 60s # 响应超时
💡 原则 :Nginx 的超时时间应 略大于 Gateway 的超时时间,让 Gateway 先返回错误信息,而不是 Nginx 直接断开连接返回 502。
限流分工:避免重复或冲突
Nginx 和 Gateway 都能做限流,但应明确分工,避免重复限流导致实际限流阈值远低于预期。
| 层级 | 限流策略 | 工具 |
|---|---|---|
| Nginx | 粗粒度,按 IP 全局限流 | limit_req_zone |
| Gateway | 细粒度,按服务/接口/用户限流 | Sentinel / Redis RateLimiter |
nginx
# Nginx:全局限流,每秒 100 请求/IP
limit_req_zone $binary_remote_addr zone=global_rate:10m rate=100r/s;
server {
location /app/ {
limit_req zone=global_rate burst=200 nodelay;
proxy_pass http://gateway-upstream/;
}
}
yaml
# Gateway:针对订单服务精细限流
- id: order-service
uri: lb://order-service
predicates:
- Path=/order/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 50 # 每秒令牌数
redis-rate-limiter.burstCapacity: 100 # 桶容量
key-resolver: "#{@userKeyResolver}"
排查 404 问题的标准流程
遇到"经过 Nginx 就 404,直连 Gateway 正常"时,按以下步骤排查:
- 直连 Gateway 测试 :
curl http://gateway:8080/user/info,确认 Gateway 本身路由没问题 - 检查 Nginx 转发路径 :在 Nginx 的
access_log中确认实际转发给 Gateway 的路径是什么 - 检查
proxy_pass末尾斜杠 :是否意外保留了/app前缀 - 检查 Host 头:Gateway 的日志中看到的 Host 是否正确
- 检查 StripPrefix 数值:Nginx 剥离了一层,Gateway 是否又多剥离或少剥离了
完整生产配置参考
nginx
# ===== Nginx 完整配置 =====
upstream gateway-upstream {
server 192.168.1.100:8080 weight=5;
server 192.168.1.101:8080 weight=5;
}
# WebSocket 升级映射
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
server_name api.example.com;
# SSL 配置
ssl_certificate /etc/nginx/certs/api.example.com.crt;
ssl_certificate_key /etc/nginx/certs/api.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
# 全局 Header 支持
underscores_in_headers on;
# 静态资源直接返回,不走 Gateway
location /static/ {
root /var/www/frontend;
expires 7d;
}
# API 请求转发到 Gateway
location /app/ {
proxy_pass http://gateway-upstream/;
# 基础 Header 透传
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;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# 超时配置
proxy_connect_timeout 10s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
# 基础限流
limit_req zone=global_rate burst=200 nodelay;
}
}
yaml
# ===== Gateway 完整配置 =====
spring:
cloud:
gateway:
httpclient:
connect-timeout: 10000
response-timeout: 60s
globalcors:
add-to-simple-url-handler-mapping: true
corsConfigurations:
'[/**]':
allowedOrigins: "https://example.com"
allowedHeaders: "*"
allowedMethods:
- GET
- POST
- PUT
- DELETE
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**
filters:
- StripPrefix=1
- id: order-service
uri: lb://order-service
predicates:
- Path=/order/**
filters:
- StripPrefix=1
📌 总结 :Nginx 和 Gateway 路由对齐的核心就三件事------路径前缀逐层剥离不重不漏 、Header 透传完整不丢 、超时/限流分工明确不冲突。掌握了这三点,基本能覆盖 90% 以上的踩坑场景。