nginx的线程池和I/O机制
启用对线程的支持
./configure --prefix=/home/jackie/software/nginx \
--with-openssl=/home/jackie/Downloads/nginx/openssl-1.0.2g \
--with-pcre=/home/jackie/Downloads/nginx/pcre-8.38 \
--with-zlib=/home/jackie/Downloads/nginx/zlib-1.2.8 \
--with-http_ssl_module \
--with-threads \
--with-debug
make -j4
make install
配置说明
定义线程池
thread_pool mypool threads=32 max_queue=65536;
thread_pool
Syntax: thread_pool name threads=number [max_queue=number];
Default:
thread_pool default threads=32 max_queue=65536;
Context: main
nginx提供了一个名为default的默认线程池,线程数量为32,队列中操作数量的上限为65536.
在AIO中使用线程池
location ~ /downloads/ {
aio threads=mypool;
directio 8k;
output_buffers 1 128k;
sendfile on;
}
aio
Syntax: aio on | off | threads[=pool];
Default:
aio off;
Context: http, server, location
使用使用指令aio threads,则使用默认的线程池default。
directio
Syntax: directio size | off;
Default:
directio off;
Context: http, server, location
directio_alignment
Syntax: directio_alignment size;
Default:
directio_alignment 512;
Context: http, server, location
sendfile
Syntax: sendfile on | off;
Default:
sendfile off;
Context: http, server, location, if in location
sendfile_max_chunk
Syntax: sendfile_max_chunk size;
Default:
sendfile_max_chunk 0;
Context: http, server, location
延伸阅读资料
nginx的aio和sendfile
启用aio时会自动启用directio,小于directio定义的大小的文件则采用sendfile进行发送,超过或等于directio定义的大小的文件,将采用aio线程池进行发送,也就是说aio和directio适合大文件下载。因为大文件不适合进入操作系统的buffers/cache,这样会浪费内存,而且Linux AIO(异步磁盘IO)也要求使用directio的形式。
sendfile_max_chunk可以减少阻塞调用sendfile()所花费的最长时间。因为Nginx不会尝试一次将整个文件发送出去,而是每次发送大小为256KB的块数据。
注意,Nginx从1.7.11开始为AIO引入了线程池支持,能够使用多线程读取和发送文件,以免工人进程被阻塞。要启用多线程支持,configure时需要显式加入–with-threads选项。
nginx对Linux native AIO机制的应用(配置篇)
要使aio生效需把directio设置为打开状况,并且如果aio生效,那么将自动不使用sendfile(),这在linux下这是显然的,要么利用aio读到缓存区,要么利用sendfile()直接发送出去,两者不可兼用。
这种设计貌似刚好把linux下aio和sendfile两种机制的优点很好的结合起来使用。对于大文件采用aio,节省cpu,而对于小文件,采用sendfile,减少拷贝;并且对于大文件aio采用directio,避免挤占文件系统缓存,让文件系统缓存更多的小文件。
从理论上来看,这种配置比较适合系统内存有限、小文件请求比较多、间隔有几个大文件请求的Web环境;如果内存足够大,那么应该充分利用文件系统缓存,而directio使得aio无法使用缓存是衡量最终是否需要采用aio的一个需要仔细考虑的因素
关于aio
关于sendfile
高性能Tomcat:漫谈行走在sendfile之上的Tomcat
Tomcat中的NIO模式和APR模式对静态文件处理的高效根源在于地从对不同的系统调用,和普通的文件传输不同的是NIO和APR方式都使用的操作系统的sendfile系统调用来对文件进行IO,而普通的方式是使用了read和write系统调用。那么这两种类型有什么不同呢,这是本文的关键点:
使用read和write方式的时候,将文件输出到网络的流程是这样的:
1,read操作先将线程从用户态切换到内核态,将文件从磁盘读到内核缓冲器。
2,read将文件从内核缓冲区读到用户地址空间,同时线程从内核态切换到用户态。
3,read返回。
4,对文件进行处理
5,write将线程从用户态切换到内核态,将文件写到操作系统内核网络部分的缓冲区。
6,write将线程切换到用户态并返回。
上面的的过程涉及到四次操作系统内核态与用户态的切换,代价是昂贵的。由于事实上我们并不需要对静态文件进行处理这个步骤,为什么要绕一个圈子呢从内核copy到用户态又copy到内核态呢,因此sendfile来了。
我们再来看看sendfile的处理流程:
1,将文件读到操作系统的内核缓冲区。
2,将文件copy到操作系统跟网络相关的内核缓冲区。
上面不会涉及到内核态到用户态以及用户态到内核态的切换,sendfile是linux2+version提供的系统调用,而且在linux2.4+version版本之后提供能zero-copy特性,上面这些说明了sendfile为我们的程序提高了问静态文件处理的能力和性能。
关于nginx的threadpool
NGINX引入线程池 性能提升9倍
实际上,最幸运的情况是,读取和发送文件操作不去处理缓慢的硬盘驱动器。如果我们有足够多的内存来存储数据集,那么操作系统将会足够聪明地在被称作“页面缓存”的地方,缓存频繁使用的文件。
“页面缓存”的效果很好,可以让NGINX在几乎所有常见的用例中展示优异的性能。从页面缓存中读取比较快,没有人会说这种操作是“阻塞”。而另一方面,卸载任务到一个线程池是有一定开销的。
因此,如果内存有合理的大小并且待处理的数据集不是很大的话,那么无需使用线程池,NGINX已经工作在最优化的方式下。
卸载读操作到线程池是一种适用于非常特殊任务的技术。只有当经常请求的内容的大小,不适合操作系统的虚拟机缓存时,这种技术才是最有用的。至于可能适用的场景,比如,基于NGINX的高负载流媒体服务器。这正是我们已经模拟的基准测试的场景。
我们如果可以改进卸载读操作到线程池,将会非常有意义。我们只需要知道所需的文件数据是否在内存中,只有不在内存中时,读操作才应该卸载到一个单独的线程中。
有意思的个人站点
若非注明,均为原创,欢迎转载,转载请注明来源:nginx的线程池和I/O机制