Nginx - 系统层面的性能优化
性能优化方法论
- 增大CPU利用率
- 增大内存利用率
- 增大磁盘IO的利用率
- 增大网络带宽的利用率
增大Nginx使用CPU的有效时长
- 使用全部的CPU资源
- worker进程数量应该大于等于(最好等于)CPU核数
- Nginx进程不做无用功浪费CPU资源
- worker进程数量等于CPU核数,甚至绑定,减少上下文切换(
vmstat
查看cs
,pidstat -w
查看cswch/s
, 增大进程优先级,Nice静态优先级-20:19,调小;动态优先级0:139)。 - 避免调用一些三方模块的阻塞api (速度不一致引发阻塞,如CPU和磁盘;业务场景产生的阻塞,如同步读网络报文)
- worker进程数量等于CPU核数,甚至绑定,减少上下文切换(
- 不被其他进程争抢资源
- 提升优先级占用CPU更长的时间
- 减少操作系统上耗资源的非Nginx进程
Numa架构
随着CPU核数越来越多,就一个内存总线,并发访问会导致冲突,访问内存受限。
控制TCP参数
TCP连接的三次握手和四次挥手:
传输层面(TCP)都是有操作系统内核控制的,Linux提供一些参数来对TCP连接进行控制:
net.ipv4.tcp_syn_retries=6
主动连接时,发送SYN的重试此时net.ipv4.ip_local_port_range=32768 60999
建立连接时本地可用端口范围net.ipv4.tcp_max_syn_backlog
SYN_RCVD状态连接的最大个数, 避免一些攻击(随意捏造IP,端口不断发送TCP连接SYN包)net.ipv4.tcp_synack_retries
被动建立连接时, 发送SYN/ACK的重试次数
nginx应用层面控制超时时间proxy_connect_timeout time
[http,server,location]
服务器端处理三次握手
如何应对SYN攻击
攻击者短时间伪造不同IP地址的SYN报文,快速占满backlog队列,使服务器不能为正常的用户服务。
通过一些参数来控制:
net.core.netdev_max_backlog
接收自网卡,但未被内核协议栈处理的报文队列长度net.ipv4.tcp_max_syn_backlog
SYN_RCVD状态连接的最大个数net.ipv4.tcp_abort_on_overflow
超出处理能力时,对新来的SYN直接回包RST,丢弃连接
tcp_syncookies
一切皆文件
操作系统可使用的最大句柄数:fs.file-max
限制用户 /etc/security/limits.conf
限制进程 worker_rlimit_nofile number
[main]
net.ipv4.tcp_fastopen
系统开启TFO功能 0-关闭,1-客户端使用,2-服务端使用 3-都使用
listen address[:port][fastopen=number]
fastopen=number防止带数据的SYN攻击,指定fastopen连接队列最大长度
滑动窗口与缓冲区
滑动窗口用于限制连接的网速,解决报文乱序和可靠传输问题。Nginx中的limit_rate等限速指令皆依赖它实现。由操作系统内核实现,client和server端都有各自的发送窗口和接收窗口。
发送TCP消息
应用调用系统调用send进行发送 -> 待发送字符流从用户态到内核态的拷贝 -> 操作系统将TCP发送队列里的内容进行发送
MSS: 每一个TCP报文的最大值
接收TCP消息
网卡接收报文到内核的receive队列 -> 操作系统将失序报文排好放进receive队列 -> Nginx应用调用系统调用recv接收阻塞socket -> 报文内核态到用户态的拷贝 -> 检查拷贝字节数大于最低接收阈值&backlog队列为空,recv方法返回
Nagle算法
避免一个连接上同时存在大量小报文,合并多个小报文一起发送,提高带宽利用率。
- 吞吐量优先:启用Nagle算法
tcp_nodelay off
- 低时延优先:禁用Nagle算法
tcp_nodelay on
仅对HTTP keep alive 连接生效
流量控制
- 拥塞窗口 发送方主动限制流量
- 通告窗口 接收方限制流量
- 实际流量 拥塞窗口与通告窗口的最小值
拥塞处理
- 慢启动
- 指数扩展拥塞窗口 (cwnd为拥塞窗口大小)
- 每收到一个ACK cwnd = cwnd + 1
- 每过一个RTT cwnd = cwnd * 2 RTT(Round Trip Time 报文一来一回的时间)
- 拥塞避免 窗口大于threshold
- 线性扩展拥塞窗口
- 每收到1个ACK, cwnd = cwnd + 1/cwnd
- 没过一个RTT, 窗口加1
- 拥塞发生
- 急速降低拥塞窗口
- RTO超时,threshold = cwnd/2, cwnd = 1 RTO(Retransmission Timeout)
- Fast Retransmit 收到3个duplicate ACK, cwnd = cwnd/2, threshold = cwnd
- 快速恢复
- 当Fast Retransmit 出现时, cwnd 调整为threshold + 3 * MSS
TCP层面上的优化
- TIME_WAIT优化
net.ipv4.tcp_tw_reuse=1
开启后,作为客户端时新连接可以使用仍然处于TIME-WAIT状态的端口 - lingering配置指令 及时关闭连接
- 以RST代替正常的四次挥手关闭连接,立刻释放端口,内存等资源
- TLS/SSL优化握手性能
ssl_session_cache off|none|[builtin[:size]][shared:name:size]
builtin, 使用openssl得当session缓存,由于在内存中使用,所以仅当同一客户端的两次连接都命中到同一个worker进程时,session缓存才会生效 - TLS/SSL使用tickets
ssl_session_tickets on|off
, Nginx将会话中的session信息作为tickets加密发送给客户端,当客户端下次发起TLS连接时,带上tickets,由Nginx解密验证后复用会话Session, 会话票证虽然更易在Nginx集群中使用,但破坏了TLS/SSL的安全机制,有安全风险,必须频繁更换tickets秘钥
HTTP长连接
- 减少握手次数
- 减少并发连接数,减少了服务器资源的消耗
- 降低TCP拥塞控制的影响
gzip压缩
通过实时压缩http包体,提升网络传输效率 gzip on|off
减少磁盘IO
优化读
-
SendFile零拷贝
-
内存盘,SSD盘
减少写
- AIO
- 增大error_log级别,error_log输出内存(
error_log memory:32m debug
环形覆盖),关闭/压缩 access_log - 是否启用proxy buffering
- syslog替代本地IO,通过网络协议发送到另一台主机上去,而不需要磁盘IO
线程池使用
大文件使用直接IO避免Buffered IO模式下磁盘页缓存中的拷贝消耗(绕开磁盘高速缓存)