高流量网站的Nginx性能调优必备清单

一份实用的Nginx性能清单,涵盖工作进程、连接、缓冲区、缓存、压缩、日志、超时、TLS和静态文件。

高流量网站的Nginx性能调优必备清单

将Nginx性能调优视为一份清单,而不是一场猜谜游戏,这样会更容易。从决定Nginx能接受多少流量的限制开始,然后向外扩展到缓冲、缓存、压缩、日志记录、超时、TLS以及其背后的后端服务。

不要一次性将所有指令都投入生产环境。一份好的Nginx性能调优清单能帮助你决定检查什么、为什么重要,以及过度调整可能出什么问题。对于一个以静态内容为主的文档网站来说,正确的设置并不适用于长轮询API或文件上传服务。

1. 优化工作进程和连接

Nginx采用主-从进程模型。主进程读取配置并管理工作进程,而工作进程则处理实际的客户端请求。正确配置这些可以显著提高并发性和资源利用率。

worker_processes

该指令决定Nginx将生成多少个工作进程。通常,将其设置为auto可以让Nginx检测CPU核心数并生成相等数量的工作进程,这是一种常见的做法。

worker_connections

定义单个工作进程可以打开的最大并发连接数。此设置与worker_processes共同决定了Nginx理论上可以处理的总并发连接数(worker_processes * worker_connections)。

multi_accept

允许一个工作进程一次接受多个新连接,从而防止在高负载下出现潜在瓶颈。

# /etc/nginx/nginx.conf

worker_processes auto; # 通常设置为 'auto' 或 CPU 核心数

events {
    worker_connections 1024; # 根据服务器容量和预期负载调整
    multi_accept on;
}

提示: 如果CPU持续偏高,单独提高worker_connections并不能解决问题。首先确认CPU消耗是否来自TLS握手、压缩、日志记录、正则表达式密集型路由或上游应用程序。

2. 高效的连接管理

优化Nginx处理网络连接的方式可以减少开销并提高响应能力。

keepalive_timeout

指定一个保持活动的客户端连接将保持打开多长时间。重用连接可以减少建立新TCP连接和SSL握手的开销。根据应用程序的交互性,常见值为15-65秒。

sendfile

启用文件描述符之间的直接数据传输,绕过用户空间缓冲。这在提供静态文件时能显著提高性能。

tcp_nopush

sendfile配合使用。Nginx尝试在一个数据包中发送HTTP头部和文件的开头部分。之后,它以完整的数据包发送数据。这减少了发送的数据包数量。

tcp_nodelay

指示Nginx在数据可用时立即发送,无需缓冲。这对于低延迟比最大化吞吐量更重要的交互式应用程序(例如聊天应用程序或实时更新)非常有益。

http {
    keepalive_timeout 65; # 保持连接65秒
    sendfile on;
    tcp_nopush on; # 需要 sendfile on
    tcp_nodelay on; # 对代理动态内容有用
}

3. 缓冲区优化

Nginx使用缓冲区来处理来自客户端的请求和来自上游服务器(如应用服务器)的响应。正确调整这些缓冲区的大小可以防止不必要的磁盘I/O,减少内存使用,并提高吞吐量。

客户端缓冲区

  • client_body_buffer_size:用于客户端请求体的缓冲区大小。如果请求体超过此大小,则会被写入临时文件。
  • client_header_buffer_size:用于客户端请求的第一行和头部的缓冲区大小。
  • large_client_header_buffers:定义用于读取客户端请求头部的较大缓冲区的数量和大小。对于包含许多cookie或长referer头的请求很有用。

代理缓冲区(用于反向代理设置)

  • proxy_buffers:用于读取来自代理服务器响应的缓冲区的数量和大小。
  • proxy_buffer_size:用于读取响应的第一个缓冲区的大小。通常较小,因为它通常只包含头部。
  • proxy_busy_buffers_size:在任何给定时间可以处于“忙”状态(正在发送给客户端)的最大响应缓冲区量。

FastCGI缓冲区(用于PHP-FPM等)

  • fastcgi_buffers:用于读取来自FastCGI服务器响应的缓冲区的数量和大小。
  • fastcgi_buffer_size:用于读取响应的第一个缓冲区的大小。
http {
    # 客户端缓冲区
    client_body_buffer_size 1M; # 根据预期的请求体大小(例如文件上传)调整
    client_header_buffer_size 1k;
    large_client_header_buffers 4 8k; # 4个缓冲区,每个8KB

    # 代理缓冲区(如果Nginx充当反向代理)
    proxy_buffers 8 16k; # 8个缓冲区,每个16KB
    proxy_buffer_size 16k; # 第一个缓冲区16KB
    proxy_busy_buffers_size 16k; # 最大16KB的忙缓冲区

    # FastCGI缓冲区(如果Nginx与PHP-FPM配合使用)
    fastcgi_buffers 16 16k; # 许多PHP-FPM应用程序的起点
    fastcgi_buffer_size 16k; # 第一个缓冲区16KB
}

警告: 将缓冲区设置得太小可能导致磁盘I/O和性能下降。设置得太大可能消耗过多内存。通过测试找到平衡点。

4. 实施强大的缓存策略

缓存是提高性能和减少后端服务器负载的最有效方法之一。Nginx可以充当强大的内容缓存。

proxy_cache_path

定义缓存目录的路径、大小、子目录级别数以及非活动项目在缓存中保留的时间。

proxy_cache

为给定的location块激活缓存,引用在proxy_cache_path中定义的区域。

proxy_cache_valid

设置Nginx应缓存具有特定HTTP状态码的响应的时间。

proxy_cache_revalidate

启用后,Nginx将使用If-Modified-SinceIf-None-Match头与后端重新验证缓存内容,从而减少带宽使用。

proxy_cache_use_stale

指示Nginx在后端服务器宕机、无响应或遇到错误时提供过期的缓存内容。这大大提高了可用性。

expires

为静态文件的客户端缓存设置Cache-ControlExpires头。这最大限度地减少了对Nginx的重复请求。

http {
    # 在http块中定义代理缓存区域
    proxy_cache_path /var/cache/nginx/my_cache levels=1:2 keys_zone=my_cache:10m inactive=60m max_size=10g;

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://my_upstream_backend;
            proxy_cache my_cache; # 为此位置启用缓存
            proxy_cache_valid 200 302 10m; # 缓存成功响应10分钟
            proxy_cache_valid 404 1m; # 缓存404响应1分钟
            proxy_cache_revalidate on;
            proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
            add_header X-Cache-Status $upstream_cache_status; # 有助于调试
        }

        # 在浏览器中长时间缓存静态文件
        location ~* \.(jpg|jpeg|gif|png|css|js|ico|woff|woff2|ttf|svg|eot)$ {
            expires 30d; # 缓存30天
            add_header Cache-Control "public, no-transform";
            # 对于静态文件,如果未代理,考虑直接从Nginx提供
            root /var/www/html;
        }
    }
}

5. 启用Gzip压缩

在将响应发送给客户端之前进行压缩,可以显著减少带宽使用并改善页面加载时间,尤其是对于基于文本的内容。

gzip on

激活gzip压缩。

gzip_comp_level

设置压缩级别(1-9)。级别1最快但压缩率最低;级别9最慢但压缩率最高。级别6通常提供良好的平衡。

gzip_types

指定应压缩的MIME类型。包括常见的文本、CSS、JavaScript和JSON类型。

gzip_min_length

设置应启用压缩的响应最小长度(以字节为单位)。小文件受益不大,甚至可能因压缩开销而变慢。

gzip_proxied

指示Nginx即使响应是代理的也要进行压缩。any是一个常见值。

gzip_vary

向响应添加Vary: Accept-Encoding头,通知代理响应可能因Accept-Encoding请求头而异。

http {
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6; # 压缩级别1-9(6是一个很好的平衡点)
    gzip_buffers 16 8k; # 16个缓冲区,每个8KB
    gzip_http_version 1.1; # 压缩的最低HTTP版本
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
    gzip_min_length 1000; # 仅压缩大于1KB的响应
}

6. 优化日志记录

虽然日志对于监控和故障排除至关重要,但过多或未经优化的日志记录可能会引入显著的磁盘I/O,尤其是在高流量站点上。

access_log

  • 禁用静态资源的日志:对于高访问量的静态内容(图片、CSS、JS),禁用access_log可以节省大量I/O。
  • 缓冲:Nginx可以在将日志条目写入磁盘之前在内存中缓冲它们,从而减少磁盘写入的频率。这里使用bufferflush参数。

error_log

设置适当的日志记录级别(criterrorwarninfodebug)。对于生产环境,warnerror通常足以捕获关键问题,而不会淹没日志。

http {
    server {
        # 动态内容的默认访问日志
        access_log /var/log/nginx/access.log main;

        location ~* \.(jpg|jpeg|gif|png|css|js|ico|woff|woff2|ttf|svg|eot)$ {
            access_log off; # 禁用常见静态文件的日志记录
            expires 30d;
        }
    }

    # 主HTTP上下文的缓冲访问日志示例
    # access_log /var/log/nginx/access.log main buffer=16k flush=5s;
    error_log /var/log/nginx/error.log warn; # 仅记录警告及以上级别
}

7. 调整超时时间

适当配置的超时时间可以防止Nginx长时间持有非活动连接,从而释放资源。

客户端超时

  • client_body_timeout:Nginx等待客户端发送请求体的时间。
  • client_header_timeout:Nginx等待客户端发送请求头部的时间。
  • send_timeout:Nginx在发送响应后等待客户端接受的时间。

代理/FastCGI超时(如果适用)

  • proxy_connect_timeout:与代理服务器建立连接的超时时间。
  • proxy_send_timeout:向代理服务器传输请求的超时时间。
  • proxy_read_timeout:从代理服务器读取响应的超时时间。
http {
    client_body_timeout 15s; # 客户端有15秒发送请求体
    client_header_timeout 15s; # 客户端有15秒发送头部
    send_timeout 15s; # Nginx有15秒向客户端发送响应

    # 代理场景
    proxy_connect_timeout 5s; # 5秒连接到上游
    proxy_send_timeout 15s; # 15秒向上游发送请求
    proxy_read_timeout 15s; # 15秒从上游读取响应

    # FastCGI场景
    fastcgi_connect_timeout 5s;
    fastcgi_send_timeout 15s;
    fastcgi_read_timeout 15s;
}

8. SSL/TLS优化

对于启用HTTPS的站点,优化SSL/TLS设置对于减少CPU开销和提高握手性能至关重要。

ssl_session_cachessl_session_timeout

启用SSL会话缓存,以避免对来自同一客户端的后续连接进行计算成本高昂的完整TLS握手。

ssl_protocolsssl_ciphers

使用现代TLS协议,如TLSv1.2TLSv1.3。小心复制的密码字符串:TLS 1.3密码的控制方式与旧版TLS密码套件不同,发行版的默认设置通常比旧指南中的过时示例更安全。

ssl_stapling

启用OCSP装订,Nginx定期从CA获取OCSP响应并将其“装订”到SSL/TLS握手。这通过避免单独的OCSP查询来减少客户端延迟。

server {
    listen 443 ssl;
    ssl_certificate /etc/nginx/ssl/your_domain.crt;
    ssl_certificate_key /etc/nginx/ssl/your_domain.key;

    ssl_session_cache shared:SSL:10m; # 共享缓存,用于10MB的会话数据
    ssl_session_timeout 10m; # 会话在10分钟后过期

    ssl_protocols TLSv1.2 TLSv1.3; # 使用现代、安全的协议
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
    ssl_prefer_server_ciphers on;

    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 1.1.1.1 8.8.8.8 valid=300s; # 使用适合您环境的解析器
    resolver_timeout 5s;
}

9. 打开文件缓存

Nginx可以缓存频繁访问文件的文件描述符,从而减少重复的系统调用来打开和关闭文件。

open_file_cache

启用缓存,指定最大元素数以及非活动元素保留的时间。

open_file_cache_valid

设置缓存检查其元素有效性的频率。

open_file_cache_min_uses

指定在inactive时间内文件必须被访问的最小次数,才能保留在缓存中。

open_file_cache_errors

决定Nginx是否应缓存打开文件时的错误。

http {
    open_file_cache max=100000 inactive=60s; # 缓存最多100,000个文件描述符60秒
    open_file_cache_valid 80s; # 每80秒检查一次有效性
    open_file_cache_min_uses 1; # 缓存至少使用过一次的文件
    open_file_cache_errors on; # 缓存与文件打开相关的错误
}

10. 使用真实流量信号进行验证

清单上的最后一项是测量。在进行更改之前,捕获一个小的基线:请求延迟、5xx率、活动连接、CPU、内存、磁盘I/O、网络吞吐量和上游响应时间。更改后,比较相同的数字。

对于反向代理,$request_time$upstream_response_time特别有用。如果两者同时上升,则后端可能很慢。如果$request_time很高而上游时间很低或为空,请检查客户端上传速度、响应传输时间、缓冲、压缩或静态文件交付。如果这两个指标都无法解释问题,请检查错误日志和操作系统。

最安全的调优顺序很简单:使用nginx -t测试配置,尽可能重新加载而不是重启,监控日志,如果延迟或错误朝错误方向发展,则快速回滚。Nginx可以处理大量流量,但前提是其限制、内核和上游应用程序相互协调。