nginx一般用作高性能反向代理。在http七层代理的情况下,它首先接收客户端请求,经过处理后再请求upstream server,因此nginx服务器上的连接数是客户端请求数的2倍,在高并发情况下,建立的连接数非常多,因此系统相关资源如打开文件数、tcp连接、可分配端口等很可能称为其瓶颈。可以从系统内核参数及nginx自身做一些优化。下面提到的相关参数按需调整。
相关内核参数
tcp连接队列调整
在内核中,为tcp连接维护了两个队列。一个是已经建立了连接的accept队列,这时候连接三次握手已经完毕,处于 established 状态;一个是还没有完全建立连接的队列,这个时候三次握手还没完成,处于 syn_rcvd 的状态。过程如下:
- 当server接收到client的 SYN 报⽂时,会将其加⼊到内核的SYN半连接队列
- 接着发送SYN+ACK给client,等待client回应ACK报⽂,client收到此包后connect()调用返回,进入establish状态
- server接收到ACK报⽂后,把此连接从SYN队列移除并放⼊到Accept队列
- 此时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中已发送未收到确认的字节大小
队列满了的情况
半连接满
此时会发生syn包被丢弃的现象,因而客户端会重试,性能下降。可能是syn攻击导致
|
|
全连接满
并发量大,server端accept()调用过慢时出现,可能由于服务器负载、应用程序本身等问题导致。 满了后新的连接直接丢弃或者回复reset包,此行为由内核参数tcp_abort_on_overflow控制(为0直接丢弃,为1回reset包),且内核socket统计信息里面listen overflowed数加1。
|
|
调优上可以适当增大全连接和半连接队列
半连接增大: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设置
查看当前进程最大打开文件描述符限制:
|
|
其他
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侧都有的
upstream侧长连接,可以适当减少tcp连接频繁创建及timewait,但需要做如下配置:
|
|
- 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