从0.8.11版本开始,nginx开始支持Linux native aio,如何在nginx里配置使用这套机制是本文介绍的重点。在下面的示例配置中,几个重要相关选项的具体含义如下:
aio:http://wiki.nginx.org/HttpCoreModule#aio
Syntax: aio on | off | sendfile
Default: off
在linux下aio可以设置为on或off(在freebsd还可以设置为sendfile),但需要2.6.22及以后版本的内核,原因在前面文章已经提到过,nginx使用的eventfd有这个要求。
directio:http://wiki.nginx.org/HttpCoreModule#directio
Syntax: directio size | off
Default: off
directio选项可以设置为off或一个大小值,表示当文件大小大于等于这个值时启用directio。
sendfile:http://wiki.nginx.org/NginxHttpCoreModule#sendfile
Syntax: sendfile on | off
Default: off
是否启用sendfile()。
[root@www nginx-1.2.0]# uname -a Linux www.t1.com 2.6.38.8 #2 SMP Wed Nov 2 07:52:53 CST 2011 x86_64 x86_64 x86_64 GNU/Linux [root@www nginx-1.2.0]# cat /etc/issue CentOS Linux release 6.0 (Final) Kernel r on an m [root@www nginx-1.2.0]# ./configure --with-file-aio [root@www nginx-1.2.0]# make [root@www nginx-1.2.0]# make install [root@www nginx-1.2.0]# cat /usr/local/nginx/conf/nginx.conf ... http { ... server { ... location / { aio on; directio 512k; sendfile on; output_buffers 1 128k; ...
要使aio生效需把directio设置为打开状况,并且如果aio生效,那么将自动不使用sendfile(),这在linux下这是显然的,要么利用aio读到缓存区,要么利用sendfile()直接发送出去,两者不可兼用,而对于freebsd系统下aio和sendfile并用的情况,我并不了解,所以也就不妄加评论;
可以看到directio是针对每个请求的文件大小而决定是否开启directio的,因此对于上面的整个示例配置,也就会针对每个请求的不同而不同:
如果某处请求的文件大于等于512k,那么将启用directio,从而aio生效,进而sendfile不生效;
如果某处请求的文件小于512k,那么将禁用directio,从而aio也就不生效,转而使用sendfile(),即sendfile生效;
这种设计貌似刚好把linux下aio和sendfile两种机制的优点很好的结合起来使用。对于大文件采用aio,节省cpu,而对于小文件,采用sendfile,减少拷贝;并且对于大文件aio采用directio,避免挤占文件系统缓存,让文件系统缓存更多的小文件。
从理论上来看,这种配置比较适合系统内存有限、小文件请求比较多、间隔有几个大文件请求的Web环境;如果内存足够大,那么应该充分利用文件系统缓存,而directio使得aio无法使用缓存是衡量最终是否需要采用aio的一个需要仔细考虑的因素;网上有人总结说nginx+aio很好,也有人说其很差,其实根据特定的系统环境和应用场景来做配置调节,才能达到性能的最优,nginx提供的AIO只是一套工具,没有固定的好与差之分,就看你能否恰当的用好它,但据nginx官网论坛来看,在linux系统的大部分场景下,目前因使用aio功能附加的限制而带来的实际效果估计并不太理想:http://forum.nginx.org/read.php?2,113524,113587#msg-113587
nginx supports file AIO only in 0.8.11+, but the file AIO is functional
on FreeBSD only. On Linux AIO is supported by nginx only on kerenl
2.6.22+ (although, CentOS 5.5 has backported the required AIO features).
Anyway, on Linux AIO works only if file offset and size are aligned
to a disk block size (usually 512 bytes) and this data can not be cached
in OS VM cache (Linux AIO requires DIRECTIO that bypass OS VM cache).
I believe a cause of so strange AIO implementaion is that AIO in Linux
was developed mainly for databases by Oracle and IBM.
先不管nginx+AIO的效果到底如何,下面来看其源码实现。不管是aio,还是sendfile,都处于数据发送阶段,而这部分逻辑落在函数ngx_output_chain()内。作为是aio还是sendfile的分支点,就看函数ngx_output_chain_as_is():
#define ngx_buf_special(b) ((b->flush || b->last_buf || b->sync) && !ngx_buf_in_memory(b) && !b->in_file) #define ngx_buf_in_memory(b) (b->temporary || b->memory || b->mmap) static ngx_inline ngx_int_t ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf) { ngx_uint_t sendfile; if (ngx_buf_special(buf)) { return 1; } if (buf->in_file && buf->file->directio) { return 0; } sendfile = ctx->sendfile; #if (NGX_SENDFILE_LIMIT) if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) { sendfile = 0; } #endif if (!sendfile) { if (!ngx_buf_in_memory(buf)) { return 0; } buf->in_file = 0; } if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) { return 0; } if (ctx->need_in_temp && (buf->memory || buf->mmap)) { return 0; } return 1; }
该函数返回1,则表示数据可以直接发送出去;如果返回0,则表示数据还在磁盘文件内,需要利用directio读取或明确要求不能使用sendfile直接发送、明确要求读到内存缓存等情况;注意:buf->file->directio由of.is_directio与配置项directio最终关联起来,具体过程不多详说。
函数ngx_output_chain_as_is()返回1的情况就不管了,原本该干嘛干嘛,走ngx_http_write_filter() -> ngx_linux_sendfile_chain()流程到最后,内存数据通过writev()发送,磁盘文件内数据通过sendfile()发送。
而返回0的情况表示要读取数据到缓存区,在我们这里的讨论上下文,也就是利用aio进行读取,也就是流程:
ngx_output_chain_copy_buf() -> ngx_file_aio_read()
从而,真正走入到nginx的AIO实现里来。当然,nginx已经把AIO的环境都设置好了,到这里只是使用AIO进行异步读取文件。
参考:
http://lightech.tuita.com/blogpost/23088122
转载请注明:爱开源 » nginx对Linux native AIO机制的应用 一