Nginx 连接复用与四层负载均衡

对比四层(stream)与七层(http)转发,从操作系统并发与 I/O 机制出发系统性说明:Nginx 的网络设计、连接复用原理与 Linux 内核调优,并附可执行配置与观测建议。

1. 从操作系统角度看网络并发模型

高并发网络服务的核心是“如何在有限 CPU/内存下同时处理大量连接”。关键是并发模型与 I/O 机制:

  • 进程/线程模型:每连接一个进程/线程,易编程,但上下文切换与栈内存开销在 1k~10k 连接下迅速放大。
  • 事件驱动模型(Reactor/Proactor):少量线程管理海量非阻塞 fd,依赖内核事件通知。

内核 I/O 通知机制:

  • select/poll:线性扫描,fd 数/开销受限,不适合高并发。
  • epoll(Linux):O(1) 监听大量 fd,边缘/水平触发,EPOLLEXCLUSIVE 降低惊群。
  • kqueue(BSD/macOS):高效通用事件队列。
  • IO_uring(Linux 新):提交/完成队列,绕过部分系统调用开销;Nginx 主线仍以 epoll 为主。

Reactor vs Proactor:Nginx 采用 Reactor(事件到来再发起 read/write),Windows 下的 IOCP 更接近 Proactor。

常见瓶颈点:

  • fd 限制(ulimit -nworker_rlimit_nofilefs.file-max)。
  • 监听/握手队列(somaxconntcp_max_syn_backlog、SYN flood)。
  • 收发队列与网卡(netdev_max_backlog、RPS/RFS/XPS、队列/中断亲和)。
  • 拥塞控制与队列管理(BBR、fq、TFO)。

2. Nginx 的网络设计模型

Nginx 采用 master + 多 worker 的事件驱动架构:

  • master:管理配置、热重载、worker 生命周期。
  • worker:每个 worker 一个事件循环,使用 epoll/kqueue 管理大量非阻塞连接;worker_processes auto; 通常与 CPU 核数一致。
  • 连接与请求分离:一个连接可承载多个请求(HTTP/1.1 keepalive、HTTP/2 多路复用)。
  • accept 协调:accept_mutex 避免惊群;或 reuseport 让每个 worker 拥有独立监听套接字。
  • 零拷贝与发送优化:sendfiletcp_nopushtcp_nodelayaio threads

事件与监听示例:

worker_processes  auto;
worker_rlimit_nofile  1048576;

events {
  use epoll;               # Linux 下优先 epoll;macOS/FreeBSD 为 kqueue
  worker_connections 65535;
  multi_accept on;
  # accept_mutex on;      # 与 reuseport 二选一
}

server {
  listen 80 reuseport backlog=65535;
  # listen 443 ssl http2 reuseport fastopen=512;
}

3. 连接复用:客户端、Nginx 与上游

目标:减少握手与 TLS 开销、降低 TIME_WAIT 与端口消耗、提升吞吐。

  • 客户端->Nginx:
    • HTTP/1.1 keepalive 串行复用
    • HTTP/2 多路复用并发流
    • HTTP/3(QUIC)在 UDP 上实现多路复用与 0-RTT
  • Nginx->上游:
    • upstream keepalive 将代理到上游的连接复用,显著降低后端握手压力
    • 连接池为“每 worker 独立”,容量需乘以 worker 数

HTTP/1.1 与上游 keepalive:

http {
  upstream api_backend {
    server 10.0.0.2:8080 max_fails=2 fail_timeout=10s;
    keepalive 256;  # 每 worker 空闲长连接上限
  }

  server {
    listen 80 reuseport backlog=65535;

    keepalive_requests 10000;
    keepalive_timeout  75s;

    location / {
      proxy_http_version 1.1;
      proxy_set_header Connection "";
      proxy_set_header Host $host;
      proxy_connect_timeout 5s;
      proxy_send_timeout    30s;
      proxy_read_timeout    30s;
      proxy_pass http://api_backend;
    }
  }
}

四层(stream)与上游:

stream {
  upstream mysql_backend {
    server 10.0.0.1:3306 max_fails=2 fail_timeout=10s;
  }

  server {
    listen 3306 reuseport backlog=32768;
    proxy_connect_timeout 3s;
    proxy_timeout        30s;
    proxy_pass mysql_backend;
  }
}

取舍提示:

  • 上游存在会话亲和或连接态时,需配合一致性哈希/粘性策略,避免跨请求状态泄漏。
  • 短连接/突发场景不宜给过大 keepalive 池,避免端口/内存占用。
  • 数据库协议建议交由专业代理(ProxySQL、pgbouncer)做更精细的连接池与语句复用。
    stream {
    upstream mysql_backend { server 10.0.0.1:3306 max_fails=2 fail_timeout=10s; }
    server { listen 3306; proxy_pass mysql_backend; }
    }
    

4. Linux 内核网络优化

推荐基线(按环境渐进验证):

# /etc/sysctl.d/99-nginx-tuning.conf
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 250000

net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syncookies = 1

net.ipv4.ip_local_port_range = 10000 65000
fs.file-max = 2097152

net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5

net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728

net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

net.ipv4.tcp_fastopen = 3

Nginx 侧连接与 fd:

worker_rlimit_nofile 1048576;
events { worker_connections 65535; }

注意事项:

  • tcp_tw_recycle 已移除,勿用;tcp_tw_reuse 收益有限,勿过度依赖。
  • 过度缩短 tcp_fin_timeout 可致异常断流;以观测为准。
  • reuseportaccept_mutex 二选一;多核下普遍优先 reuseport
  • 网卡队列/中断亲和等需要硬件与驱动配合,谨慎变更。

5. 观测与压测

  • Nginx:stub_status$upstream_response_time 分位数、active/reading/writing 分布。
  • 系统:ss -sss -tisar -n TCP,DEVethtool -S/proc/net/netstat
  • 压测:wrk(HTTP/1.x)、h2load(HTTP/2/3)、iperf3(链路)。

示例:

wrk -t8 -c1024 -d60s --latency http://127.0.0.1/
h2load -n 100000 -c 200 -m 100 https://127.0.0.1:443/

6. 常用配置快照

http {
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;

  keepalive_timeout 75s;
  keepalive_requests 10000;

  upstream api_backend { server 10.0.0.2:8080; keepalive 256; }

  server {
    listen 80 reuseport backlog=65535;
    location / {
      proxy_http_version 1.1;
      proxy_set_header Connection "";
      proxy_set_header Host $host;
      proxy_pass http://api_backend;
    }
  }
}

7. 排错 Checklist

  • 5xx 或 upstream timed out:核查后端与上游连接池、proxy_*_timeout、网络丢包。
  • 连接拒绝或超时:检查 somaxconn/backlogworker_connectionsss -lnt 中队列与监听状态。
  • TIME_WAIT/端口耗尽:扩大 ip_local_port_range 与上游 keepalive,排查异常关闭。
  • 吞吐增长受限:核查 sendfiletcp_nopush、NIC 队列、CPU 亲和与磁盘 I/O。