在Nginx配置中,指令很多,但与nginx性能主要有关的并不是很多,在使用中,公司的ops都会给出他们的标配,往往我们除非有特殊的性能需求,才会考虑nginx的默认配置是否对性能有影响,且nginx自身在设计时,对性能的优化已考虑很多。
OS考量
- 事件模型:nginx的事件模型应与选择的操作系统相匹配, Linux 2.6+有效的模型是使用epoll。
- 文件描述符:因为与客户端建立对应的连接,代表需要使用某文件句柄描述符与之通信,可以使用命令ulimit -a查看系统设定值,建议特别是高并发时,尽量提高内核限制,然后使用nginx指令worker_rlimit_nofile 改变nginx worker 进程可打开的最大文件描述符限制。
Nginx 自身
- worker_processes : 定义nginx worker进程的数量,系统默认为1,通常设定这个值时,应该与cpu核的数目一致,即使比CPU核数目多出,但不会带来很明显的效益,在测试时发现,增加这个数字,会立即提高CPU使用率,但更多是因为进程的调度造成,并没有有效利用CPU。此外,还应考虑读取文件的大小以及负载模式。如果业务逻辑有大量的阻塞IO操作,可以适当提高这个值。
- worker_connections: 某worker进程在某时刻可维持的连接数,wiki指出nginx的最大客户端连接数为worker_connections*worker_processes值,很多人给出的建议是尽量调大这个值,当业务高并发连接时。理想设置是(umilit -n) /num of worker_processes ,但是考虑到HTTP请求的特性,短暂而高突发且不同请求的时长不一致,可以都设置为最大值umlit --n ,实际中使用时,可根据自己的情况适当调整,毕竟描述符的使用也是需要系统资源的。
- worker_cpu_affinity : 绑定worker进程到某特殊的CPU上,仅允许FressBSD与Linux使用,参考别人的建议以及实际压测发现,并没有带来变化,毕竟操作系统层次的调度算法,可参考的运行因素很多,会比实际手工分配有机会在负载均衡上做得更出色,不建议去考虑此指令,除非你的CPU负载在实际查看中发现存在问题,但也是推荐修改调度算法。
- sendfile:在于传输TCP帧时,可以将数据直接从内存写到网络芯片的缓存中,避免数据在内核态到用户态之间反复上下文切换,以及其他内存访问操作如TLB,虚拟内存地址到实地址的映射等,降低CPU消耗等,一般建议开启。在网上查阅相关配置时,指出当读取文件过大(>4M)时,建议关闭这个选项,调整其他指令的值,特别是输出缓冲区,使用异步IO(Linux许多版本还不支持)等,但是至于为什么关闭sendfile,发现都没有很好的解释,看sendfile的官方指出在初始设计时,并不是针对大文件的偏移操作,但是2.4版本已经添加sendfile64,且已经有glibc的sendfile透明封装好,因而对于这个建议还是很困惑。有人给出的解释是“以平衡磁盘与网络I/O处理速度,降低系统的uptime”,但是觉得还是模糊,毕竟磁盘有内核缓存,网络芯片也有自身的缓存。偶然间看到nginx邮件列表中的内容,指出sendfile在nginx中传输文件的大小是受到限制的。
- TCP_NODELAY与TCP_NOPUSH: 与socket选项有关,决定着操作系统如何处理网络缓存以及何时将数据发送给终端用户。TCP_NOPUSH, 避免传输数据小,网络带宽无法有效利用,将多个数据组包一起发送,也就是Nagle's 算法, 而wiki中指出仅能与sendfile一起使用,可以方便在调用sendfile之前准备头部信息,或优化网络吞吐率,毕竟减少传输过程的网络数据包。但是HTTP的数据更多是偏向流处理,而不是类似telnet,等待用户输入数据,且此算法会有最高达200ms的延迟上限; TCP_NODELAY则是禁用Nagle's 算法,wiki指出启用仅被包含在keep_alive 连接里。Linux 内核2.5.17 之后已经允许两个指令可以组合在一起使用。默认是TCP_NODELAY 开启,TCP_NOPUSH 关闭。
- keepalive_timeout: 涉及到HTTP协议的特性,允许客户端与服务端建立的连接持续到所设置的时间为止,允许客户端发起多个请求,而不再进行多次握手协议,节省socket建立的时间,与NGINX自身的优化倒是无关,更多体现在客户端加载页面多个资源的响应速度,影响用户的体验。根据业务适当设置,以免恶意的客户端占用,造成性能的影响。
减少IO
Nginx在CPU与内存使用上效率都很高,普遍不是性能瓶颈,但是磁盘因为自身的特点,造成读写都很慢,因而应尽量减少nginx的磁盘读写。
- open_file_cache:缓存打开的文件描述符、文件大小以及修改时间,还有存在的目录信息等其他信息,至于相关配置其他指令可参考nginx wiki.
- access_log :记录正常的访问请求,可用于统计,安全检查等,但是却频繁的读写文件,且buffer不工作,对于任何的日志项打开文件,写完后就迅速关闭文件。如果没有特殊需求,建议关闭。否则尝试改变请求访问日志记录的保存方式等,当然容忍其开启也可以。
- error _log: 与access_log一样,但是因为出错的请求并不是很多,因而开启影响不大!
在nginx中也需要调整buffer大小,设置过小,某些请求或者后端返回的数据就会存储在临时文件中,造成磁盘的读写。
- client_body_buffer_size: 请求体的缓存大小,如果请求体的数据大于设置的这个值将会被写入到临时文件中,因而在处理post请求的数据时,需要注意这个选项。
- fastcgi_buffers: 用于缓存后端fastcgi进程返回的数据,当返回的数据过大就会写入临时文件,因而运行PHP程序时,可以适当调整此值,默认是32KB/64KB,取决于操作系统的页设置大小。
- output_buffers :这条指令虽然nginx有关的配置中出现,但是在nginx指令的索引中没有找到,而且查阅不少配置,发现更多是读取大文件时,关闭sendfile,调整此值,提高服务器的吞吐率。
- gzip:并不属于减少磁盘IO,但是可以有效减少网络传输数据的大小,毕竟现实中数据包传输时间受带宽影响的时间要明显高于主机之间的距离造成的传送时间,因而压缩数据包,可以有效利用带宽,降低带宽造成的延迟,默认压缩级别是7,但是压缩级别不要设置太高,毕竟要消耗CPU时间,带来的压 缩效果并不很明显。
可能在配置中看到的其他某些选项
- client_header_buffer_size与large_client_header_buffers: 缓存请求头,当请求头的大小超出这两个指令指定的缓存限制时,就会返回相关错误码,因而采用默认配置就好,并不影响nginx的性能。
- client_body_timeout与client_header_timeout:更多是设置服务端在读取客户端发送的请求时设置的超时时间,避免客户端恶意或者网络状况不佳造成连接长期占用,影响服务端的可处理的能力。需要注意的是,client_body_timeout是设置在两个连续的读取操作之间的超时,并不是传输整个请求体的超时时间设置。当相关的数据包在超时时间内没有读取到时,都会返回408,默认设置值是60s。
- send_lowat与send_timeout: 侧重与客户端的交互,send_lowats 当设置为非零值时,nginx就会试图减少对客户端socket的发送操作,默认设置为零;而send_timeout是设置服务端传送回应包时的超时时间,针对两个连续的写操作,而不是整个回应过程的超时设置,默认设置60s.
在Nginx中还有很多其他指令,可能会影响到浏览器的行为,如某些模块的指令可有效利用浏览器的本地缓存,能够影响到用户浏览网页的体验效果。
在实际使用中,如果没有特殊需求,可直接采用使用nginx源码安装后给出的默认配置,调整worker_processes以及worker_connections,其他参数,针对需要,决定是否开启。
参考文献
- http://nginx.org/en/docs/http/ngx_http_core_module.html#directives
- http://linux.die.net/man/2/sendfile
- http://www.techrepublic.com/article/use-sendfile-to-optimize-data-transfer/ (对sendfile如何起作用很不错的分析)
- http://blog.martinfjordvald.com/2011/04/optimizing-nginx-for-high-traffic-loads
- http://www.lifelinux.com/how-to-optimize-nginx-for-maximum-performance/
- http://baus.net/on-tcp_cork/ (tcp_nopush设置时,对业务的弊与利)
- https://calomel.org/nginx.html (nginx配置文件样例还有些调优的内容)
- http://www.phoenixvps.com/guides/2013/04/nginx-configuration-examples (nginx 配置文件样例)