对于类似ligthy的事件(event)驱动的server,某个阻塞操作会大大降低程序的效率。文件读取操作通常是最容易堵塞的地方;而异步IO(AIO)貌似是解决文件堵塞的灵丹妙药。
大概三个月前,花了点时间在lighttpd 1.4.18上实现了AIO的支持,也是了结一个心病。由于原来的LINUX AIO计划早已停止,我拿POSIX AIO for LINUX 做实验平台,补丁也移植到比较新的2.6.23上。
拿ab测试,发现AIO的大文件吞吐性能很差,不到linux sendfile的一半,系统负载却大很多;让人感觉不可思议,怎么会这样呢?印象中lighty 1.5的aio测试效果还不错的。仔细拜读lighty 1.5的aio 代码,才恍然大悟:
1) AIO不支持sendfile。纯AIO实现 需要把文件内容读到内存中,然后再写到网络层上。为了不占用大量内存,一般是按1M或几M大小依次读取文件内容。AIO可以在读取完毕后通知服务程序,避免阻塞;但内容读取到用户内存中再写到网络层的效率很低
2) Lighty 1.5为解决上面的问题采取了两个办法,第一是多线程;第二是不到万不得已的情况不用AIO,大部分是用mmap/sendfile的。
再拜读Posix AIO规范说明,发现AIO原本意图是解决实时系统中文件操作堵塞的问题。实时系统中文件一般不大,文件操作也不频繁,AIO就比较合适。而在web服务中,适合AIO的前提条件并不存在,因此效果不佳也正常。POSIX AIO定义的操作也不多
最后写三点感想:
1) 完成某个工作的,可以用到的工具(开源项目)很多,选择时要对备选的工具多考察,选择最适合的,而不是看起来最美的
2) 功能不是越多越好,保持代码简洁、系统框架简洁很重要。比如我个人认为ligthy 1.5中引入glib库是个败笔
3) sendfile 很好。对它的多年优化工作可不是白费的
Jan 刚刚发布了LINUX AIO支持。从他公布的测试结果,性能提升是非常非常显著的。大家去lighttpd's life看看,代码在trunk。
作者自己改进就是快啊,熟悉代码,是其它人不可比拟的优势,感慨一把。我也是考虑写个linux-aio-sendfile.c network_backend 实现,呵呵
AIO支持代码还有bug,不适宜用在重要服务上。
keep alive通俗地讲,就是所谓的常连接。常连接节省了TCP连接过程的开销,有一定的优势。现有操作系统越来越先进,建立连接的开销越来越小,比如linux 2.6的epoll,freebsd的kqueue,可以让程序不fork进程或开线程就能同时服务N多连接。与此同时,客户端比如IE,Firefox却可以同时开多个线程取内容,如果开了Keep alive,反而不好。因此一般情况下不需Keep Alive
两台squid,做同样内容的缓存,CPU利用率却差别很大。
1台2Xeon 3G,4G内存,系统RHAS 3.6,Squid CPU使用量如下,TOP显示大部分时间在iowait
Resource usage for squid:
UP Time: 83842.743 seconds
CPU Time: 38569.170 seconds
CPU Usage: 46.00%
最新的Linux 2.6 Kernel是2.6.14.3,更新日期 2005-11-24 22:15
最新的Linux 2.4 Kernel是2.4.32,更新日期 2005-11-16 19:13
最新的Linux 2.2 Kernel是2.2.26,更新日期 2004-02-25 00:28