性能

工作进程数

worker_processes - 用于设置 Nginx 的工作进程数。

  • worker_processes 的默认值为 1。
  • 设置 worker_processes 的安全做法是将其设为 auto,则启动 Nginx 时会自动分配工作进程数。当然,也可以显示的设置一个工作进程数值。
  • 一般一个进程足够了,你可以把连接数设得很大。(worker_processes: 1,worker_connections: 10,000)如果有 SSL、gzip 这些比较消耗 CPU 的工作,而且是多核 CPU 的话,可以设为和 CPU 的数量一样。或者要处理很多很多的小文件,而且文件总大小比内存大很多的时候,也可以把进程数增加,以充分利用 IO 带宽(主要似乎是 IO 操作有 block)

示例:

  1. ## The safest way:
  2. worker_processes auto;
  3. ## VCPU = 4 , expr $(nproc --all) - 1
  4. worker_processes 3;

最大连接数

worker_connections - 单个 Nginx 工作进程允许同时建立的外部连接的数量。数字越大,能同时处理的连接越多。

worker_connections 不是随便设置的,而是与两个指标有重要关联:

  • 内存
    • 每个连接数分别对应一个 read_event、一个 write_event 事件,一个连接数大概占用 232 字节,2 个事件总占用 96 字节,那么一个连接总共占用 328 字节,通过数学公式可以算出 100000 个连接数大概会占用 31M = 100000 * 328 / 1024 / 1024,当然这只是 nginx 启动时,worker_connections 连接数所占用的 nginx。
  • 操作系统级别”进程最大可打开文件数“。
    • 进程最大可打开文件数受限于操作系统,可通过 ulimit -n 命令查询,以前是 1024,现在是 65535。
    • nginx 提供了 worker_rlimit_nofile 指令,这是除了 ulimit 的一种设置可用的描述符的方式。 该指令与使用 ulimit 对用户的设置是同样的效果。此指令的值将覆盖 ulimit 的值,如:worker_rlimit_nofile 20960; 设置 ulimits:ulimit -SHn 65535

使用 HTTP/2

HTTP / 2 将使我们的应用程序更快,更简单且更可靠。 HTTP / 2 的主要目标是通过启用完整的请求和响应多路复用来减少延迟,通过有效压缩 HTTP 标头字段来最小化协议开销,并增加对请求优先级和服务器推送的支持。

HTTP / 2 与 HTTP / 1.1 向后兼容,因此有可能完全忽略它,并且一切都会像以前一样继续工作,因为如果不支持 HTTP / 2 的客户端永远不会向服务器请求 HTTP / 2 通讯升级:它们之间的通讯将完全是 HTTP1 / 1。

请注意,HTTP / 2 在单个 TCP 连接中多路复用许多请求。 通常,当使用 HTTP / 2 时,将与服务器建立单个 TCP 连接。

您还应该包括 ssl 参数,这是必需的,因为浏览器不支持未经加密的 HTTP / 2。

HTTP / 2 对旧的和不安全的密码有一个非常大的黑名单,因此您应该避免使用它们。

示例:

  1. server {
  2. listen 10.240.20.2:443 ssl http2;
  3. ...

维护 SSL 会话

客户端每次发出请求时都进行新的 SSL 握手的需求。默认情况下,内置会话缓存并不是最佳选择,因为它只能由一个工作进程使用,并且可能导致内存碎片,最好使用共享缓存。

使用 ssl_session_cache 时,通过 SSL 保持连接的性能可能会大大提高。10M 的值是一个很好的起点(1MB 共享缓存可以容纳大约 4,000 个会话)。通过共享,所有工作进程之间共享一个缓存(可以在多个虚拟服务器中使用相同名称的缓存)。

但是,大多数服务器不清除会话或票证密钥,因此增加了服务器受到损害将泄漏先前(和将来)连接中的数据的风险。

示例:

  1. ssl_session_cache shared:NGX_SSL_CACHE:10m;
  2. ssl_session_timeout 12h;
  3. ssl_session_tickets off;
  4. ssl_buffer_size 1400;

尽可能在 server_name 指令中使用确切名称

确切名称,以星号开头的通配符名称和以星号结尾的通配符名称存储在绑定到侦听端口的三个哈希表中。

首先搜索确切名称哈希表。 如果未找到名称,则搜索具有以星号开头的通配符名称的哈希表。 如果未在此处找到名称,则搜索带有通配符名称以星号结尾的哈希表。 搜索通配符名称哈希表比搜索精确名称哈希表要慢,因为名称是按域部分搜索的。

正则表达式是按顺序测试的,因此是最慢的方法,并且不可缩放。由于这些原因,最好在可能的地方使用确切的名称。

示例:

  1. ## It is more efficient to define them explicitly:
  2. server {
  3. listen 192.168.252.10:80;
  4. server_name example.org www.example.org *.example.org;
  5. ...
  6. }
  7. ## Than to use the simplified form:
  8. server {
  9. listen 192.168.252.10:80;
  10. server_name .example.org;
  11. ...
  12. }

避免使用 if 检查 server_name

当 Nginx 收到请求时,无论请求的是哪个子域,无论是 www.example.com 还是普通的 example.com,如果始终对 if 指令进行评估。 由于您是在请求 Nginx 检查每个请求的 Host 标头。 效率极低。

而是使用两个服务器指令,如下面的示例。 这种方法降低了 Nginx 的处理要求。

示例:

❌ 错误配置

  1. server {
  2. server_name domain.com www.domain.com;
  3. if ($host = www.domain.com) {
  4. return 301 https://domain.com$request_uri;
  5. }
  6. server_name domain.com;
  7. ...
  8. }

⭕ 正确配置

  1. server {
  2. server_name www.domain.com;
  3. return 301 $scheme://domain.com$request_uri;
  4. ## If you force your web traffic to use HTTPS:
  5. ## 301 https://domain.com$request_uri;
  6. ...
  7. }
  8. server {
  9. listen 192.168.252.10:80;
  10. server_name domain.com;
  11. ...
  12. }

使用 $request_uri 来避免使用正则表达式

使用内置变量 $request_uri,我们可以完全避免进行任何捕获或匹配。默认情况下,正则表达式的代价较高,并且会降低性能。

此规则用于解决将 URL 不变地传递到新主机,确保仅通过现有 URI 进行返回的效率更高。

示例:

❌ 错误配置

  1. ## 1)
  2. rewrite ^/(.*)$ https://example.com/$1 permanent;
  3. ## 2)
  4. rewrite ^ https://example.com$request_uri? permanent;

⭕ 正确配置

  1. return 301 https://example.com$request_uri;

使用 try_files 指令确认文件是否存在

try_files is definitely a very useful thing. You can use try_files directive to check a file exists in a specified order.

You should use try_files instead of if directive. It’s definitely better way than using if for this action because if directive is extremely inefficient since it is evaluated every time for every request.

The advantage of using try_files is that the behavior switches immediately with one command. I think the code is more readable also.

try_files allows you:

  • to check if the file exists from a predefined list
  • to check if the file exists from a specified directory
  • to use an internal redirect if none of the files are found

示例:

❌ 错误配置

  1. ...
  2. root /var/www/example.com;
  3. location /images {
  4. if (-f $request_filename) {
  5. expires 30d;
  6. break;
  7. }
  8. ...
  9. }

⭕ 正确配置

  1. ...
  2. root /var/www/example.com;
  3. location /images {
  4. try_files $uri =404;
  5. ...
  6. }

使用 return 代替 rewrite 来做重定向

您应该使用服务器块和 return 语句,因为它们比通过位置块评估 RegEx 更简单,更快捷。 该指令停止处理,并将指定的代码返回给客户端。

示例:

❌ 错误配置

  1. server {
  2. ...
  3. if ($host = api.domain.com) {
  4. rewrite ^/(.*)$ http://example.com/$1 permanent;
  5. }
  6. ...

⭕ 正确配置

  1. server {
  2. ...
  3. if ($host = api.domain.com) {
  4. return 403;
  5. ## or other examples:
  6. ## return 301 https://domain.com$request_uri;
  7. ## return 301 $scheme://$host$request_uri;
  8. }
  9. ...

开启 PCRE JIT 来加速正则表达式处理

允许使用 JIT 的正则表达式来加速他们的处理。

通过与 PCRE 库编译 Nginx 的,你可以用你的 location 块进行复杂的操作和使用功能强大的 return 和 rewrite。

PCRE JIT 可以显著加快正则表达式的处理。 Nginx 的与 pcre_jit 比没有更快的幅度。

如果你试图在使用 pcre_jit;没有可用的 JIT,或者 Nginx 的与现有 JIT,但当前加载 PCRE 库编译不支持 JIT,将配置解析时发出警告。

当您编译使用 NGNIX 配置 PCRE 库时,才需要—with-PCRE-JIT 时(./configure —with-PCRE =)。当使用系统 PCRE 库 JIT 是否被支持依赖于库是如何被编译。

从 Nginx 的文档:

JIT 正在从与—enable-JIT 配置参数内置 8.20 版本开始 PCRE 库提供。当 PCRE 库与 nginx 的内置(—with-PCRE =)时,JIT 支持经由—with-PCRE-JIT 配置参数使能。

示例:

  1. ## In global context:
  2. pcre_jit on;

进行精确的位置匹配以加快选择过程

精确的位置匹配通常用于通过立即结束算法的执行来加快选择过程。

示例:

  1. ## Matches the query / only and stops searching:
  2. location = / {
  3. ...
  4. }
  5. ## Matches the query /v9 only and stops searching:
  6. location = /v9 {
  7. ...
  8. }
  9. ...
  10. ## Matches any query due to the fact that all queries begin at /,
  11. ## but regular expressions and any longer conventional blocks will be matched at first place:
  12. location / {
  13. ...
  14. }

使用 limit_conn 改善对下载速度的限制

Nginx provides two directives to limiting download speed:

Nginx 提供了两个指令来限制下载速度:

  • limit_rate_after - 设置 limit_rate 指令生效之前传输的数据量
  • limit_rate - 允许您限制单个客户端连接的传输速率

此解决方案限制了每个连接的 Nginx 下载速度,因此,如果一个用户打开多个(例如) 视频文件,则可以下载 X * 连接到视频文件的次数

示例:

  1. ## Create limit connection zone:
  2. limit_conn_zone $binary_remote_addr zone=conn_for_remote_addr:1m;
  3. ## Add rules to limiting the download speed:
  4. limit_rate_after 1m; ## run at maximum speed for the first 1 megabyte
  5. limit_rate 250k; ## and set rate limit after 1 megabyte
  6. ## Enable queue:
  7. location /videos {
  8. ## Max amount of data by one client: 10 megabytes (limit_rate_after * 10)
  9. limit_conn conn_for_remote_addr 10;
  10. ...