Nginx - 系统层面的性能优化

发布时间 2023-08-08 14:59:27作者: rachel_aoao

性能优化方法论

  • 增大CPU利用率
  • 增大内存利用率
  • 增大磁盘IO的利用率
  • 增大网络带宽的利用率

增大Nginx使用CPU的有效时长

  • 使用全部的CPU资源
    • worker进程数量应该大于等于(最好等于)CPU核数
  • Nginx进程不做无用功浪费CPU资源
    • worker进程数量等于CPU核数,甚至绑定,减少上下文切换(vmstat查看cs, pidstat -w查看cswch/s, 增大进程优先级,Nice静态优先级-20:19,调小;动态优先级0:139)。
    • 避免调用一些三方模块的阻塞api (速度不一致引发阻塞,如CPU和磁盘;业务场景产生的阻塞,如同步读网络报文)
  • 不被其他进程争抢资源
    • 提升优先级占用CPU更长的时间
    • 减少操作系统上耗资源的非Nginx进程

Numa架构

随着CPU核数越来越多,就一个内存总线,并发访问会导致冲突,访问内存受限。
image

控制TCP参数

TCP连接的三次握手和四次挥手:
image

传输层面(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]

服务器端处理三次握手
image

如何应对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
    image

一切皆文件

操作系统可使用的最大句柄数:fs.file-max
限制用户 /etc/security/limits.conf
限制进程 worker_rlimit_nofile number [main]

image

net.ipv4.tcp_fastopen 系统开启TFO功能 0-关闭,1-客户端使用,2-服务端使用 3-都使用
listen address[:port][fastopen=number] fastopen=number防止带数据的SYN攻击,指定fastopen连接队列最大长度

滑动窗口与缓冲区

滑动窗口用于限制连接的网速,解决报文乱序和可靠传输问题。Nginx中的limit_rate等限速指令皆依赖它实现。由操作系统内核实现,client和server端都有各自的发送窗口和接收窗口。
image

发送TCP消息

image

应用调用系统调用send进行发送 -> 待发送字符流从用户态到内核态的拷贝 -> 操作系统将TCP发送队列里的内容进行发送

MSS: 每一个TCP报文的最大值

接收TCP消息

image

网卡接收报文到内核的receive队列 -> 操作系统将失序报文排好放进receive队列 -> Nginx应用调用系统调用recv接收阻塞socket -> 报文内核态到用户态的拷贝 -> 检查拷贝字节数大于最低接收阈值&backlog队列为空,recv方法返回

Nagle算法

避免一个连接上同时存在大量小报文,合并多个小报文一起发送,提高带宽利用率。

  • 吞吐量优先:启用Nagle算法 tcp_nodelay off
  • 低时延优先:禁用Nagle算法 tcp_nodelay on
    仅对HTTP keep alive 连接生效
    image

流量控制

  • 拥塞窗口 发送方主动限制流量
  • 通告窗口 接收方限制流量
  • 实际流量 拥塞窗口与通告窗口的最小值

拥塞处理

  • 慢启动
    • 指数扩展拥塞窗口 (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零拷贝
    image

  • 内存盘,SSD盘

减少写

  • AIO
  • 增大error_log级别,error_log输出内存(error_log memory:32m debug 环形覆盖),关闭/压缩 access_log
  • 是否启用proxy buffering
  • syslog替代本地IO,通过网络协议发送到另一台主机上去,而不需要磁盘IO

线程池使用

大文件使用直接IO避免Buffered IO模式下磁盘页缓存中的拷贝消耗(绕开磁盘高速缓存)
image