上期我们讲到
LoaRunner性能测试Apachetop监控,这期我们讲LoaRunner性能测试Apache调优。
Apache调优
Apache最近的版本是2.2版,Apache2.2是一个多用途的web服务器,其设计在灵活性、可移植性和性能中求得平衡。虽然没有在设计上刻意追求性能指标,但是Apache2.2仍然在许多现实环境中拥有很高的性能。相比于Apache 1.3,2.2版本作了大量的优化来提升处理能力和可伸缩性,而且大多数的改进在默认状态下就可以生效。但是,在编译时和运行时,都有许多可以显著提高性能的选择。本章节主要阐述在安装Apache2.2时,服务器管理员可以改善性能的各种方法。关于Apache调优主要包括三个方面的内容:硬件与操作系统方面、运行时的配置和编译时的配置。
硬件与操作系统方面
影响web服务器性能的最大的因素是内存,一个web服务器应该尽量不使用交换机制,因为交换产生的滞后使用户总感觉“不够快”,此时用户就可能去按“停止”和“刷新”,从而给web服务器带来更大的负载。所以应该控制MaxClients的设置,以避免服务器产生太多的子进程而发生交换,先计算出每个Apache进程平均消耗的内存,然后再为其它进程留出足够多的内存。
其他因素比较普通,装一个足够快的CPU,一个足够快的网卡,几个足够快的硬盘,这里说的“足够快”是指能满足实际应用的需求。
操作系统是很值得关注的又一个因素,已经被证实的很有用的经验有:
选择能够得到的最新最稳定的版本并打好补丁。现在许多操作系统厂商都提供了可以显著改善性能的TCP协议栈和线程库。
如果操作系统支持sendfile()系统调用,则务必安装带有此功能的版本或补丁。在支持sendfile的系统中,Apache2可以更快地发送静态内容而且占用较少的CPU时间。
运行时的配置
运行时的配置主要涉及到的相关模块为:mod_dir、mpm_common、mod_status,涉及到的相关指令为:AllowOverride、HostnameLookups、DirectoryIndex、EnableMMAP、EnableSendfile、KeepAliveTimeout、MaxSpareServers、MinSpareServers、Options、StartServers。
1)AllowOverride
AllowOverride两种设置All或None,如果网站空间允许覆盖(通常是用.htaccess文件),则Apache会试图对文件名的每一个组成部分都打开.htaccess,例如:
DocumentRoot/www/htdocs
AllowOverrideAll
如果请求“/index.html”,则Apache会试图打开“/.htaccess”、“/www/.htaccess”、“/www/htdocs/.htaccess”。为了得到最佳性能,应当对文件系统中所有的地方都使用AllowOverrideNone设置。
2)HostnameLookups和其它DNS
在Apache1.3以前的版本中,HostnameLookups默认被设为On,这样会带来延迟,因为对每一个请求都需要作一次DNS查询。在Apache2.2中,它被默认地设置为Off。如果需要日志文件提供主机名信息以生成分析报告,则可以使用日志后处理程序logresolve,以完成DNS查询,而客户端无须等待。
一般情况下应该是在其它的机器上,而不是在web服务器上执行后处理和其他日志统计操作,以免影响服务器的性能。
如果你使用了任何“Allowfromdomain”或“Denyfromdomain”指令(也就是domain使用的是主机名而不是IP地址),则代价是要进行两次DNS查询(一次正向和一次反向,以确认没有作假)。所以,为了得到最高的性能,应该避免使用这些指令(不用域名而用IP地址也是可以的)。
注意:可以把这些指令包含在
HostnameLookupsoff
HostnameLookupson
如果在某些CGI中偶尔需要DNS名称,则可以调用gethostbyname来解决。
3)FollowSymLinks和SymLinksIfOwnerMatch
如果网站空间中没有使用OptionsFollowSymLinks,或使用了OptionsSymLinksIfOwnerMatch,Apache就必须执行额外的系统调用以验证符号连接。文件名的每一个组成部分都需要一个额外的调用。例如,如果设置了:
DocumentRoot/www/htdocs
OptionsSymLinksIfOwnerMatch
在请求“/index.html”时,Apache将对“/www”、“/www/htdocs”、“/www/htdocs/index.html”执行lstat()调用,而且lstat()的执行结果不被缓存,因此对每一个请求都要执行一次,如果确实需要验证符号连接的安全性,则可以这样:
DocumentRoot/www/htdocs
OptionsFollowSymLinks
Options-FollowSymLinks+SymLinksIfOwnerMatch
这样,至少可以避免对DocumentRoot路径的多余的验证。
注意:如果Alias或RewriteRule中含有DocumentRoot以外的路径,那么同样需要增加这样的段。为了得到最佳性能,应当放弃对符号连接的保护,在所有地方都设置FollowSymLinks,并放弃使用SymLinksIfOwnerMatch。
4)内容协商(ContentNegotiation)
一个资源可能会有多种不同的表现形式,比如,可能会有不同语言或者媒体类型的版本甚至其组合。最常用的选择方法是提供一个索引页以供选择。但是由于浏览器可以在请求头信息中提供其首选项的表现形式,因此就有可能让服务器进行自动选择。比如,浏览器可以表明希望看见法语的信息,如果没有,英语的也行。如需仅请求法语的表现形式,浏览器可以发出:
Accept-Language:fr
注意:此首选项信息仅当存在多种可选的语言表现形式时才有效。
下面是一个更复杂的请求,浏览器表明,可以接受法语和英语,但最好是法语;接受各种媒体类型,最好是HTML,但纯文件或其他文本类型也可以;最好是GIF或JPEG,但其他媒体类型也可以,并允许其他媒体类型作为最终表现形式:
Accept-Language:fr;q=1.0,en;q=0.5
Accept:text/html;q=1.0,text/*;q=0.8,image/gif;q=0.6,image/jpeg;q=0.6,image/*;q=0.5,*/*;q=0.1
Apache支持HTTP/1.1规范中定义的“服务器驱动”的内容协商,可以完全地支持Accept、Accept-Language、Accept-Charset、Accept-Encoding请求头,这些是RFC2295和RFC2296中定义的实验协商协议,但是不支持这些RFC中定义的“功能协商”。
资源(resource)是一个在URI(RFC2396)中定义的概念上的实体,一个HTTP服务器,比如Apache,以表现形式(representation)提供对其命名空间中资源的访问,各种表现形式由已定义的媒体类型、字符集和编码的字节流构成。任何一个特定的时刻,一个资源可以没有,或者有一个,或者有多个表现形式。如果有多个表现形式存在,则称该资源是可协商的(negotiable),其各种表现形式称为变种(variant),而一个可协商的资源的各种变种的区别途径称为变元(dimension)。
可以使用下述两种途径之一向服务器提供有关各变种的信息,以实现对资源的协商:
使用类型表(也就是一个*.var文件)明确指定各变种的文件名。
使用“MultiViews”搜索,即服务器执行一个隐含的文件名模式匹配,并在其结果中选择。
使用类型表文件
类型表是一个与type-map处理器关联的文档(或者兼容早期Apache配置的MIME类型:application/x-type-map),要使用这个功能,必须在配置中建立处理器,以定义一个文件后缀为type-map,最好的方法是在配置文件中这样设置:
AddHandlertype-map.var
类型表文件应该与所描述的资源同名,且对每个有效变种都有一个块(entry),每个块由若干连续的HTTP头行组成,不同变种的块用空行分开,块中不允许有空行,通常类型表都以一个描述总体性质的组合块作为开始(这不是必须的,如果有也会被忽略)。下例是一个描述资源foo的命名为foo.var的类型表文件:
URI:foo
URI:foo.en.html
Content-type:text/html
Content-language:en
URI:foo.fr.de.html
Content-type:text/html;charset=iso-8859-2
Content-language:fr,de
注意:即使将MultiViews设置为On,类型表仍然优先于文件后缀名,如果不同的变种具有不同的资源品质,就可以对媒体类型使用“qs”参数来表示这种不同。实例:一个图片的jpeg、gif、ASCII-art三个有效变种:
URI:foo
URI:foo.jpeg
Content-type:image/jpeg;qs=0.8
URI:foo.gif
Content-type:image/gif;qs=0.5
URI:foo.txt
Content-type:text/plain;qs=0.01
qs的取值范围是0.000到1.000,取值为0.000的变种永远不会被选择,没有指定qs值的变种其qs值为1.0。qs值表示一个变种相对于其他变种的"品质",比如在表现一张照片时,jpeg通常比字符构图有更高的品质;而如果要表现的本来就是一个ASCII-art,那么当然字符构图就会比jpeg文件有更高的品质。因此,qs的值取决于变种所表现的资源本身。mod_negotation类型表文档中有完整的HTTP头的列表。
Multiviews
MultiViews是一个针对每个目录的选项,也就是说可以在httpd.conf或.htaccess(如果正确设置了AllowOverride)文件中的
MultiViews的效果是:如果服务器收到对/some/dir/foo的请求,而/some/dir/foo并不存在,但是如果/some/dir启用了MultiViews,则服务器会查找这个目录下所有的foo.*文件,并有效地伪造一个说明这些foo.*文件的类型表,分配给他们相同的媒体类型及内容编码,并选择其中最合适的匹配返回给客户。
MultiViews还可以在服务器检索一个目录时,用于DirectoryIndex指令搜索的文件名。如果设置了:
DirectoryIndexindex
而index.html和index.html3并存,则服务器会作一个权衡;如果都没有,但是有index.cgi,则服务器会执行它。
如果一个目录中没有任何文件具有mod_mime可以识别的表示其字符集、内容类型、语言和编码的后缀,那么其结果将取决于MultiViewsMatch指令的设置,这个指令决定了在MultiViews协商中将使用的处理器、过滤器和其他后缀类型。
实践中,内容协商的好处大于性能的损失,如果你很在意那一点点的性能损失,则可以禁止使用内容协商。但是仍然有个方法可以提高服务器的速度,就是不要使用通配符,如:
DirectoryIndexindex.*
而使用完整的列表,如:
DirectoryIndexindex.cgiindex.plindex.shtmlindex.html
其中最常用的应该放在前面。
还有,建立一个明确的type-map文件在性能上优于使用“OptionsMultiViews”,因为所有需要的信息都在一个单独的文件中,而无须搜索目录。请参考内容协商文档以获得更详细的协商方法和创建type-map文件的指导。
5)内存映射
在Apache2.2需要搜索被发送文件的内容时,比如处理服务器端包含时,如果操作系统支持某种形式的mmap(),则会对此文件执行内存映射。在某些平台上,内存映射可以提高性能,但是在某些情况下,内存映射会降低性能甚至影响到httpd的稳定性:
在某些操作系统中,如果增加了CPU,mmap还不如read()迅速。比如,在多处理器的Solaris服务器上,关闭了mmap,Apache2.0传送服务端解析文件有时候反而更快。
如果对作为NFS装载的文件系统中的一个文件进行了内存映射,而另一个NFS客户端的进程删除或者截断了这个文件,那么进程在下一次访问已经被映射的文件内容时,会产生一个总线错误。
如果有上述情况发生,则应该使用EnableMMAPoff关闭对发送文件的内存映射。注意:此指令可以被针对目录的设置覆盖。
6)Sendfile
在Apache2.2能够忽略将要被发送的文件内容时(比如发送静态内容),如果操作系统支持sendfile(),则Apache将使用内核提供的sendfile()来发送文件。
在大多数平台上,使用sendfile可以通过免除分离的读和写操作来提升性能,然而在某些情况下,使用sendfile会危害到httpd的稳定性
一些平台可能会有Apache编译系统检测不到的有缺陷的sendfile支持,特别是将在其他平台上使用交叉编译得到的二进制文件运行于当前对sendfile支持有缺陷的平台时。
对于一个挂载了NFS文件系统的内核,它可能无法可靠的通过自己的cache服务于网络文件。
如果出现以上情况,应使用“EnableSendfileoff”来禁用sendfile。注意,这个指令可以被针对目录的设置覆盖。
7)进程的建立
在Apache1.3以前,MinSpareServers、MaxSpareServers、StartServers的设置对性能都有很大的影响。尤其是为了应对负载而建立足够的子进程时
Apache需要有一个“渐进”的过程,在最初建立StartServers数量的子进程后,为了满足MinSpareServers设置的需要,每一秒钟只能建立一个子进程。所以,对一个需要同时处理100个客户端的服务器,如果StartServers使用默认的设置5,则为了应对负载而建立足够多的子进程需要95秒。在实际应用中,如果不频繁重新启动服务器,这样还可以,但是如果为了提供10分钟的服务,这样就很糟糕了。
“一秒钟一个”的规定是为了避免在创建子进程过程中服务器对请求的响应停顿,但是它对服务器性能的影响太大了,必须予以改变,在Apache1.3中,这个“一秒钟一个”的规定变得宽松了,创建一个进程,等待一秒钟,继续创建第二个,再等待一秒钟,继而创建四个,如此按指数级增加创建的进程数,最多达到每秒32个,直到满足MinSpareServers设置的值为止。
从多数反应看来,似乎没有必要调整MinSpareServers、MaxSpareServers、StartServers。如果每秒钟创建的进程数超过4个,则会在ErrorLog中产生一条消息,如果产生大量此消息,则可以考虑修改这些设置,可以使用mod_status的输出作为参考。
与进程创建相关的是由MaxRequestsPerChild引发的进程的销毁。其默认值是“0”,意味着每个进程所处理的请求数是不受限制的。如果此值设置得很小,比如30,则可能需要大幅增加。在SunOS或者Solaris的早期版本上,其最大值为10000以免内存泄漏。
如果启用了持久链接,子进程将保持忙碌状态以等待被打开连接上的新请求,为了最小化其负面影响,KeepAliveTimeout的默认值被设置为5秒,以谋求网络带宽和服务器资源之间的平衡,在任何情况下此值都不应当大于60秒。