[翻译] Nginx 备战-优化指南

对于上线优化这个事情,个人来说更加倾向于使用一些快速的调优模式首先来解决 80% 的问题。感觉上这篇“Battle ready Nginx – an optimization guide”讲得还是很到位了。所以随手翻译了,分享给大家。

———— 翻译分隔线 ————

Nginx 备战-优化指南

作者:Zachary Orr

大多数关于 Nginx 的指南只告诉你那最基本的部分:apt-get 一个包,修改这里和那里的某些行,然后就得到了一个 web 服务器!而在大多数情况下,一个常见的 nginx 安装也能为你的网站提供良好的服务。然而如果你的确想进一步压榨 nginx 的性能,那么就必须走得更远一些。在这个指南中,我将会解释通过调整 nginx 中的哪些设置可以得到更好的性能,来应对大量客户端请求。同时要注意,这不是完整的调优指南。只是对一些可以调整来改进性能的设置的简单概述。请务必具体情况具体对待。

(优化后的)基础配置

接下来唯一要修改的文件就是 nginx.conf,它保存有 nginx 不同模块的全部设置。可以在服务器的 /etc/nginx 目录找到 nginx.conf。首先,先探讨一下一些全局的设置,然后看看配置里的每个模块,并讨论一下哪个设置能在巨大的客户端连接数时,带来更好的性能,以及为什么它们可以提升性能。一个完整的配置文件可以在文章的结尾找到。

顶级配置

在 nginx.conf 文件中,有一些在模块之外的顶级配置。

user www-data;
pid /var/run/nginx.pid;

worker_processes auto;

worker_rlimit_nofile 8192;

userpid 应当使用默认值——由于它对我们的目的没有任何影响,所以我们不会修改这部分。

worker_processes 定义了 nginx 在为你的网站提供服务时,worker 进程的数量。优化值受到包括(单不限于)CPU 内核数、存储数据的磁盘数、负载值在内的许多因素的影响。如果不确定的话,将其设置为可用的 CPU 内核的数量是一个不错的开始(设置为“auto”,将会尝试自动检测可用的值)。

worker_rlimit_nofile 修改了 worker 进程打开文件数的最大值限制。如果不设置,操作系统会限制它。当操作系统和 nginx 处理超过“ulimit -n”的数量的文件的情况时产生报告,因此将这个数值调大,这样 nginx 就不会遇到“too many open files”的问题。

# bump up our hard limit
sudo sh -c ulimit -HSn 100000

为了让这个设置(在重启以后)永久生效,需要修改系统的配置文件。添加(如果已经有了,就修改)下面的行

> sudo nano /etc/security/limits.conf

* soft nofile 200000
* hard nofile 200000

然后务必重启,或者通过其他途径让 limits 生效

events 模块

worker_connections 设置了一个 worker 进程可以同时打开的链接数。由于已经调整了 worker_rlimit_nofile,所以可以安全的将这个数值调整得很大。

multi_accept 告诉 nginx 在收到新链接的请求的通知时,尽可能接受链接。

use 设置了线程使用哪个池化方法来多个客户端。如果使用 Linux 2.6+,应当设置为 epoll。如果使用 *BSD,应当设置为 kqueue。想了解更多关于事件池?让 Wikipedia 做你的向导吧(警告,如果要弄明白所有事情,一副马克思式的大胡子和一门操作系统课程是必须的)。

(需要注意的是,如果没有指定 nginx 使用哪个池化方法,它将会根据操作系统选择最合适的那个)

HTTP 模块

HTTP 模块控制了 nginx 的 http 处理的核心功能。虽然这里只有很少的设置项,但我们还是要简单的了解一下。每个配置的片段都应当放在 http 模块中,后面不再特别说明。

http {

    sendfile on;

    tcp_nopush on;
    tcp_nodelay on;

    ...
}

sendfile 激活了 sendfile()。sendfile() 在磁盘和 TCP 端口(或者任意两个文件描述符)之间复制数据。在 sendfile 出现之前,为了传输这样的数据需要在用户空间上分配一块数据缓存。使用 read() 从源文件读取数据到缓存,然后使用 write() 将缓存写入到网络。sendfile() 直接从磁盘上读取数据到操作系统缓冲。由于这个操作是在内核中完成的,sendfile() 比伴随上下文切换/缓冲垃圾的 read() 和 write() 联合使用要更加有效率(了解更多关于 sendfile)。

tcp_nopush 告诉 nginx 在一个包中发送全部的头文件,而不是一个一个发送。

tcp_nodelay 告诉 nginx 不要缓存数据,应该快速的发送小数据——这仅仅应该使用在哪些频繁发送小的碎片信息而无需立刻获取响应的,需要实时传递数据的应用中。

    access_log off;

    error_log /var/log/nginx/error.log crit;

access_log 确定了 nginx 是否保存访问日志。将这个设置为关闭可以降低磁盘 IO 而提升速度(换句话说,你只有一条命)。

error_log 告诉 nginx 应当记录临界错误。

    keepalive_timeout 20;

    client_header_timeout 20;
    client_body_timeout 20;

    reset_timedout_connection on;

    send_timeout 20;

keepalive_timeout 指定了与客户端的 keep-alive 链接的超时时间。服务器会在这个时间后关闭链接。我们会降低这个值,以避免让 worker 过长时间的忙碌。

client_header_timeoutclient_body_timeout (分别)设置了请求头和请求体的超时时间。这个值也应该设置得较低。

reset_timedout_connection 告诉 nginx 当客户端失去相应时关闭链接。这将会释放为该客户端分配的所有内存。

send_timeout 指定了响应客户端的超时时间。这个时间并不是指整个传输时间,而是在客户端两次读操作之间的间隔。如果客户端在这个时间内没有准备好再次读取数据,nginx 会关闭链接。

    limit_conn_zone $binary_remote_addr zone=addr:5m;

    limit_conn addr 100;

limit_conn_zone 设定了可以按照键保存数据的共享内存区(例如当前链接数)的参数。5m 表示 5 兆字节,应当足够存储(32k * 5)32 字节状态,或者(16k * 5)64 字节状态。

limit_conn 设置了指定键值的最大连接接数。这里键为 addr,值为 100,因此将只允许每个 IP 100 个并发连接。

    include /etc/nginx/mime.types;

    default_type text/html;

    charset UTF-8;

include 会直接包含其他文件的内容到当前文件。这里加载了 MIME 的列表以待使用。

default_type 设置了文件默认的 MIME 类型。

charset 设置了头中包含的默认的字符集。

下面的两个性能改进的选项在 WebMasters StackExchange 的这个伟大问题中给予了说明。

    gzip on;
    
    # gzip_static on;

    gzip_proxied any;

    gzip_min_length 256;
    
    gzip_comp_level 4;

    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

gzip 告诉 nginx gzip 压缩发送的数据。这会减少需要发送的数据的数量。

gzip_static 告诉 nginx 告诉 nginx 在对内容进行 gzip 之前,寻找相同名字的已经 gzip 过的内容。这需要你预先压缩文件(在这个例子中被注释掉了),但这样可以使用最高的压缩比,同时 nginx 不再压缩这些文件(在这里了解更多关于 gzip_static 的信息)。

gzip_proxied 允许或禁止基于请求/响应的压缩。设置为 any,就可以 gzip 所有的请求。

gzip_min_length 设置了 gzip 数据的最小的字节数。由于 gzip 压缩会降低处理请求的速度,所以如果一个请求小于 1000 字节,将不对其进行压缩。

gzip_comp_level 设置了数据压缩的等级。等级可以是 1-9 的任意一个值,9 表示最慢但是最高比例的压缩。设置为 4 是一个不错的折中选择。

gzip_types 设置进行 gzip 的类型。有上面这些,不过还可以添加更多。

    # cache informations about file descriptors, frequently accessed files
    # can boost performance, but you need to test those values
    open_file_cache max=100000 inactive=20s; 
    open_file_cache_valid 30s; 
    open_file_cache_min_uses 2;
    open_file_cache_errors on;

    ##
    # Virtual Host Configs
    # aka our settings for specific servers
    ##

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

open_file_cache 启用了缓冲区并指定了在缓存区中的实体数量,以及缓存的时长。最大值设置一个尽可能大的值,而它们超过 20 秒未被使用的话,就将其移除缓冲区。

open_file_cache_valid 指定了检查 open_file_cache 中信息的有效性的时间间隔。

open_file_cache_min_uses 定义了 open_file_cache 中在指定时间间隔里,闲置文件的最小使用次数。

open_file_cache_errors 指定了在查找一个文件的时候,是否缓存错误。

include 再次向配置添加了一些文件。引入了定义在不同文件中的服务模块。如果你的服务模块不在这个路径下,你应当修改这几行到正确的路径。

完整的配置文件:

user www-data;
pid /var/run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 8192;

events {
    worker_connections 2048;
    multi_accept on;
    use epoll;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    access_log off;
    error_log /var/log/nginx/error.log crit;

    keepalive_timeout 20;
    client_header_timeout 20;
    client_body_timeout 20;
    reset_timedout_connection on;
    send_timeout 20;

    limit_conn_zone $binary_remote_addr zone=addr:5m;
    limit_conn addr 100;

    include /etc/nginx/mime.types;
    default_type text/html;
    charset UTF-8;

    gzip on;
    gzip_proxied any;
    gzip_min_length 256;
    gzip_comp_level 4;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    open_file_cache max=100000 inactive=20s; 
    open_file_cache_valid 30s; 
    open_file_cache_min_uses 2;
    open_file_cache_errors on;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

在编辑完配置后,请确保重新加载/重启(reload/restart)nginx,以便使用新的配置文件。

sudo service nginx reload

补充

就这样了!你的 web 服务器已经准备好与之前困扰你的访问者军团开战了。当然,这并不意味着你只有这一种方法来加速你的网站。近期我将会撰写更多的文章,来介绍加速你网站的其他方法。

想反馈?给我发电子邮件或在 加入 HackerNews 上的讨论

Leave a comment

Your email address will not be published. Required fields are marked *