分类:
2012-03-27 14:30:20
原文地址:Apache性能优化 作者:jerryswxs
目前httpd守护进程越来越丰富,而Apache2或许是大家最熟悉,应用范围最广泛的。该篇幅主要探讨一下Apache2与性能相关的配置。我们从简单的配置说起。我们开始吧…
HostnameLookups OffHostnameLookups设置如果一旦启用,服务器会对客户端的hostname进行nslookup查询。这将延迟对用户的响应。我们截取了一段,开启了HostnameLookups选项的进程调用记录。
open("/etc/hosts", O_RDONLY) = 22 fcntl64(22, F_GETFD) = 0 fcntl64(22, F_SETFD, FD_CLOEXEC) = 0 fstat64(22, {st_mode=S_IFREG|0644, st_size=174, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7ff1000 read(22, "# Do not remove the following li"..., 4096) = 174 read(22, "", 4096) = 0 close(22) = 0 munmap(0xb7ff1000, 4096) = 0 socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 22 connect(22, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("202.96.209.5")}, 28) = 0 fcntl64(22, F_GETFL) = 0x2 (flags O_RDWR) fcntl64(22, F_SETFL, O_RDWR|O_NONBLOCK) = 0 gettimeofday({1271519250, 942822}, NULL) = 0 poll([{fd=22, events=POLLOUT}], 1, 0) = 1 ([{fd=22, revents=POLLOUT}]) send(22, "0q\1\0\0\1\0\0\0\0\0\0\00265\0014\003173\00261\7in-addr"..., 42, MSG_NOSIGNAL) = 42 poll([{fd=22, events=POLLIN}], 1, 5000) = 1 ([{fd=22, revents=POLLIN}]) ioctl(22, FIONREAD, [102]) = 0 recvfrom(22,"0q\201\200\0\1\0\1\0\0\0\0\00265\0014\003173\00261\7in-addr"...,1024,0, {sa_family=AF_INET,sin_port=htons(53), sin_addr=inet_addr("202.96.209.5")}, [16]) = 102 close(22)从这份快照可以看出,Apache首先会从/etc/hosts中查找是否有与客户端hostname相同的DNS记录,如果没有找到,则会连接指定的域名服务器进行nslookup操作。
AllowOverride None一般,将AllowOverride设置为AllowOverride None的性能是最优的。如果该值设置All,目录设置允许被.htaccess文件覆盖。那么Apache则会在文件名的每一个组成部分都尝试打 开.htaccess文件。要避免这种情况,可以将AllowOverride设置为None。 下面是一段将AllowOverride all设置的系统调用快照,以此来说明取值all的劣性。
stat64("/opt/virtaul_hosts/perfgeeks.com/wp-content/themes/perfgeeks/style.css", {st_mode=S_IFREG|0755, st_size=7929, ...}) = 0 open("/opt/virtaul_hosts/perfgeeks.com/.htaccess", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory) open("/opt/virtaul_hosts/perfgeeks.com/wp-content/.htaccess", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory) open("/opt/virtaul_hosts/perfgeeks.com/wp-content/themes/.htaccess", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory) open("/opt/virtaul_hosts/perfgeeks.com/wp-content/themes/perfgeeks/.htaccess", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory) open("/opt/virtaul_hosts/perfgeeks.com/wp-content/themes/perfgeeks/style.css/.htaccess", O_RDONLY|O_LARGEFILE) = -1 ENOTDIR (Not a directory) open("/opt/virtaul_hosts/perfgeeks.com/wp-content/themes/perfgeeks/style.css", O_RDONLY|O_LARGEFILE) = 22 mmap2(NULL, 7929, PROT_READ, MAP_SHARED, 22, 0) = 0xb7f85000 munmap(0xb7f85000, 7929) = 0我们可以看到,自stat64()开始,Apache一共进行了5次open()的内核调用,并且都返回-1(表示文件不存在),分别在文件名 /wp-content/themes/perfgeeks/style.css每一个组成部分下面试图打开.htaccess文件。必竟这5次open 内核函数调用不是很必要。如果有条件的读者可将.htacess中的配置合并到相应的Apache的配置文件中,并且把AllowOverride设置为 None,以获得最佳性能。
Options FollowSymLinksOptions的选值很多,除了All之外,我们还比较关心Indexes、FollowSymLinks和 SymLinksIfOwnerMatch。其中FollowSymLinks表示允许在此目录使用符号连接。这是一种什么概念呢,比如/va/ftp /data是你存ftp上传数据的地方,而web目录是/var/www/perfgeeks,假定你想通过http: //访问/var/ftp/data目录的数据,你可以在/var/www/perfgeeks目录下建立一 个符号连接ftp指向/var/ftp/data。而FollowSymLinks就是指明这种操作是允许的,指示Apache不必去检查ftp文件是否 为链接。另外SymLinksIfOwnerMatch则要求和符号连接与其指向的目录或文件属主是同一人(相同的uid)才允许上述操作,即/var /www/perfgeeks/ftp与/var/ftp/data拥有者的uid要求是一样的,这样Apache就必须通过请求系统内核调用 stat()来检查文件名每一个组成部分是否为链接,如果是链接就要去核实是否与链接指向的原文件具有相同的uid。我们推荐设置 FollowSymLinks,而不要设置SymLinksIfOwnerMatch,这样可以获取更高的性能。因为,假定没有设置 FollowSymLinks或者一旦设置了SymLinksIfOwnerMatch,则会额外地调用系统内核函数lstat()来验证目录是否为符号 连接。而且是验证文件的每一个组成部分。下面,我们来看二份快照,分别是设置了FollowSymLinks和SymLinksIfOwnerMatch 的strace记录。
#-----Option FollowSymLinks------------------------ read(11, "GET /test/test.php HTTP/1.1\r\nHos"..., 8000) = 417 gettimeofday({1271731674, 100730}, NULL) = 0 stat64("/var/www/html/test/test.php", {st_mode=S_IFREG|0644, st_size=27, ...}) = 0 setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={60, 0}}, NULL) = 0 #-----Option SymLinksIfOwnerMatch--------------- read(11, "GET /test/test.php HTTP/1.1\r\nHos"..., 8000) = 417 gettimeofday({1271731349, 444196}, NULL) = 0 stat64("/var/www/html/test/test.php", {st_mode=S_IFREG|0644, st_size=27, ...}) = 0 lstat64("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test/test.php", {st_mode=S_IFREG|0644, st_size=27, ...}) = 0 setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={60, 0}}, NULL) = 0对比两份快照,很明显。SymLinksIfOwnerMatch比FollowSymLinks设置额外地调用了5次lstat(),验证是否为符号连接。
DirectoryIndex index.php index.html index.html.var当用户访问的URL以一个”/”结尾的时候,DirectoryIndex则指明了要寻找的资源列表。也就是说,Apache会依次找/path /index.php, /path/index.html等等。所以,DirectoryIndex指定的资源列表顺序与数量都会影响性能,我们推荐数量不宜太多,把最常用的资 源放在列表的最前面。这里,我们分别提供不同顺序,不同长度的三份strace快照:
#--配置 DirectoryIndex index.html index.html.var index.php 且资源列表的所有资源都不存在 read(11, "GET /test/ HTTP/1.1\r\nHost: 192.1"..., 8000) = 366 gettimeofday({1271745805, 985450}, NULL) = 0 stat64("/var/www/html/test/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 stat64("/var/www/html/test/index.html", 0xbfd3691c) = -1 ENOENT (No such file or directory) lstat64("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test/index.html", 0xbfd3691c) = -1 ENOENT (No such file or directory) stat64("/var/www/html/test/index.html.var", 0xbfd3691c) = -1 ENOENT (No such file or directory) lstat64("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test/index.html.var", 0xbfd3691c) = -1 ENOENT (No such file or directory) stat64("/var/www/html/test/index.php", 0xbfd3691c) = -1 ENOENT (No such file or directory) lstat64("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test/index.php", 0xbfd3691c) = -1 ENOENT (No such file or directory) gettimeofday({1271745805, 988039}, NULL) = 0 #--配置:DirectoryIndex index.html index.html.var index.php 其中index.php资源存在 read(11, "GET /test/ HTTP/1.1\r\nHost: 192.1"..., 8000) = 366 gettimeofday({1271746049, 388916}, NULL) = 0 stat64("/var/www/html/test/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 stat64("/var/www/html/test/index.html", 0xbfd3691c) = -1 ENOENT (No such file or directory) lstat64("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test/index.html", 0xbfd3691c) = -1 ENOENT (No such file or directory) stat64("/var/www/html/test/index.html.var", 0xbfd3691c) = -1 ENOENT (No such file or directory) lstat64("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test/index.html.var", 0xbfd3691c) = -1 ENOENT (No such file or directory) stat64("/var/www/html/test/index.php", {st_mode=S_IFREG|0644, st_size=27, ...}) = 0 setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={60, 0}}, NULL) = 0 #--配置DirectoryIndex index.php index.html index.html.var 且资源index.php存在 read(11, "GET /test/ HTTP/1.1\r\nHost: 192.1"..., 8000) = 392 gettimeofday({1271746373, 504237}, NULL) = 0 stat64("/var/www/html/test/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 stat64("/var/www/html/test/index.php", {st_mode=S_IFREG|0644, st_size=27, ...}) = 0 setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={60, 0}}, NULL) = 0对比上面三份不同的DirectoryIndex配置与快照,我们可以看到正确位置、合适的资源列表数目给我们省下了不少的系统内核函数调用。当用 户访问一个目录的时候,Apache会根据DirectoryIndex指定的资源列表依次查找。所以,第三份是性能最佳的配置。
Options MultiViews我们应该尽量避免使用Options MultiViews来实现内容协商,它非常低效,因为要搜索整个目录。我们可以通过type-map的方式实现内容协商。把所有要用到的type- map放在一个文件里面,比搜索文件性能会更高。所谓,内容协商,指从几个有效资源中选择一个最匹配用户端请求的资源给用户端,这个过程就叫做内容协商。 比如,用户请求了资源/path/test/foo, 而且/path/test/foo文件并不存在。MultiViews的做法就是在整个目录查找foo.*所有文件,并且跟据用户请求的content- type, content-encoding等信息,把一个最接近用户请求的资源作为用户请求的资源,返回给用户。这里给出了一份Options MultiViews方式内容协商的快照,以供参考。
read(11, "GET /test/foo HTTP/1.1\r\nHost: 19"..., 8000) = 369 gettimeofday({1271751239, 855470}, NULL) = 0 stat64("/var/www/html/test/foo", 0xbf93f62c) = -1 ENOENT (No such file or directory) lstat64("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/test/foo", 0xbf93f62c) = -1 ENOENT (No such file or directory) open("/var/www/html/test/", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY) = 12 fstat64(12, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 fcntl64(12, F_SETFD, FD_CLOEXEC) = 0 getdents64(12, /* 9 entries */, 4096) = 272 stat64("/var/www/html/test/foo.gif", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 stat64("/var/www/html/test/foo.php", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 stat64("/var/www/html/test/foo.pdf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 stat64("/var/www/html/test/foo.jpg", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 stat64("/var/www/html/test/foo.txt", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 getdents64(12, /* 0 entries */, 4096) = 0 close(12) = 0 … lstat64("/var/www/html/test/foo.php", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 open("/var/www/html/test/foo.php", O_RDONLY) = 12 fstat64(12, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 lseek(12, 0, SEEK_CUR) = 0 read(12, "", 8192) = 0 close(12) = 0 chdir("/etc/httpd/conf") = 0 setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0 writev(11, [{"HTTP/1.1 200 OK\r\nDate: Tue, 20 A"..., 254}], 1) = 254 write(8, "192.168.0.98 - - [20/Apr/2010:16"..., 172) = 172 shutdown(11, 1 /* send */)我们在/test目录下面分别创建了foo.*共5个文件,当我们请求/test/foo的时候,我们可以从加粗部分,很容易地发现Apache在 找不到/test/foo的情况下,它会在/test查看所有foo.*文件状态。我们再从红色部分可以发现Apache经过内容协商之后,最终决定以 /test/foo.php作为用户的响应,而并没有返回用户404错误。所以,我们在配置Apache的时候,尤其要避免使用Option MultiViews,因为这种配置效率较低。
EnableMMAP on尽可能地打开内存映射文件设置。所谓内存映射文件,指的就是Linux内核通过内核调用mmap()将磁盘文件与进程某段内存地址空间(有可能是物 理内存,但也有可能是虚拟内存)对映起来,自此Apache就可以通过访问内存直接访问磁盘文件,它不需要使用read()和write()等系统内核调 用来访问磁盘了。所以,在大多数情况下,可以通过内存映射文件的机制来提高磁盘I/O性能。Apache2开始支持mmap功能,对于较大的静态文 件,Apche是不会使用mmap的,因为需要不小的内存开销。另外,mmap仅对较小的静态文件有效。也就是说,Apache2对于较小的静态文件,才 会启用mmap将文件映射到进程内存地空间。
read(11, "GET /test/test.html HTTP/1.1\r\nHo"..., 8000) = 375 gettimeofday({1271778564, 175940}, NULL) = 0 stat64("/var/www/html/test/test.html", {st_mode=S_IFREG|0644, st_size=10, ...}) = 0 open("/var/www/html/test/test.html", O_RDONLY|O_LARGEFILE) = 12 mmap2(NULL, 10, PROT_READ, MAP_SHARED, 12, 0) = 0xb6107000 writev(11, [{"HTTP/1.1 200 OK\r\nDate: Tue, 20 A"..., 260}, {"hello wold", 10}], 2) = 270 munmap(0xb6107000, 10) = 0 write(8, "192.168.0.98 - - [20/Apr/2010:23"..., 179) = 179在这里,加粗部分我们可以看得出。open()以只读的方式打开请求文件,接下来通过mmap2()将文件映射到进程内存地址空间。我们并没有看到 系统内核read()调用,这是因为Apache直接读取了进程内存空间的数据,属于用户态行为。即,一旦映射之后,用户就不用像从前一次一次通过系统内 核调用read()去将磁盘数据读入用户态内存空间了。这样省去了与用户空间与内核空间读文件上下切换的开销。
EnableSendfile On如果你的系统支持Sendfile机制的话,通过EnableSendfile On设置开启该机制。Sendfile机制可以提高Apache性能。所谓Sendfile,就是我们平常所提到过的”零拷贝”。一般,我们对一个磁盘文 件写操作,要经过几次拷贝才能够完成。首先用户进程,通过fgets()向请求系统内核调用read,这时候read会将磁盘文件数据拷到系统内核空间的 缓冲区,然后系统内核调用返回的数据拷到用户空间的缓冲区。当回写数据的时候,又会通过fwrite()函数请求系统内核调用write,将数据写回磁 盘。磁盘设备->内核缓冲(内存)->用户缓冲(内存)来回共4次拷贝。对于一个静态文件请求,我们会发现,首先从磁盘设备将数据拷贝到内核 空间缓冲区,又从内核空间缓冲区拷贝到用户空间缓冲区,马上又从用户空间缓冲区拷贝回内核缓冲区,接着从内核缓冲区写入到网络接口设备。我们发现,静态数 据从内核空间交给用户空间之后,并没有被程序做任何处理,又原原本本地传回了内核空间,数据就这样白白地兜了一圈。所以,Linux自支持 Sendfile机制以后,它可以让数据不用交给用户空间缓冲区,直接在内核空间处理掉。这样节约了内核态的切换和用户态数据的复制开销。Apache对 于较大的静态资源请求,会启用Sendfile。下面,我们来对比一下启用与不启用sendfile的快照
open("/var/www/html/test/20090316.pdf", O_RDONLY|O_LARGEFILE) = 13 writev(11, [{"HTTP/1.1 200 OK\r\nDate: Wed, 21 A"..., 260}], 1) = 260 _llseek(13, 0, [0], SEEK_SET) = 0 read(13, "%PDF-1.4\n%\n3 0 obj\n<<\n/Producer "..., 8192) = 8192 write(11, "%PDF-1.4\n%\n3 0 obj\n<<\n/Producer "..., 8192) = 8192 read(13, "\310Y\10\302\263\3330\r W"..., 8192) = 8192 write(11, "\310Y\10\302\263\3330\r/H\265x W"..., 8192) = 8192 read(13, "\315~\273:\371w\t\324\246\307"..., 8192) = 8192 write(11, "\315~\273:\371w\t\324\0333\215F\303Pa\246\307"..., 8192) = 8192 read(13, "\236\263\207\330\336\370\16{[\231\323;\206"..., 8192) = 8192 write(11, "\236\263\207\330\16{[\231\323;\206"..., 8192) = 8192 .... poll([{fd=11, events=POLLOUT, revents=POLLOUT}], 1, 120000) = 1 #---------------------sendfile-------------- stat64("/var/www/html/test/20090316.pdf", {st_mode=S_IFREG|0644, st_size=502106, ...}) = 0 open("/var/www/html/test/20090316.pdf", O_RDONLY|O_LARGEFILE) = 12 setsockopt(11, SOL_TCP, TCP_CORK, [1], 4) = 0 writev(11, [{"HTTP/1.1 200 OK\r\nDate: Wed, 21 A"..., 260}], 1) = 260 sendfile64(11, 12, [0], 502106) = 34780 setsockopt(11, SOL_TCP, TCP_CORK, [0], 4) = 0 poll([{fd=11, events=POLLOUT, revents=POLLOUT}], 1, 120000) = 1 sendfile64(11, 12, [34780], 467326) = 33580 poll([{fd=11, events=POLLOUT, revents=POLLOUT}], 1, 120000) = 1 sendfile64(11, 12, [68360], 433746) = 21900 poll([{fd=11, events=POLLOUT, revents=POLLOUT}], 1, 120000) = 1 sendfile64(11, 12, [90260], 411846) = 55480快照显示,启用sendfile之后一次性读取的数据会比read()读取得多好多倍,read()每次只读取8192bytes到缓冲区。而没有 启用sendfile功能,需要请求read()和write()不少回才调用一次poll事件。同时每次poll事件都需要多次复制数据。需要注意的 是,使用sendfile最好把ipv6内核模块卸掉。同时,对于很小的静态文件请求,sendfile发挥的作用并不十分明显,这是因为处理小文件时, 发送数据的整个周期占用的时间比例并不多,效果也自然不明显。
Deny/Allow from ip_address我们可以通过Deny/Allow指令限制一些非法访问。比如Deny from 或者Deny from 220.181.6.175。虽然,二者都拒绝了相同的访问者。但是后者是我们比较推荐的做法,能够得到更好的性能。因为,前者Apache需要进行 nslookup操作,而且需要进行二次DNS查询,即正、反查询。所以,为了更好的性能,我们推荐使用Deny/Allow from ip_address,而不推荐使用Deny/Allow from domain。这里,我们进演示一下Deny from 配置的一个strace快照,以供参考
stat64("/var/www/html/test/index.php", {st_mode=S_IFREG|0644, st_size=27, ...}) = 0 open("/etc/hosts", O_RDONLY) = 12 fcntl64(12, F_GETFD) = 0 fcntl64(12, F_SETFD, FD_CLOEXEC) = 0 fstat64(12, {st_mode=S_IFREG|0644, st_size=180, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb60d3000 read(12, "# Do not remove the following li"..., 4096) = 180 read(12, "", 4096) = 0 close(12) = 0 munmap(0xb60d3000, 4096) = 0 socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 12 connect(12, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.0.2")}, 28) = 0 fcntl64(12, F_GETFL) = 0x2 (flags O_RDWR) fcntl64(12, F_SETFL, O_RDWR|O_NONBLOCK) = 0 gettimeofday({1271861040, 74406}, NULL) = 0 poll([{fd=12, events=POLLOUT, revents=POLLOUT}], 1, 0) = 1 send(12, "\317\n\1\0\0\1\0\0\0\0\0\0\00298\0010\003168\003192\7in-add"..., 43, MSG_NOSIGNAL) = 43 poll([{fd=12, events=POLLIN, revents=POLLIN}], 1, 5000) = 1 ioctl(12, FIONREAD, [133]) = 0 recvfrom(12,"\317\n\205\203\0\1\0\0\0\1\0\0\00298\0010\003168\003192\7in-add"...,1024,0, {sa_family=AF_INET,sin_port=htons(53), sin_addr=inet_addr("192.168.0.2")}, [16]) = 133 close(12)这里我们可以看到,Apache先将/etc/hosts映射到内存,然后查找的DNS记录。在没有找到相应DNS记录的时候,创建了一个socket,并且连接到本地域名服务器192.168.0.2,并且发出了DNS查询请求。
不要加载没有使用到的模块服务器的物理内存资源是非常有限且宝贵,加载没有使用到的模块会占用额外的内存空间,而且一直占用着,直到关闭进程。我们来看一看,这是 CentOS5.3默认的Apache占用的内存量。下面两行分Apache默认加载模块与禁用掉不必要加载模块内存占用对比。虽然只节约了2k不到的内 存,但需要注意的是这是一个Apache子进程节约的内存空间。
apache 11069 0.0 0.4 54780 9320 pts/1 S+ 22:38 0:00 /usr/sbin/httpd apache 11651 0.1 0.3 52428 7500 pts/4 S+ 10:46 0:00 /usr/sbin/httpd我们经过测试后,根据我们的实际情况,最终只打开了以下Apache模块
@ cat /usr/local/httpd/conf/httpd.conf |grep "LoadModule" |grep -v "^#" LoadModule authz_host_module modules/mod_authz_host.so LoadModule log_config_module modules/mod_log_config.so LoadModule env_module modules/mod_env.so LoadModule ext_filter_module modules/mod_ext_filter.so LoadModule mime_magic_module modules/mod_mime_magic.so LoadModule expires_module modules/mod_expires.so LoadModule deflate_module modules/mod_deflate.so LoadModule headers_module modules/mod_headers.so LoadModule setenvif_module modules/mod_setenvif.so LoadModule mime_module modules/mod_mime.so LoadModule autoindex_module modules/mod_autoindex.so LoadModule dir_module modules/mod_dir.so LoadModule alias_module modules/mod_alias.so LoadModule rewrite_module modules/mod_rewrite.so Keep-Alive、Mod_Expires、Header、Gzip Compress(Deflate)这些内容,已经上一篇幅<<>>中详细地探讨过,这里就不再重复了。这里只贴一下,我们相关的配置吧。
KeepAlive On MaxKeepAliveRequests 100 KeepAliveTimeout 10有时候出于维护原因。我们可能会建立一些虚拟目录,进行一些数据受限访问。这种情况,我们建议不要记录日志,因为读日志文件与写日志是磁盘I/O操作。操作I/O操作始终很昂贵。
目录的层级不要太深,越简单越好目录的层次结构,不要太深。我们推荐3层就差不多了。因为,我们常常需要lstat(), stat(), cd()等操作于这些目录。虽然,这些内核调用微不足道,但是我们还是要遵守这样一条优化原则:减少不必要的系统内核调用。
Worker MPM与Prefork MPM。(待续)我们将在其它篇幅更深入地介绍Apache Worker MPM与Apache Prefork MPM
总结1. 避免不必要的DNS查询
a) HostnameLookups Off
b) Deny/Allow from ip_address
2. Sendfile对于较大静态资源请求效率更高,同时建议关闭操作系统ipv6内核模块
3. 减少不必要的系统内核调用
a) AllowOverride None 禁止去尝试打开.htaccess
b) Options FollowSymLinks 禁止去判断访问目录是否为连接
c) DirectoryIndex index.php index.html 合理的资源列表数握与位置顺序会节约更多的系统内核调用
d) Options MultiViews 低效的内容协商
e) 保护简洁的目录层级结构
f) 关闭不要必要的日志记录功能
4. 节约系统内存资源:不要加载没有使用到的模块
5. MMap、Deflate(Gzip)、Expires、Header、Keep-Alive
6. 有条件的话,自己编译Apache,并且支持Worker MPM
7. strace /usr/sbin/httpd -X -f /etc/http/conf/httpd.conf方便跟踪系统内核调用。-X即debug模式,只启用单个worker,且不从控制台分离。