找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4933

积分

0

好友

683

主题
发表于 1 小时前 | 查看: 2| 回复: 0

1️⃣ 适用场景与前置条件

优化前,请先确认你的环境是否符合以下要求,这是配置生效的基础。

项目 要求
适用场景 日均 PV 100万+ 的高并发 Web 应用,需要支持百万级连接
OS 版本 RHEL/CentOS 8.0+ 或 Ubuntu 20.04 LTS+
内核版本 Linux Kernel 4.15+ (推荐 5.10+,支持 BBR)
Nginx 版本 1.20.x+ 或 1.24.x+(LTS 版本)
资源规格 8C16G(最小)/ 16C32G(推荐)/ 32C64G(超大规模)
网络 10Gbps+ 网络、万级连接数支持网卡
存储 SSD(日志 I/O 不能是瓶颈)
权限 root 权限或能修改 /etc/nginx/ 目录
技能水平 高级运维工程师,理解 TCP/IP、HTTP 协议、性能优化

2️⃣ 反模式警告:哪些场景不适用?

⚠️ 以下场景不推荐使用此配置方案:

  1. 低并发、低流量应用

    • 症状:日均 PV < 10万,应用响应时间 < 100ms
    • 原因:百万级配置会浪费资源,反而增加复杂度
    • 改进:使用 Nginx 官方默认配置即可
  2. 无监控、无告警系统

    • 症状:调参后无法量化效果,性能衰减无法及时发现
    • 改进:部署 Prometheus + Grafana 等监控后再调参
  3. 在生产高峰期直接修改参数

    • 症状:调参失败导致业务中断、连接数暴涨
    • 改进:参数变更在低峰期进行,且需 30 分钟观察期
  4. 盲目增大所有参数(如内存)

    • 症状:worker_processes 设置 > CPU 核数,导致上下文切换频繁
    • 改进:每个参数都有最优值,非“越大越好”
  5. 配置与 OpenResty/tengine 等分支混用

    • 症状:配置语法差异导致启动失败
    • 改进:本方案仅适用于原生 Nginx 1.20+

替代方案对比:

场景 推荐方案 理由
已有 CDN/LB 仅优化源站 Nginx 减少网络往返,性价比高
API 网关场景 Kong/Traefik + Nginx Kong 提供更强的插件系统
实时性要求高 HAProxy + Nginx HAProxy 更快(C 语言实现)
需要 Lua 动态脚本 OpenResty 基于 Nginx + Lua 的强大组合

3️⃣ 快速清单(20分钟上手)

  • [ ] 环境准备(5分钟)
    • [ ] 确认 Nginx 版本 >= 1.20:nginx -v
    • [ ] 确认内核支持百万连接:ulimit -n(需 >= 100万)
    • [ ] 备份原始配置:cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
  • [ ] 应用优化配置(10分钟)
    • [ ] 复制本方案的 nginx.conf 到 /etc/nginx/
    • [ ] 修改 worker_processes、worker_connections 等关键参数
    • [ ] 验证配置语法:nginx -t
  • [ ] 系统参数调优(10分钟)
    • [ ] 编辑 /etc/sysctl.conf,应用网络优化参数
    • [ ] 执行 sysctl -p 使生效
    • [ ] 编辑 /etc/security/limits.conf,提升文件描述符上限
  • [ ] 启动 & 验证(5分钟)
    • [ ] 启动 Nginx:systemctl restart nginx
    • [ ] 检查进程:ps aux | grep nginx
    • [ ] 验证监听:netstat -tln | grep 80
  • [ ] 性能测试(30分钟)
    • [ ] 用 Apache Bench 或 wrk 压测
    • [ ] 观察 CPU、内存、网络占用
    • [ ] 若能达到 10万+ RPS,则配置成功

4️⃣ 实施步骤详解:从系统到应用的全面优化

Step 1:系统级优化 - 突破百万连接上限

目标:让 Linux 内核支持百万级并发连接。

第 1 步:检查当前限制

# 查看全局文件描述符限制
cat /proc/sys/fs/file-max
# 预期输出:需要 >= 1000万(支持 100万 连接 x 10 倍安全系数)

# 查看进程级限制
ulimit -n
# 预期输出:通常 1024(太小!)

# 查看网络连接上限
cat /proc/sys/net/ipv4/ip_local_port_range
# 预期输出:32768 - 60999(共 ~28k 端口,这是基础)

# 查看 TCP TIME_WAIT 状态数
netstat -an | grep TIME_WAIT | wc -l

第 2 步:编辑系统配置文件

# 编辑 /etc/sysctl.conf
cat >> /etc/sysctl.conf << 'EOF'
### ===== 文件描述符相关 =====
fs.file-max = 10000000 # 全局最大 FD 数

### ===== TCP 连接相关 =====
# 监听队列
net.core.somaxconn = 65535 # listen 队列最大长度
net.ipv4.tcp_max_syn_backlog = 65535 # SYN 队列最大长度

# 连接复用(加快新连接建立)
net.ipv4.tcp_tw_reuse = 1 # 复用 TIME_WAIT 连接
net.ipv4.tcp_timestamps = 1 # 启用 TCP 时间戳(必须)

# 连接超时
net.ipv4.tcp_fin_timeout = 10 # FIN_WAIT2 超时(默认 60s,改为 10s)
net.ipv4.tcp_keepalive_time = 600 # keepalive 探测时间间隔(10 分钟)
net.ipv4.tcp_keepalive_probes = 3 # keepalive 探测次数
net.ipv4.tcp_keepalive_intvl = 15 # keepalive 探测间隔

# 缓冲区优化
net.core.rmem_default = 134217728 # 默认接收缓冲 128MB
net.core.rmem_max = 134217728 # 最大接收缓冲 128MB
net.core.wmem_default = 134217728 # 默认发送缓冲 128MB
net.core.wmem_max = 134217728 # 最大发送缓冲 128MB
net.ipv4.tcp_rmem = 4096 87380 134217728 # TCP 接收缓冲自动调整
net.ipv4.tcp_wmem = 4096 65536 134217728 # TCP 发送缓冲自动调整

# 网络设备队列
net.core.netdev_max_backlog = 100000 # 网卡接收队列最大长度

# 连接优化
net.ipv4.ip_local_port_range = 1024 65535 # 本地端口范围(默认 32768-60999)
net.ipv4.tcp_tw_recycle = 0 # 关闭 TIME_WAIT 快速回收(可能导致丢包)

# 性能优化
net.core.busy_read = 50 # 忙轮询(仅在超高性能场景)
net.core.busy_poll = 50
EOF

# 生效配置
sysctl -p

验证配置生效:

# 验证全局 FD 限制
cat /proc/sys/fs/file-max
# 预期输出:10000000

# 验证 listen 队列
cat /proc/sys/net/core/somaxconn
# 预期输出:65535

# 验证 TCP 缓冲
cat /proc/sys/net/core/rmem_max
# 预期输出:134217728

第 3 步:编辑进程级限制

# 编辑 /etc/security/limits.conf
cat >> /etc/security/limits.conf << 'EOF'
# Nginx 进程文件描述符限制
nginx soft nofile 1000000
nginx hard nofile 1000000
nginx soft nproc 1000000
nginx hard nproc 1000000

# 若以 root 启动(不推荐)
* soft nofile 1000000
* hard nofile 1000000
* soft nproc 1000000
* hard nproc 1000000
EOF

# 使生效(需要重新登录或重启 Nginx)

验证生效:

# 重启 Nginx 后检查
ps aux | grep nginx | head -1 | awk '{print $1}' > /tmp/nginx_user
cat /proc/$(pgrep -f 'nginx: worker' | head -1)/limits | grep "Max open files"
# 预期输出:Max open files 应该 >= 1000000

常见错误与解决:

  • 错误 1:sysctl -p 提示某些参数不存在
    # 症状
    sysctl: cannot stat /proc/sys/net/ipv4/tcp_tw_recycle: No such file or directory
    # 原因:内核版本太旧或参数被移除
    # 解决:删除该参数,继续应用其他参数
    sed -i '/tcp_tw_recycle/d' /etc/sysctl.conf
    sysctl -p
  • 错误 2:Nginx 进程级 FD 限制仍然是 1024
    # 症状:ps 中看到 Nginx FD 限制仍为 1024
    # 原因:limits.conf 修改后需要重启 Nginx
    systemctl restart nginx
    # 再验证
    ps aux | grep nginx | head -1 | awk '{print $1}' | xargs cat /proc/$(pgrep -f 'nginx: worker' | head -1)/limits

回滚要点:
若调优后网络异常,恢复 /etc/sysctl.conf.bak

cp /etc/sysctl.conf.bak /etc/sysctl.conf
sysctl -p

Step 2:Nginx 核心配置优化

目标:配置 Nginx 使其能高效处理百万级连接。以下是经过验证的核心配置模板。

配置模板 (/etc/nginx/nginx.conf):

# Nginx 百万级配置模板 v1.0
# 适用于:日均 PV 1000万+、并发连接 100万+ 的生产环境

#============ 全局块 ============
# 运行用户(不推荐用 root)
user nginx nginx;

# worker 进程数(通常 = CPU 核数)
# 查看:nproc 或 cat /proc/cpuinfo | grep processor | wc -l
# 设置规则:
#   - CPU <= 8 核:worker_processes = CPU 核数
#   - CPU > 8 核:worker_processes = CPU 核数 - 2(留资源给系统)
#   - 或使用 auto(Nginx 自动检测)
worker_processes auto;

# worker 优先级(越小越优先,范围 -20 到 19)
worker_priority -10;  # 提升 Nginx 优先级,让系统进程让步

# 错误日志
error_log /var/log/nginx/error.log warn;

# PID 文件
pid /var/run/nginx.pid;

# worker rlimit
worker_rlimit_nofile 1000000;  # 每个 worker 最大 FD 数
worker_rlimit_core 0;          # 关闭 core dump(节省磁盘)

#============ 事件块 ============
events {
    # 每个 worker 最大并发连接数
    # 规则:(系统最大 FD 数) / worker_processes
    # 例如:1000000 / 16 = 62500,可安全设置 65536
    worker_connections 65536;

    # 事件模型(Linux 必须用 epoll)
    use epoll;

    # 是否一次调用 accept() 接受多个连接
    multi_accept on;

    # 优化:当 accept 队列有连接时立即处理
    accept_mutex off;  # 关闭互斥锁,提升并发处理能力
}

#============ HTTP 块 ============
http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # ===== 日志配置 =====
    # 日志格式(包含关键性能指标)
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for" '
                    'rt=$request_time uct="$upstream_connect_time" '
                    'uht="$upstream_header_time" urt="$upstream_response_time"';

    # 访问日志(高并发下建议关闭或用 buffer)
    # 选项 1:完全关闭(最快,但无日志)
    # access_log off;
    # 选项 2:使用 buffer(推荐,平衡性能和可观测性)
    access_log /var/log/nginx/access.log main buffer=32k flush=5s;

    # ===== 性能优化 =====
    # 文件发送优化
    sendfile on;                   # 启用 sendfile(零拷贝)
    tcp_nopush on;                 # 禁用 Nagle 算法,加速小包发送
    tcp_nodelay on;                # 立即发送数据(HTTP 长连接需要)

    # 超时配置
    keepalive_timeout 65 65;       # HTTP Keep-Alive 超时(推荐 60-120 秒)
    client_body_timeout 10;        # 客户端请求体读取超时
    client_header_timeout 10;      # 客户端请求头读取超时
    send_timeout 10;               # 发送响应超时

    # 请求大小限制
    client_max_body_size 20m;      # 最大请求体大小(根据业务调整)

    # 缓冲区优化
    client_body_buffer_size 128k;  # 请求体缓冲
    client_header_buffer_size 1k;  # 请求头缓冲
    large_client_header_buffers 4 8k;  # 大请求头缓冲

    # Gzip 压缩(可选,CPU 密集但节省带宽)
    gzip on;
    gzip_vary on;
    gzip_min_length 1k;            # 小于 1KB 不压缩
    gzip_comp_level 3;             # 压缩级别 1-9(3 是性能和压缩的平衡)
    gzip_types text/plain text/css text/xml text/javascript
                    application/x-javascript application/xml+rss;
    gzip_disable "msie6";          # 禁用 IE6 gzip

    # HTTP 版本
    http2_max_field_size 16k;      # HTTP/2 字段大小限制
    http2_max_header_size 32k;

    # 哈希表优化(减少哈希冲突)
    types_hash_max_size 2048;
    variables_hash_max_size 1024;
    variables_hash_bucket_size 128;
    server_names_hash_max_size 1024;
    server_names_hash_bucket_size 128;

    # 连接池优化(后端连接复用)
    upstream_keepalive_connections 64;  # 保持最多 64 个连接到后端
    upstream_keepalive_timeout 60;      # 后端连接空闲超时
    upstream_keepalive_requests 100;    # 单条连接最多处理 100 个请求

    # ===== 代理优化 =====
    # (若 Nginx 作为反向代理时)
    proxy_buffering on;
    proxy_buffer_size 4k;
    proxy_buffers 8 4k;
    proxy_busy_buffers_size 8k;
    proxy_redirect off;
    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;
    proxy_connect_timeout 5s;       # 连接超时
    proxy_send_timeout 10s;         # 发送超时
    proxy_read_timeout 10s;         # 读取超时

    # ===== 限流 & 安全 =====
    # 限制并发连接数(防止某个 IP 占用过多连接)
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
    limit_conn conn_limit 10;       # 每个 IP 最多 10 个并发连接

    # 限制请求速率(防止暴力请求)
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
    limit_req zone=req_limit burst=20 nodelay;

    #============ Server 块(虚拟主机)============
    # 健康检查端点(内部使用,不记录日志)
    server {
        listen 8080;
        server_name _;

        location /health {
            access_log off;
            return 200 "ok\n";
            add_header Content-Type text/plain;
        }

        location /metrics {
            access_log off;
            return 200 "Nginx metrics endpoint\n";
            add_header Content-Type text/plain;
        }
    }

    # 主服务(Web 应用)
    server {
        listen 80 deferred reuseport backlog=65535;
        # listen [::]:80 deferred reuseport backlog=65535;  # IPv6(可选)
        server_name _;

        # SSL(可选,若需 HTTPS)
        # listen 443 ssl http2 deferred reuseport backlog=65535;
        # ssl_certificate /etc/nginx/ssl/cert.pem;
        # ssl_certificate_key /etc/nginx/ssl/key.pem;
        # ssl_protocols TLSv1.2 TLSv1.3;
        # ssl_ciphers HIGH:!aNULL:!MD5;
        # ssl_session_cache shared:SSL:10m;
        # ssl_session_timeout 10m;
        # ssl_session_tickets off;
        # ssl_stapling on;
        # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

        # 根路径
        root /var/www/html;
        index index.html index.htm;

        # 静态文件缓存
        location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf)$ {
            expires 30d;
            add_header Cache-Control "public, immutable";
        }

        # 动态请求代理到后端
        location / {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_connection_manager true;
            proxy_buffering on;
        }

        # 错误页面
        error_page 404 /404.html;
        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
            root /usr/share/nginx/html;
        }
    }

    # 后端应用服务器定义
    upstream backend {
        # 保持连接复用
        keepalive 64;

        # 后端服务器列表
        server 192.168.1.10:8080 weight=1 max_fails=2 fail_timeout=10s;
        server 192.168.1.11:8080 weight=1 max_fails=2 fail_timeout=10s;
        server 192.168.1.12:8080 weight=1 max_fails=2 fail_timeout=10s;

        # 负载均衡算法(默认轮询)
        # least_conn;     # 最少连接
        # ip_hash;        # 基于 IP 哈希(会话保持)
        # hash $uri;      # 基于 URI 哈希
    }
}

关键参数详解:

  1. worker_processes
    • 最优值:CPU 核数(多了反而降速,因为上下文切换)
    • 查看命令nproccat /proc/cpuinfo | grep processor | wc -l
  2. worker_connections
    • 最优值(系统最大 FD 数 - 预留) / worker_processes
    • 例如:(100万 - 10万) / 16 = 5.6万,可设 65536
  3. listen backlog
    • 含义:等待 accept() 的连接队列深度
    • 最优值:与 net.core.somaxconn 一致(65535)
  4. keepalive_timeout
    • 含义:HTTP Keep-Alive 连接保活时间
    • 推荐:60-120 秒(太小频繁断连,太大积压僵尸连接)
  5. proxy_buffering
    • on:Nginx 缓冲后端响应(适合后端慢)
    • off:流式发送,延迟低(适合后端快)

验证配置语法:

nginx -t
# 预期输出:语法 OK

常见错误与解决:

  • 错误 1:启动失败,提示“Address already in use”
    # 查看占用进程
    lsof -i :80
    # 解决方案 1:杀掉旧进程
    kill -9 <pid>
    # 解决方案 2:更改监听端口(测试时)
    sed -i 's/listen 80/listen 8080/g' /etc/nginx/nginx.conf
  • 错误 2:配置不生效
    # 原因:修改后未重载
    systemctl reload nginx  # 或 nginx -s reload

Step 3:性能测试与验证

目标:验证配置是否达到百万级处理能力。

安装测试工具:

  • RHEL/CentOS: yum install -y httpd-toolsyum install -y wrk
  • Ubuntu/Debian: apt install -y apache2-utilsapt install -y wrk

基准测试 1:Apache Bench 压测

# 发起 100 万个请求,并发 1000
ab -n 1000000 -c 1000 http://localhost/

# 关键指标解释:
# - Requests per second: 吞吐量(RPS),> 100K 说明配置成功
# - Time per request:平均延迟,< 100ms 为优秀
# - Failed requests:失败次数,应该为 0

基准测试 2:复杂场景的 wrk 压测

# 用 wrk 模拟更复杂的场景:10 个线程,1000 并发连接,持续 30 秒
wrk -t10 -c1000 -d30s http://localhost/

# 关键指标:
# - Latency:响应延迟(Avg < 50ms,p99 < 200ms 为好)
# - Req/Sec:每秒吞吐量

5️⃣ 核心原理:为什么 Nginx 能处理百万连接?

Nginx 的强大源于其事件驱动、异步非阻塞的架构。它与传统的每个连接一个线程(或进程)的模型截然不同。

关键机制:

  1. 异步非阻塞 I/O (epoll): 每个 worker 进程使用一个线程,通过 epoll 监控所有连接。只有连接上有数据可读/写时,worker 才去处理,避免了为大量空闲连接创建线程的开销。
  2. 内存开销小: 每个空闲连接主要占用 socket buffer 和少量元数据(约10-30KB)。100万连接的理论内存占用约为20-30GB,在现代服务器上是可接受的。
  3. CPU 开销集中在活跃连接: 大多数连接处于空闲状态,不消耗CPU。CPU只处理实际有数据交互的活跃连接(通常只占总连接的1-10%)。

关键参数的性能影响:

参数 性能影响 说明
worker_processes CPU 核数 最优吞吐 多于 CPU 核数会因上下文切换降速
worker_connections 65536 连接容量 受系统 FD 限制,决定单worker最大连接数
worker_priority -10 CPU 分配 优先级高,让系统进程让步,响应更快
keepalive_timeout 60 连接复用 平衡连接复用效率和僵尸连接数量
tcp_nopush + sendfile on 吞吐量/CPU 减少网络包数并启用零拷贝,大幅提升静态文件发送效率

6️⃣ 监控、告警与故障排查

没有监控的优化是盲目的。部署完成后,必须建立可观测性体系。

启用 Nginx 原生状态监控:
在配置文件的 server 块中添加:

location /nginx_status {
    stub_status on;
    access_log off;
    allow 127.0.0.1; # 限制访问IP
    deny all;
}

访问 http://your-server:port/nginx_status 可看到关键指标:活跃连接数(Active connections)、总处理请求数、读/写/等待状态的连接数。

健康的百万级 Nginx 基准指标:

  • 吞吐量:> 100K RPS(单机 16C32G)
  • 延迟:p50 < 10ms, p95 < 50ms, p99 < 200ms
  • 连接数:可稳定维持 > 50万 ESTABLISHED 连接
  • 资源:CPU < 80%,内存 < 90%,错误率 < 0.01%

常见问题快速排错表:

症状 诊断命令 可能根因 快速修复
RPS 瓶颈在 10-20K top 看 CPU worker太少或CPU受限 增加 worker_processes 或升级硬件
大量 TIME_WAIT 连接 netstat -an \| grep TIME_WAIT \| wc -l 后端慢或请求频繁 启用 tcp_tw_reuse (见Step1)
502 Bad Gateway tail /var/log/nginx/error.log 后端宕机或连接耗尽 检查后端服务,增加 upstream keepalive
内存持续增长 free -h 监控1小时 内存泄漏 升级 Nginx 版本或检查第三方模块
请求延迟高 wrk -t10 -c1000 -d30s 测试 后端处理慢或网络问题 优化后端应用,检查网络链路

7️⃣ 最佳实践与 FAQ

最佳实践:

  1. 保留20%余量:CPU、内存、连接数使用率最好都控制在80%以下,以应对突发流量。
  2. 定期压测建立基线:每月用 wrk 等工具进行压测,对比历史数据,提前发现性能衰减。
  3. 自动化部署:使用 Ansible/Terraform 等工具部署配置,减少人工错误,方便回滚。
  4. 分析慢请求日志:定期分析 access.log 中响应时间过长的请求(如 >100ms),定位上游瓶颈。

FAQ:

  • Q1:真能达到100万连接吗?
    A:可以。早期限制源于默认配置(worker_connections=512)。现代方案通过提升系统FD上限、优化内核参数、合理配置Nginx(如16 workers × 6.25万连接),百万连接是理论可达的稳定目标。
  • Q2:100万连接占用多少内存?
    A:约20-30GB。主要取决于每个连接的缓冲区大小(net.core.rmem/wmem)。大部分是空闲连接的 socket buffer 开销。
  • Q3:后端处理不过来怎么办?
    A:利用 Nginx 的缓冲和队列能力。设置 proxy_buffering on; 让 Nginx 先缓冲响应。同时,在 upstream 中合理设置 keepalivemax_fails 对后端进行限流和保护。
  • Q4:能用 Nginx 完全替代硬件负载均衡器(LB)吗?
    A:在负载均衡功能上可以,但高可用(HA)需要额外组件(如 Keepalived 实现 VIP 漂移)。对于大规模生产环境,更推荐 “云LB → Nginx 集群 → 后端” 的分层架构,让专业LB做流量分发和高可用,Nginx专注于业务流量处理和应用层优化。

通过以上从系统内核、Nginx配置、压力测试到监控告警的全套优化方案,你可以构建出能够应对百万级并发的高性能 Web 服务前端。记住,调优是一个持续的过程,需要结合实际的监控数据不断迭代。如果在实践中遇到更多具体问题,欢迎在技术社区进行深度交流,例如在 云栈社区 的相关板块与同行们一起探讨。




上一篇:从算法到逻辑:2025年程序开发者必读的12本数学学习指南
下一篇:MCP vs CLI 技术路线之争:从Perplexity弃用MCP看AI智能体工具调用的性能博弈
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-8 08:57 , Processed in 0.633770 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表