Nginx 与后端长连接调优:keepalive 与 upstream 复用

    发布时间:2026-09-20 04:30 更新时间:2026-09-20 04:30 阅读量:0

    很多站长在 Nginx 反向代理后面挂 Node、Java、Go 或 PHP-FPM 服务时,都会遇到一个现象:明明在 upstream 里写了 keepalive,用 ss 或后端日志看,Nginx 还是在不停地新建 TCP 连接。连接数一高,握手和 TIME_WAIT 就吃掉不少资源。这篇就把这件事讲透:keepalive 到底是给谁用的、proxy_http_version 为什么必须一起改、以及怎么验证连接有没有真正复用。

    先分清两个方向的 keepalive

    Nginx 里谈长连接,其实有两个完全独立的方向,混在一起看很容易蒙。第一段是客户端到 Nginx 的连接,由 keepalive_timeout、keepalive_requests 控制,决定浏览器和 Nginx 之间要不要复用同一条 TCP。第二段是 Nginx 到后端 的连接,也就是上游连接池,由 upstream 块里的 keepalive 指令控制,决定 Nginx 作为客户端去连后端时,能不能复用已有连接。

    问题通常出在第二段。你在 upstream 里加了 keepalive,只表示 Nginx 愿意把空闲的后端连接留在池子里;但这条连接上的 HTTP 报文是不是允许保持,还取决于协议版本和请求头。默认情况下 Nginx 用 HTTP/1.0 跟后端说话,而 HTTP/1.0 默认是短连接,后端收到请求处理完就关。所以只写 keepalive 不写版本,等于白写。

    三段配置缺一不可

    要让上游连接真正复用,需要同时满足三件事:启用 HTTP/1.1、显式声明 Connection 头、设置连接池大小。少了任何一段,连接都会在响应结束后被关掉。下面是一份可以直接套用的 upstream 配置:

    upstream backend_app {
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
        keepalive 32;          # 每个 worker 进程为上游保留的空闲连接数
        keepalive_requests 1000;  # 单条连接最多处理多少请求后关闭
        keepalive_timeout 60s;    # 空闲连接保留多久
    }
    
    server {
        listen 80;
        location /api/ {
            proxy_pass http://backend_app;
            proxy_http_version 1.1;              # 关键:改用 HTTP/1.1
            proxy_set_header Connection "";      # 关键:清空默认的 close
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

    这里的 proxy_set_header Connection "" 是最容易被忽略的一行。Nginx 默认会给上游带一个 Connection: close,只要这个头存在,后端就会在响应后关连接,前面所有努力都作废。把它置空,等于告诉后端“别急着关”。

    另外,keepalive 的数值不是越大越好。它表示每个 worker 进程缓存的空闲连接上限,实际占用是 连接数 × worker 进程数。如果后端本身连接数敏感,这个值调太大反而会占住后端资源。一般从 16 到 64 之间起步,按后端承载能力调整,具体以官方文档和实际压测结果为准。

    验证连接到底有没有复用

    配置改完 reload 之后,先确认语法没问题:

    nginx -t
    nginx -s reload

    然后观察和后端之间的连接状态。假设后端在 8080 端口,可以这样看:

    ss -tnp state established '( dport = :8080 or sport = :8080 )' | grep nginx

    反复用 curl 打几次接口,再执行上面的命令。如果连接数基本稳定、不随请求次数线性增长,说明复用生效了;如果每请求一次就多一条 ESTABLISHED,或者出现大量 TIME_WAIT,那多半还是短连接。也可以在后端服务侧打印连接建立日志,对比请求数和建连次数,这个办法最直观。

    还有两个常见坑值得留意。一是负载均衡算法:默认轮询会让请求分散到不同后端,每条后端连接的使用频率下降,复用效果看起来不明显,这属于正常现象,不代表配置错了。二是 upstream 里写了多个 server 但只有一个健康,Nginx 仍然会对不健康的节点反复尝试,日志里会看到连接失败,需要配合 max_fails 和 fail_timeout 处理,别误判成长连接没生效。

    小结与下一步

    回到最初的问题:开了 keepalive 仍频繁建连,绝大多数情况不是 keepalive 没用,而是缺了 proxy_http_version 1.1 和 proxy_set_header Connection "" 这两步。把三段配齐,再用 ss 对比请求前后的连接数,就能判断复用是否落地。建议下一步结合后端自身的连接上限做一次小流量压测,观察 TIME_WAIT 数量和后端句柄占用,再微调 keepalive 数值,而不是一次调很大。

    继续阅读

    📑 📅
    Docker 容器内 CPU 被限流排查:cfs_quota、cpuset 与 top 显示异常 2026-09-19
    Nginx gzip 与 Brotli 压缩配置实战:静态资源体积优化 2026-09-19
    PHP-FPM 进程数怎么调:pm.max_children 与内存估算 2026-09-19
    Docker容器DNS解析异常排查:resolv.conf与自定义网络 2026-09-19
    MySQL连接被拒绝Connection refused逐层排查思路 2026-09-19
    Docker 容器日志写满磁盘:json-file 限制与 max-size 配置 2026-09-20
    Linux时间与时区排查:date、timedatectl与容器时区一致性 2026-09-20
    服务器网卡多队列与中断绑定入门:RPS、RSS 与 irqbalance 怎么取舍 2026-09-20
    Nginx 上传大文件报 413 与超时:参数与缓冲区排查 2026-09-20
    Docker容器缺命令:精简镜像补装与临时容器调试 2026-09-20