参考来源: Nginx 从入门到企业实战二
Nginx 调优与背后原理
Linux 内核方面调优
首先,需要修改 /etc/sysctl.conf 文件来更改内核参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42net.ipv4.tcp_synack_retries = 2 # 如果服务器没有收到 ACK,就会一直重发 SYN+ACK 报文。当网络繁忙、不稳定时,报文丢失就会变严重,此时应该调大重发次数。反之则可以调小重发次数。
net.core.netdev_max_backlog = 250000 # 表示当每个网络接口接受数据包的速率比内核处理这些包的速率快时,允许发送队列的数据包的最大数目
net.ipv4.tcp_max_syn_backlog = 10240 # 半连接队列长度,该参数用于记录尚未收到客户端确认信息的连接请求的最大值,对于拥有128内存的系统而言,此参数的默认值为1024,对小内存的系统则是128,一般在系统内存比较充足的情况下,可以增大这个参数的赋值。
net.core.somaxconn = 10240 # 全连接长度,该参数用于调节系统同时发起的TCP连接数,一般默认值为128,在客户端高并发的请求的情况下,该默认值较小,可能导致连接超时或者重传问题,我们可以根据实际情况结合并发数来调节此值。
net.ipv4.tcp_syncookies = 1 # 用来减少 STN 洪水攻击的影响
net.ipv4.tcp_syn_retries = 2 # 设置重发 SYN 报文的次数,默认为 6 次
# 注:client 端在多次重发 SYN 包而得不到回应时返回 connection time out 错误
# 通过命令行查看会有如下信息提示
# xxxxxx SYNs to LISTEN sockets dropped 也就是 SYN 包被丢弃
# net.ipv4.tcp_fastopen # 简称 TFO,有4个可用值,分别为 0、1、2、3,其含义如下:
# 0. 表示禁用 TFO 功能
# 1. 作为客户端时可以使用 TFO 功能
# 2. 作为服务端时可以使用 TFO 功能
# 3. 无论是作为客户端还是服务端都可以使用 TFO 功能
# 在实际的使用中需要客户端和服务端同时支持 TFO 功能才能启动(目前浏览器好像只有 Chrome 才支持,所以真正应用的场景并不多
# 另外,在 Nginx 服务中使用该功能,为了防止带有数据的 SYN 共计,一般会限制 TFO 队列的连接长度,比如 listen 127.0.0.1:8000 fastopen=1024;
net.ipv4.ip_local_port_range = 1024 65000 # 默认值为 32768~60999 表示主动连接端可以建立的随机端口范围。但是当 Server 端开启了 net.ipv4.tcp_syncookies = 1,那么 SYN 半连接队列就没有逻辑上的最大值了
net.ipv4.tcp_abort_on_overflow = 1 # 当全连接队列满了之后,即使 client 继续向 server 发送 ACK 的包,服务端会通过该参数的值来决定如何响应
# 0 表示直接丢弃该 ACK,服务端过一段时间再次发送 SYN+ACK 给 client(也就是重新走握手的第二步),如果 client 超时等待比较短,就会返回 read timeout
# 1 表示 发送 RST 通知 Client,Client 端会返回 connection reset by peer.
net.ipv4.tcp_rmem = 16384 1048576 12582912 # 读缓冲区的最小值,默认值,最大值,单位为字节,其默认值与最大值会覆盖内核参数 net.core.rmem_default 与 net.core.rmem_max 的值
net.ipv4.tcp_wmem = 16384 1048576 12582912 # 写缓冲区的最小值,默认值,最大值,单位为字节,其默认值与最大值会覆盖内核参数 net.core.wmem_default 与 net.core.wmem_max 的值
net.ipv4.tcp_mem = 1541646 2055528 3083292 # 分别表示系统无内存压力,启动压力模式的阈值,以及最大值,单位为页的数量;
net.ipv4.tcp_moderate_rcvbuf = 1 # 表示开启自动调整缓存模式
net.ipv4.tcp_orphan_retries = 3 # 表示发送 FIN 报文的重试次数,默认为 8次,0 也表示 8 次,如果服务器上有大量的 FIN_WAIT1 连接,肯定是网络层面出现问题,及时我们调小这个值,也只是减少该状态值出现的时间,而不能解决根本问题。但是适当调小该值,有利于在网络层面出现问题的情况下直接暴露出来,而不是长时间的尝试等待。一般在 web 服务器上我们会把这个值设置为2 或者 3
net.ipv4.tcp_fin_timeout = 15 # 表示主动断开连接端保持在 FIN_WAIT2 状态的超时时间
net.ipv4.tcp_max_tw_buckets = 5000 # 设置服务器允许的 TIME_WAIT 数量,调大这个值,只能解决 TIME_WAIT 过多导致的无法建立新连接的问题,而不能解决资源浪费的问题。如果 TIME_WAIT 的数量超过这个设置的值,将打印警告信息 TCP: time wait bucket table overflow
net.ipv4.tcp_tw_reuse = 1 # 开启后,我们在作为客户端时可以使用仍然处于 TIME_WAIT 状态的端口,与服务端使用之前维持的连接,但要解决数据报文重复造成的混乱问题以及 ACK 报文丢失造成的连接被重置问题,必须设置 net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_timestamps = 1 # 如果想启动 net.ipv4.tcp_tw_reuse 必须设先置该参数的值为 1
net.ipv4.tcp_tw_recycle = 0 # 当作为服务端时,用户经过 NAT 网络访问时,其源IP地址都会被转换成相同的出口地址,当来自同一个 IP 地址(任意源端口号),后来的数据包中 TCP 选项字段如果有 timestamp 且比前面的数据包中的 timestamp 小,则 server 不做 ACK 响应。目前较高版本 Linux 4.12 已经移除了该参数,所以尽量不要启动该功能,即设置 net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_retries1 = 2 # retries1 表示到达重传次数后更新路由缓存
net.ipv4.tcp_retries2 = 3 # retries2 表示到达重传次数后关闭 TCP 连接
net.ipv4.tcp_keepalive_time = 600 # 表示多久对一个 TCP 空闲连接发送一次心跳探测报文,单位是秒,默认值为 7200(2小时)
net.ipv4.tcp_keepalive_intvl = 2 # 探测包每次发送的时间间隔
net.ipv4.tcp_keepalive_probes = 3 # 探测包发送次数
fs.file-max = 500000000
# fs.file-max 表示操作系统可以使用的最大句柄数,该默认值太小,需要调整
# fs.file-nr = 768 0 398458 这个参数不需要设置,主要作用是看当前 已分配,正使用,以及最大值的文件句柄数
fs.nr_open = 10000000
# fs.nr_open 表示单个进程可以打开的文件句柄数,默认值已经很大了,但是如果是分布式图片服务器可能还是不足,可以适当调整设置用户级的文件描述符,可以通过创建文件
/etc/security/limits.d/nofile.conf
来实现1
2
3
4
5# vim /etc/security/limits.d/nofile.conf
* soft nofile 65535
* hard nofile 65535
root soft nofile 65535
root hard nofile 65535还可以在应用层面设置 worker_rlimit_nofile 参数,该参数配置在主配置文件 main 段
1
2
3Syntax: worker_rlimit_nofile number;
Default: -
COntext: main
Nginx 主配置段调优
在介绍完系统部分的优化内容后,Nginx 自身也提供了一些优化参数。
worker_priority
设置进程的静态优先级
1
2
3Syntax: worker_priority number;
Default: worker_priority 0;
Context: main在 nginx 官方文档中提到可以将该值设置为 -20
20,其实从 Linux 系统层面来说,超过 -2019 都是无意义的
设置完城后需要重启 Nginx 才能生效
worker_processes
设置 worker 的进程数
1
2
3Syntax: worker_processes number | auto;
Default: worker_processes 1;
Context: main如果设置成 auto,则会根据 CPU 核心数自动创建相同个数的 worker 进程,在 centOS 中可以通过以下命令查看 CPU 核心数
1
lscpu |grep CPU\(s\)
worker_cpu_affinity
设置 CPU 绑定提高缓存命中率
1
2
3
4Syntax: worker_cpu_affinity cpumask ...;
worker_cpu_affinity auto [cpumask];
Default: —
Context: main示例: 对于四核心的 CPU 应该这样绑定
1
2worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;假如我只想启动2个 worker 进程,并且仅使用 CPU0 与 CPU3,则可以这样设置
1
2worker_processes 2;
worker_cpu_affinity 0001 1000;查看自己节点上 CPU 的三级缓存大小
1
2
3
4
5
6
7
8
9# cat /sys/devices/system/cpu/cpu0/cache/index1/size # 1 级缓存大小
32K
# cat /sys/devices/system/cpu/cpu0/cache/index2/size # 2 级缓存大小
256K
# cat /sys/devices/system/cpu/cpu0/cache/index3/size # 3 级缓存大小
12288K
# cat /sys/devices/system/cpu/cpu0/cache/index3/shared_cpu_list # 3级缓存是一个 CPU 插槽上所有核心共享的,可以查看下哪几个核心在一个插槽上
0-1
reuseport
设置 worker 进程间的负载均衡
如何让多个 worker 进程间的响应处理达到比较合理的均衡值,Nginx 引入了 accept_mutex 参数,如果启用该参数则表示多个 worker 进程间顺序接收新连接,关闭的话则所有 worker 进程会同时被唤醒,但是只有一个 worker 进程能接收处理该连接。在 nginx 1.11.3 版本以前,该参数是默认启用的。但是这种处理方式在处理并发较多时性能特别差。在 Linux 内核 3.9+ 以上的版本中,可以使用 reuseport,需要注意的是,在使用 reuseport 时,请设置accept_mutex off;
1
Syntax: listen address[:port] [reuseport]
worker_connections
- 设置单个 worker 进程允许同时打开的连接数
我们可以通过配置worker_connections
来修改单个 worker 进程可以同时打开的连接数,默认是 512,需要调大该值,但是不能超过worker_rlimit_nofile
1 | Syntax: worker_connections number; |
worker_rlimit_nofile
设置所有 worker 进程允许同时打开的文件数
一般 worker_rlimit_nofile 表示所有 worker 进程可以打开的文件句柄数,该值默认值未设置,将会收到系统级,用户级限制(注意,Nginx worker 进程的默认用户是 nginx)1
2
3Syntax: worker_rlimit_nofile number;
Default: -
Context: main
use epoll
设置配置使用 epoll 时间模型
在 RHEL/CentOS 系统中,Nginx 编译后默认就是使用 epoll 模型,基本语法如下1
2
3Syntax: use method;
Default: -
Context: events
HTTP 配置段优化
我们这里简单介绍一些关于 http 配置段中比较通用的优化参数,同样的,在 HTTP 配置段中的优化我们也从 文件IO
,网络IO
以及 网络连接
等三个核心层面进行介绍。
文件IO优化方向
优化读取速度之
sendfile
,基本语法如下1
2
3Syntax: sendfile on | off;
Default: sendfile off;
Context: http, server, location, if in location主要注意的是,这个参数其实并不是适用于任何场景。Nginx 中打开 sendfile 配置选项即可享受 sendfile 系统调用所带来的优化,但仍需注意该优化仅针对静态资源处理有效,对于反向代理并不起作用,这是因为 sendfile 中数据源句柄只能是文件句柄,而返乡代理的双端都是 socket 句柄,导致不能使用 sendfile。
由于 sendfile 系统调用导致数据不经过 user space,因此与 Nginx 中的 output-filter 存在冲突,比如在同一个上下文中同时打开 gzip 与 sendfile 会导致 sendfile 失效。优化读写速度之
direct I/O
,我们知道一般数据 I/O 时,一定会经过内核缓冲区,应用缓冲区,这是因为缓冲区的速度往往较 IO设备高很多,可以极大程度提高数据的读写效率。在 Nginx 中也是支持 Direct I/O 模式,相关的参数为directio
,应用的常见主要是读取大文件时。基本语法如下
1
2
3Syntax: directio size | off;
Default: directio off;
Context: http, server, locationDirect I/O 的特点就是数据传输不经过操作系统内核缓冲区。
优化读写速度之异步 I/O,Linux 异步 I/O 是 Linux 2.6.22 中的一个标准特性,CPU 处理其他任务和 I/O 操作可以重叠执行,在 Nginx 中启用异步 I/O 的参数为
aio
。
其基本语法如下1
2
3Syntax: aio on | off | threads[=pool];
Default: aio off;
Context: Http, server, location总结:
在 Nginx 中我们会把 directio 与 aio 一起使用,以免造成数据读取阻塞
在 Nginx 中我们同样会将 sendfile 与 aio 一起使用,aio 会为 sendfile 提前预加载数据
在 nginx 中使用 directio 时会自动禁用 sendfile。示例1:
1
2
3
4
5location /video/ {
sendfile on;
aio on;
directio 8m
}以上配置表示,当文件大小超过 8m 时,启动 aio 与 directio,此时 sendfile 会自动禁用;
当文件小于 8m 时,aio 与 sendfile 一起使用,此时 directio 不生效;