java 技术随笔

Nginx 高并发配置实战:worker 调优、keepalive、动静分离与限流防护

同样的服务,有人用 Nginx 反代压测 2 万并发就各种超时,有人能扛 10 万+。差别不在 Nginx 本身,而在配置文件有没有按高并发场景调优。本文从 Nginx 的进程模型讲起,给出一套面向高并发的完整配置:worker 进程与连接数、epoll 事件模型、keepalive 连接复用、gzip 与静态缓存、限流防护,最后配一张可直接改改用的参考配置。

先理解模型:为什么 Nginx 能扛高并发

Nginx 采用 master + worker 多进程 + 事件驱动(epoll)架构:master 负责加载配置、管理 worker;worker 进程各自独立监听端口,用 epoll 同时管理成千上万条连接,一个 worker 单线程就能支撑数万并发连接——因为它是异步非阻塞的,连接空闲时不占线程。这跟 Apache 的"一连接一线程"模型有本质区别,也是 Nginx 高并发的根基。

理解了这个模型,所有调优参数就都顺理成章了:调优的目标是让每个 worker 能处理更多连接、让连接更快被处理完、让每个连接占用更少的资源。

worker 进程与事件模型调优

worker 进程数一般等于 CPU 核数,避免进程切换开销;worker 连接数决定了单进程能承载的连接上限:

worker_processes auto;   # = CPU 核数,或手动写 8 之类
worker_rlimit_nofile 65535;  # 单进程可打开文件数上限,必须调大

events {
    use epoll;             # Linux 高并发下的事件模型
    worker_connections 65535;   # 单 worker 最大连接数
    multi_accept on;       # 一次 accept 多个新连接,减少唤醒
}   

最大并发连接数的估算:总连接数约 = worker_processes × worker_connections(再考虑反向代理场景每个客户端连接会占两倍:一条对客户端、一条对上游)。所以 8 核 × 65535 ≈ 52 万理论连接上限,实际受系统文件句柄、内存和带宽限制。

配合的内核参数(需要 root,写入 /etc/sysctl.conf):

# 端口监听队列与文件句柄
net.core.somaxconn = 65535
fs.file-max = 1000000
net.ipv4.tcp_max_syn_backlog = 65535

HTTP 层:连接复用与协议优化

高并发下最容易被忽视的是连接开销。HTTP/1.1 的 keepalive 让客户端复用 TCP 连接,避免每次请求都三次握手;反向代理到上游 Java 服务的连接也要复用,否则高并发时频繁新建 TCP 连接会拖垮上游:

http {
    keepalive_timeout 65;   # 客户端长连接超时
    keepalive_requests 1000;  # 单条长连接最多处理请求数

    # gzip 压缩,显著降低传输体积(HTML/CSS/JS 可省 60%+)
    gzip on;
    gzip_min_length 1k;
    gzip_comp_level 5;      # 1-9,5 是性价比点
    gzip_types text/plain text/css application/javascript application/json image/svg+xml;
    gzip_vary on;
}

静态资源用浏览器缓存 + open_file_cache 减少磁盘 IO:

location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
    expires 7d;              # 浏览器缓存 7 天
    access_log off;
    open_file_cache max=10000 inactive=20s;  # 缓存文件句柄
    open_file_cache_valid 60s;
    open_file_cache_min_uses 2;
}

反向代理 + 负载均衡:高并发架构的核心

Nginx 最常用的高并发姿势是扛在最前面,把请求分发到多个 Java 后端。upstream 定义后端集群,proxy_pass 转发:

upstream backend {
    # 默认轮询;ip_hash 可让同一客户端固定后端(有状态会话)
    least_conn;            # 转发给当前连接最少的后端,更均衡
    server 10.0.0.11:8080 weight=5 max_fails=2 fail_timeout=30s;
    server 10.0.0.12:8080 weight=3;
    server 10.0.0.13:8080 backup;   # 备用机,前两台挂了才启用

    keepalive 64;          # 每个 worker 对上游保持的空闲长连接数(关键!)
}

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;      # 上游连接复用必须用 1.1
        proxy_set_header Connection "";  # 清掉 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_connect_timeout 5s;    # 连不上后端快速失败,别都堆在超时上
        proxy_read_timeout 30s;
        proxy_send_timeout 30s;

        # 响应缓冲:吞吐优先可加大,实时性要求高可关小
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
    }
}

这里最容易踩的坑是忘了 proxy_http_version 1.1 和空 Connection 头,导致 upstream keepalive 不生效——高并发下后端会看到大量新建连接,连接数瞬间打满,表现为偶发 502/超时。

限流与防护:高并发下的"安全阀"

只调性能不够,还得防突刺。Nginx 内置 ngx_http_limit_req_module(漏桶限流)和 limit_conn(连接数限制),给接口装上安全阀:

http {
    # 定义限流区:按 IP,每秒 10 请求,突发 20
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
    # 定义连接数限制区
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

    server {
        location /api/ {
            limit_req zone=api_limit burst=20 nodelay;  # 突发 20 后拒绝
            limit_conn conn_limit 50;                   # 单 IP 最多 50 并发
            proxy_pass http://backend;
        }
    }
}

限流返回 503 而不是 502,语义更准确;配合 error_page 把限流响应做成友好提示页。

动静分离:把 Nginx 的性能用在刀刃上

静态资源(图片/JS/CSS)由 Nginx 直接返回,动态请求(/api/、/user/ 等)才转发 Java。这是最经典的高并发架构:Nginx 吃下 80% 的静态流量,后端只扛真正的业务请求。如果静态资源量很大,再叠一层 CDN,把压力挡在更前面。

server {
    listen 80;
    server_name www.example.com;
    root /data/www;         # 静态文件根目录

    location / { }          # 命中静态文件直接返回

    location /api/ {        # 动态请求转后端
        proxy_pass http://backend;
        proxy_set_header Host $host;
    }
}

高并发压测自检清单

配置完别急着上线,先自检这五项:1. worker_processes 是否 = 核数、worker_connections 是否调大;2. 系统 somaxconn / file-max 是否同步调大(Nginx 配了系统不配等于白配);3. 反代场景是否开了 upstream keepalive + HTTP/1.1;4. 静态资源是否有 expires 缓存;5. 是否有 limit_req/limit_conn 兜底。用 wrk 或 ab 压测看 QPS 与错误率,逐步逼近瓶颈。

一句话总结:Nginx 高并发不是某一个参数调出来的,而是"进程模型 + 连接复用 + 缓存分流 + 限流兜底 + 系统内核"整套配合的结果。

标签
Nginx高并发反向代理性能调优