参考:https://www.nginx.com/blog/tuning-nginx/

nginx一般用作高性能反向代理。在http七层代理的情况下,它首先接收客户端请求,经过处理后再请求upstream server,因此nginx服务器上的连接数是客户端请求数的2倍,在高并发情况下,建立的连接数非常多,因此系统相关资源如打开文件数、tcp连接、可分配端口等很可能称为其瓶颈。可以从系统内核参数及nginx自身做一些优化。下面提到的相关参数按需调整

相关内核参数

tcp连接队列调整

在内核中,为tcp连接维护了两个队列。一个是已经建立了连接的accept队列,这时候连接三次握手已经完毕,处于 established 状态;一个是还没有完全建立连接的队列,这个时候三次握手还没完成,处于 syn_rcvd 的状态。过程如下:

  1. 当server接收到client的 SYN 报⽂时,会将其加⼊到内核的SYN半连接队列
  2. 接着发送SYN+ACK给client,等待client回应ACK报⽂,client收到此包后connect()调用返回,进入establish状态
  3. server接收到ACK报⽂后,把此连接从SYN队列移除并放⼊到Accept队列
  4. 此时server端应⽤调⽤accpet() socket接⼝从Accept 队列取出连接用于数据通信

如何查看这两个队列情况?

全连接查看:ss -ant

半连接查看:ss -ant | grep -i syn_recv | wc -l

  • LISTEN:
    • Recv-Q 表示当前socket完成三次握手等待用户进程(此处即nginx)accept的连接个数,即全连接队列使用量。一般我们常常看到此值为0,没有堆积
    • Send-Q 表示当前socket全连接队列最大长度
  • 非LISTEN:
    • Recv-Q 表示receive queue中已收到未读取的字节大小
    • Send-Q 表示send queue中已发送未收到确认的字节大小

队列满了的情况

  1. 半连接满

    此时会发生syn包被丢弃的现象,因而客户端会重试,性能下降。可能是syn攻击导致

1
2
$ netstat -s | grep -i syn
    612 SYNs to LISTEN sockets dropped
  1. 全连接满

    并发量大,server端accept()调用过慢时出现,可能由于服务器负载、应用程序本身等问题导致。 满了后新的连接直接丢弃或者回复reset包,此行为由内核参数tcp_abort_on_overflow控制(为0直接丢弃,为1回reset包),且内核socket统计信息里面listen overflowed数加1。

1
2
$ netstat -s | grep -i overflow
    612 times the listen queue of a socket overflowed

调优上可以适当增大全连接和半连接队列

半连接增大:tcp_max_syn_backlog && net.core.somaxconn && backlog

可参考小林coding相关文章

全连接增大:min(backlog,/proc/sys/net/core/somaxconn) ,backlog是调用listen函数传入的参数,参见https://man7.org

端口范围调整

net.ipv4.ip_local_port_range:必须大于ip_unprivileged_port_start(默认1024),默认值32768-60999

timewait优化

  • net.ipv4.tcp_tw_reuse=1:重用timewait连接。只能用于连接发起方,开启后客户端调用connect的时候,内核找一个time_wait超过1s的连接给新的连接复用,前提是开启tcp对时间戳tcp_timestamps的支持
  • net.ipv4.tcp_max_tw_buckets:最大timewait状态的套接字数,如果超过这个值,timewait套接字立即被销毁并打印警告日志,治标不治本,一样参考内核文档

文件句柄相关

  • sys.fs.file-max:内核可分配的最大文件句柄数
  • nr_open:单个进程能打开的最大文件描述符,默认1024*1024 (1048576),实际限制依赖于ulimit,在/etc/security/limits.conf设置

查看当前进程最大打开文件描述符限制:

1
2
3
4
# 主进程
$ cat /proc/$(cat /var/run/nginx.pid)/limits | grep "open files"
# 子进程
$ ps --ppid $(cat /var/run/nginx.pid) -o %p | sed '1d'| xargs -I{} cat /proc/{}/limits | grep "open files"

其他

net.core.netdev_max_backlog:当接口接收数据包的速度快于内核处理它们的速度时,在INPUT端排队的最大数据包数,默认1000,可适量调大一些如10000

nginx本身

连接优化

  • keepalive
    • 客户端侧和upstream侧都有的
      • keepalive_timeout timeout [header_timeout]:server端空闲时保持连接打开的超时时间,第二个可选参数配置后会在响应头部添加Keep-Alive: timeout=time
      • keepalive_requests:一个保活连接中最大请求数,默认1000,一般不建议配太大,不利于内存释放,不利于流量均衡调度
      • keepalive_time:一个保活连接的最大存活时间
    • upstream侧独有的
      • keepalive:每个工作进程连接到上游服务器的空闲保活连接的最大连接数,空闲的多了就关闭最近最少使用的连接

upstream侧长连接,可以适当减少tcp连接频繁创建及timewait,但需要做如下配置:

1
2
3
# http1.1版本才支持keepalive和相对路径重定向,此参数默认值是1.0
proxy_http_version 1.1;
proxy_set_header Connection "";
  • worker_processes:nginx 进程数,建议按照cpu 数目来指定,可以进行亲和性绑定
  • worker_connections:单个work进程同时打开的最大连接数
  • http2支持

超时时间

一些timeout的配置

  • client_body_timeout:读取客户端请求体的两个连续读之间的超时时间,超时则返回408状态码
  • client_header_timeout:请求头读取超时,则408
  • send_timeout:响应客户端的两个连续写操作之间的超时时间

核心模块参数

  • epoll:事件处理模型(默认)
  • backlog:accept全连接队列大小(在k8s的nginx-controller ingress实现中不用配置此参数,默认从内核配置读取
  • tcp_nodelay :默认为on,即关闭Nagle算法,一辆卡车装一块煤炭也行的意思。早些年优化网络用途
  • tcp_nopush:默认为off,也是tcp socket选项,需要先开启sendfile。数据包会累计到一定大小后才发送
  • sendfile:默认off,利用DMA引擎将文件拷贝到内核缓冲区,之后数据被拷贝到内核socket缓冲区中,两文件描述符之间直接传递数据,不经过内核空间,即零拷贝。

压缩和缓存策略

  • 压缩:gzip
  • 缓存
    • open_file_cache
    • open_file_cache_vaild
    • CDN
  • 一些buffer的配置
    • client_body_buffer_size:request body大于此buffer的话,部分或全部内容会写入临时文件。默认是两个内存页的大小
    • client_header_buffer_size:客户端请求的Header头缓冲区大小,默认1k,如果这里放不下请求行或请求头字段,则放到由下面large_client_header_buffers指令配置的更大缓冲区
    • large_client_header_buffers 4 8k:大请求头缓存大小配置参数,如果请求行大小超出了这里的配置,那么返回414状态码;如果一个请求头字段就超过了一个buffer大小,那么直接返回400
    • client_max_body_size:设置客户端请求体最大值
    • access_log:日志的轮询、缓冲、过滤等
  • 给客户端响应的缓存策略

安全

  • server_token off:响应头或者错误页不显示nginx版本
  • nginx限流,简单DDoS防御
    • limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
    • limit the number of connections per single IP
    • limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s; # limit the number of requests for a given session
    • ngx_http_upstream_module

监控

ngx_http_status_module模块

参考:https://gist.github.com/denji/8359866