原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。html
相对于Apache的同步IO
模型,Nginx
因为采用了NIO
的缘故,性能上碾压前者。Nginx
是轻量级的,占用的系统资源更少,自然支持高并发。linux
今天咱们就简单的讨论一下nginx的线程模型
。注意不是进程模型
哦。nginx
nginx的IO模型,你们应该都有所了解。简单而言,就是一个master
进程和多个worker
进程(进程数由配置决定);master
进程负责accept
请求并队列化,最后转发给worker
进程并由其进行请求处理和响应的整个过程。程序员
nginx是以多进程模式运行的。nginx在1.7.11版本提供了多线程特性(multi-threading),不过这个多线程仅用在aio模型中对本地文件
的操做上,出发点就是以非阻塞模式,来提升文件IO的效率和并发能力。缓存
因此这个多线程,并非nginx经过多线程的方式处理proxy request
(这部分是经过epoll模式),而是用来处理本地的一些静态文件。微信
这里涉及到几个基本指令:sendfile
、aio
和directio
,它们均与本地文件的操做有关,接下来咱们分别看看它的意义。网络
这个指令与系统函数sendfile()
具备相同的语义,sendfile的目的就是提升本地文件经过socket发送的效率。官方的博客介绍了如何利用nginx 线程池aio,实现9倍
的性能。多线程
它还有一个比较好记的名称,叫作零拷贝
。那与传统的文件读取而后发送到网络上,有什么区别呢?架构
磁盘、网络驱动器、内存是三种不一样的传输介质,若是从本地读取一个文件并经过socket发送出去,一般状况下是进过以下几个步骤:并发
能够看到,数据的发送过程涉及到屡次copy,这受限于计算机系统的设计问题。
sendfile的主要出发点,就是要减小数据的copy以提升发送效率,sendfile是linux系统级
的调用,socket能够经过DMA
(直接内存访问)方式直接访问文件数据,并经过传输协议发送,减小了2次数据copy(磁盘到内核,内核到工做区)。
sendfile_max_chunk
参数用于限定每次sendfile()调用发送的最大数据尺寸,若是不限制大小的话,将会独占整个worker进程,默认为“无限制”。这也太霸道了。
对于nginx而言,代理静态本地的静态文件资源(一般是小文件)将是很是高效的,建议对一些静态文件好比html、图片等,开启此参数。
location /video {
sendfile on;
sendfile_max_chunk 128k;
aio on;
}
复制代码
这个指令用于开启对O_DIRECT
标记(BSD,linux)的使用,对应directio()
这个系统调用。
此参数是针对大文件
而设定的,sendfile
针对的是小文件。经过directio能够指定限定的尺寸大小,对于超过此size的文件,将会使用directio
(而再也不使用sendfile)。
根据directio的设计初衷,它具有sendfile的基本原理,只是不使用内核cache,而是直接使用DMA,并且使用以后内存cache(页对齐部分)也将被释放。
所以directio一般适用于大文件读取,并且一般读取频率很低。由于对于高频的读取,它并不能提升效率(由于它不会重用cache,而是每次都DMA)。因为存在性能权衡问题,此参数默认为off。
location /video {
sendfile on;
directio 8m;
aio on;
}
复制代码
谈到aio模型,其实语义也基本相同,就是异步文件IO,nginx默认关闭此特性,它须要在高版本的linux平台上才支持(2.6.22+)。
在linux上,directio只能读取基于512字节边界对齐
的blocks,文件结束的那些未对齐的block将使用阻塞模式读取。
一样,若是文件在开头没有对齐,整个文件都将阻塞式读取。这里所谓的对齐,就是文件数据在内存页中的cache状况。
当aio和sendfile都开启时,将会对那些size大于directio设定值的文件使用aio机制:即当小于directio设定值的文件将直接使用sendfile(aio不参与)。
aio,简单而言,就是使用多线程异步模式读取较大的文件,以提升IO效率,可是事实上可能并无任何提升。由于大文件的读取,并不能使用cache、并且自己也是耗时的,即便是多线程,对于request的等待时间也是没法预估的,特别是并发请求较高的时候。可是aio可以提升IO的并发能力,这个是肯定的。
默认状况下,多线程
模式是关闭的,咱们须要经过--with-threads
配置来开启,此特性尽在支持epoll
、kqueue
的平台上兼容。对于线程池的设置,咱们能够经过thread_pool
来声明,并在aio指令中指定。
咱们的配置文件近一步丰富了一些。
thread_pool default_pool threads=16;##main上下文
...
location /video {
sendfile on;
sendfile_max_chunk 128k;
directio 8M;
aio threads=default_pool;
}
复制代码
当线程池中全部的线程都处于busy
状态,那么新的task请求将会加入到等待队列。咱们能够在thread_pool中使用max_queue
参数来指定队列的大小,默认队列大小为65536
,当队列已满后续的请求将会抛出error。
nginx官方宣称使用多线程模式,在aio读取文件场景下,性能有9倍的提高,但我仍是对这个测试具备必定怀疑态度。
多线程 + aio在必定程度的确能够提升文件IO的读取性能,可是对于大文件而言,这彷佛并无想象的那么优秀。这受制于linux平台底层的自己特性,除非nginx本身对文件cache作了额外的操做。
到目前为止,xjjdog仍有如下建议(供参考):
做者简介:小姐姐味道 (xjjdog),一个不容许程序员走弯路的公众号。聚焦基础架构和Linux。十年架构,日百亿流量,与你探讨高并发世界,给你不同的味道。个人我的微信xjjdog0,欢迎添加好友,进一步交流。