云联天下首页 |  登陆 |  注册 |  密码找回 |  关于我们 | 加入收藏 
首页技术资料系统/服务器→Apache 性能提示 【字号: 】 【背景色 杏仁黄 秋叶褐 胭脂红 芥末绿 天蓝 雪青 灰 银河白(默认色)

Apache 性能提示

网址来源:http://www.kehui.net发布时间: 2002-01-21 18:12:12
简介 <br>Apache是把正确性放在首位、把速度放在其次的通用Web服务器。即使这样,它的性能十分令人满意。许多站点只有不到10M的出口带宽。Apache能够在这些站点的低端Pentium服务器上全速工作。实际上,拥有更多带宽的站点出于一些原因(比如大量的CGI和数据库事务处理)需要用一台以上的机器满足带宽需求。这些原因导致了以往的Apache开发工作集中在正确性和可配置性。



不幸的是许多人过于重视某些指标,并把它们的原始数据当作评价Web服务器优劣的标准。被普遍接受标准的是“原始最低性能(bare minimum performance)”,而在这以外的其他速度指标只适用于很小部分的市场需求。但为了避免Apache在一些市场中受到排挤,我们在Apache1.3上尽了相当的努力,将它与高端服务器的差距减至最小。



另有一些人只是想试试这些东东能运行得多快。这些人竭力把Apache最后一滴性能挤出来,他们也想看看究竟是什么影响了Apache的性能。这篇文章的其余部分就是针对他们而撰的。



请注意本文适用于Unix上的Apache1.3,部分内容适用于NT平台。目前的Apache尚未在NT上进行优化。事实上,不同的编程模型使它在NT上的性能表现相当不好。(即POSIX模型。NT借助POSIX子系统模拟这种编程标准,因此效率很低。Apache2.0抛弃了POSIX直接与操作系统打交道,性能将有所飞跃——译者注)



关于硬件平台和操作系统 <br>最直接影响Web服务器性能的硬件要数RAM。一台Web服务器从不应该访问内存交换区。交换增加了每次请求的延时,用户将因此认为“不够快”。他们会点击[停止]并重新装载网页,这将进一步增加服务器的负担。您能够也有必要调节MaxClients,使您的服务器不会衍生太多的子进程而导致交换。



除此之外的事情就没那么关键了。拥有快速的CPU、快速的网卡和硬盘都可以让您的服务器“足够快”。其实这足够快个词是需要凭经验去体会的。



操作系统的选用也是值得斟酌的大问题。普遍的准则是:及时得到操作系统提供商的最新TCP/IP补丁。迅速涌现的HTTP服务打破了截止到1994年乃至95年的Unix内核中设定的许多假设情况。理想的选择包括目前的FreeBSD和Linux。



关于运行时设置(Run-Time Configuration) <br>HostnameLookups <br>1.3版以前的Apache中,HostnameLookups的缺省值是On,这将导致每次请求时服务器都要进行NDS查询,从而增加了延迟。Apache1.3将此缺省值设为Off。在1.3及以后的版本中,如果您使用了任何allow from domain或deny from domain命令,所付出的代价将是两次DNS查询带来的延时(在一次逆向查询后跟着一次正向查询,以保证前者得到的结果是真实的)。因此为了得到最理想的性能应避免使用HostnameLookups(使用IP地址而非域名也是个好主意)。



限制命令的使用范围是可行的,比如使用类似<Location /server-status>的容器。这种情况下,DNS查询只发生在符合条件的请求中。下面的例子使查询只发生在.html和.cgi文件的请求中:



HostnameLookups off <br><Files ~ ".(html|cgi)$> <br>HostnameLookups on <br></Files> <br>关闭了DNS查询后,如果在您的CGI程序中需要DNS名称的话,可以考虑在那些程序中调用gethostbyname。



FollowSymLinks 和 SymLinksIfOwnerMatch <br>在任何情况下,只要您没有指定FollowSymLinks的选项(即Options FollowSymLinks),或者指定了SymLinksIfOwnerMatch选项,Apache将不得不调用额外的系统函数来检查符号链接。每次针对文件名的请求都将触发一次检查。比如您指定了:



DocumentRoot /www/htdocs <br><Directory /> <br>Options SymLinksIfOwnerMatch <br></Directory> <br>当一个指向URI /index.html的请求到来时,Apache将对/www,/www/htdocs和/www/htdocs/index.html分别调用lstat(2)。不仅如此,lstat的结果是从不被缓存的,因此每次请求都要重新这样的检查。如果您的确需要安全的符号链接的话,可以试着这样做:



DocumentRoot /www/htdocs <br><Directory /> <br>Options FollowSymLinks <br></Directory> <br><Directory /www/htdocs> <br>Options -FollowSymLinks +SymLinksIfOwnerMatch <br></Directory> <br>这至少避免了对DocumentRoot目录本身的检查。请注意,如果在RocumentRoot之外有Alias或者RewriteRule涉及的目录,您需要为这些目录增加类似的选项。为了在无符号链接检查的情况下得到最佳性能,请在所有地方设置FollowSymLinks,并去掉所有的SymLinksIfOwnerMatch。



AllowOverride <br>在任何情况下,只要您允许覆盖(通常是.htaccess文件),Apache将试图为每次针对文件名称的请求打开.htaccess文件。比如:



DocumentRoot /www/htdocs <br><Directory /> <br>AllowOverride all <br></Directory> <br>当指向URI /index.html的请求到来时,Apache将试图打开/.htaccess、/www/.htaccess和/www/htdocs/.htaccess。这个问题可以用类似解决FollowSymLinks的方法解决。为了得到最佳性能,在所有地方使用AllowOverride None。



内容协商 <br>如果您对每处细微的性能调节都很在意,在可能的情况下避免内容协商(content-negotiation)。实际应用中,协商的益处超过了给性能带来的损失。您可以在一种情况下提速服务器:避免使用这样的通配符:



DirectoryIndex index <br>请列出所有可能的情况:



DirectoryIndex index.cgi index.pl index.shtml index.html <br>并把最常用的选择放在前面。



 



进程的建立 <br>对于1.3版以前的Apache,MinSpareServers、MaxSpareServers、和StartServers这三个参数对性能测试的结果有巨大影响。Apache启动后需要一个“爬升”期使其子进程数与服务器的负载相平衡。刚刚启动的Apache生成StartServers个子进程。而后将每隔一秒生成一个新的子进程,最终达到MinSpareServers的要求。所以如果服务器用StartServers等于5的默认值启动后被100个客户并发访问,Apache将用后续的95秒种生成足够的子进程以平衡负载。由于现实中的服务器不经常启动,这种技术在实际应用中工作得很好。但在评测软件中的表现就不那么出色了,因为这些软件可能顶多运行10分钟。



一秒一个的规则防止服务器在生成子进程时过于忙碌。如果它忙于繁殖进程,请求将被搁置。但这个规则对直观性能的影响太大了,它必须有所改观。在Apache 1.3中,一秒一个的规则被废弃了。它首先衍生一个子进程,等一秒,衍生两个,等一秒,再衍生两个,直到一秒衍生32个子进程。随后它将保持这个速度直到满足MinSpareServers的要求。



这看起来足够好了。几乎不用在MinSpareServers、MaxSpareServers或StartServers上费工夫了。当每秒钟衍生的进程数超过4时,ErrorLog中会增加一条相应的记录。如果您看到了很多这样的提示,请调整这些参数。mod_status的输出会给您一些提示。



于进程相关的问题是由MaxRequestsPerChild导致的进程终止。MaxRequestsPerChild缺省地设置为0,意味每个子进程处理的请求数不受限制。如果当前的设置值非常小,您可能希望大幅度提升这个值。为了防止内存泄露,在SunOS或者低版本的Solaris上,应把此值设为10000左右。



如果使用了持续连接(keep-alives),子进程将繁忙等待(busy waiting)已打开连接的后续请求而不能做其他的事。缺省的15秒种试图使影响将至最底。您需要在网络带宽和服务器资源之间作出权衡。任何情况下,不应设置持续连接时间超过60秒。否则大部分好处将变成损失。



关于编译时设置 <br>mod_status 和 ExtendedStatus On <br>如果在编译Apache时您包含了mod_status并且将ExtendedStatus设置为On,Apache将为每个请求进行两次gettimeofday(2)系统调用(或者针对不同的系统调用times(2))及(在1.3以前的版本)许多次time(2)。这些都是为了在报告中含有时间戳。为了得到最佳性能,请将ExtendedStatus设为Off(这是缺省的设置)。



多socket中的accept 串行化 <br>这部分文章将讨论Unix socket API不利的一方面。假设您的服务器用多个Listen命令侦听多个端口或者多个IP地址。Apache使用select(2)检测每个socket连接(connection)是否就绪。select(2)示意有零个或至少一个连接等待某个socket。Apache含有多个子进程,所有空闲的子进程同时侦听新的连接。原始的实现如下所示(这个例子不是真正的代码,它出于教学目的被简化了)



for (;;) { <br>for (;;) { <br>fd_set accept_fds;



FD_ZERO (&accept_fds); <br>for (i = first_socket; i <= last_socket; ++i) { <br>FD_SET (i, &accept_fds); <br>} <br>rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL); <br>if (rc < 1) continue; <br>new_connection = -1; <br>for (i = first_socket; i <= last_socket; ++i) { <br>if (FD_ISSET (i, &accept_fds)) { <br>new_connection = accept (i, NULL, NULL); <br>if (new_connection != -1) break; <br>} <br>} <br>if (new_connection != -1) break; <br>} <br>process the new_connection; <br>} <br>但这种实现会引起严重的饥饿问题。由于多个子进程同时执行这个循环,它们将在select中阻塞。当任何socket上出现一个请求时,所有被阻塞的进程将复苏,并从select返回(苏醒进程的数量取决于操作系统和时间)。它们将继续执行并试图接受这个连接,但只有一个进程会成功(假设目前仍只有一个连接),其余进程将阻塞在accept中。这将把所有失败的进程锁定,使它们只为一个socket上的请求服务。它们会一直被阻塞,直到在那个socket上出现足够的请求把它们唤醒。这一饥饿问题首先在PR#467被提出。至少有两种解决它的方法。



一种方案是使用非阻塞的socket。这种情况下,accept不会阻塞子进程,它们将会立即返回。但这种方案会造成CPU时间的浪费。假设有十个在select中的空闲进程,而后到来了一个连接请求。九个进程将苏醒、试图接受连接、失败,并返回select,这些进程实际什么都没做。而且如果在这期间,其他socket上出现请求,没有哪个进程会为它服务。总而言之,这种方案不是十分有效,除非您拥有和空闲子进程数目相当的CPU——恐怕不切实际。



另一种方案被Apache采纳。这种方案串行化(serialize)对内层循环的调用。代码如下所示(改进的部分被加粗显示):



for (;;) { <br>accept_mutex_on (); <br>for (;;) { <br>fd_set accept_fds;



FD_ZERO (&accept_fds); <br>for (i = first_socket; i <= last_socket; ++i) { <br>FD_SET (i, &accept_fds); <br>} <br>rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL); <br>if (rc < 1) continue; <br>new_connection = -1; <br>for (i = first_socket; i <= last_socket; ++i) { <br>if (FD_ISSET (i, &accept_fds)) { <br>new_connection = accept (i, NULL, NULL); <br>if (new_connection != -1) break; <br>} <br>} <br>if (new_connection != -1) break; <br>} <br>accept_mutex_off (); <br>process the new_connection; <br>} <br> 



accept_mutex_on和accept_mutex_off 两个函数实现了互斥量(mutual exclusion semaphore),在任意时刻只能有一个子进程拥有互斥量。多种方法可以实现互斥量。在src/conf.h(1.3版之前)或src/include/ap_config.h(1.3版及以后)可以作出以下选择。一些系统不提供任何互斥方法。在这些系统上使用多个Listen命令是不保险的。



USE_FLOCK_SERIALIZED_ACCEPT <br>此方法用flock(2)系统调用对一个锁文件加锁。(此文件在LockFile命令中指定) <br>USE_FCNTL_SERIALIZED_ACCEPT <br>此方法用flock(2)系统调用对一个锁文件加锁。(此文件在LockFile命令中指定) <br>USE_SYSVSEM_SERIALIZED_ACCEPT <br>(1.3版及以后)此方法借助SysV的信号量(semaphores)实现互斥。但不巧的是SysV信号量有一些负面作用。一是Apache可能在清除信号量之前非正常终止;二是在使用信号量API时需要考虑到任何与服务器UID相同的CGI程序可以进行拒绝服务攻击(就是说所有的CGI程序都可以这样做,除非使用suexec或cgiwrapper之类的方法)。所以,这种方法并不被IRIX之外的系统广泛采纳(由于大多数IRIX系统上,使用前两种方法的代价太大)。 <br>USE_USLOCK_SERIALIZED_ACCEPT <br>(1.3版及以后)此方法仅在IRIX上可用。它调用usconfig(2)创建互斥量。虽然这种方法避免了对SysV信号量的种种争议,但它不是IRIX的缺省方案。这是由于在单处理器的IRIX系统 (5.3或6.2)上,uslock代码比SysV信号量慢两个数量级;但在多处理器的IRIX中前者比后者快一个数量级。这无非使问题复杂化了。所以在多处理器IRIX系统上,您需要用如下的附加参数编译Apache: <br>在EXTRA_CFLAGS中添加-DUSE_USLOCK_SERIALIZED_ACCEPT <br>USE_PTHREAD_SERIALIZED_ACCEPT <br>(1.3版及以后)此方法实现了POSIX标准互斥量。它理应可以工作在任何实现了全部POSIX线程规范的系统上,但事实是只有在Solaris 2.5或以上的系统及特定的配置中才能工作。如果您尝试这种方法的话,需要小心服务器挂起或者没有响应。服务器在只输出静态网页的情况下运行得很好。 <br>如果您的系统上有其他串行化的方法,为它书写代码(并把补丁寄给Apache)是值得的。



有一个考虑到但从未实现的方案是对循环部分地串行化——即允许一定数目的进程进入循环。在同一时刻可运行若干进程的多处理器系统上,这个主意是满不错的。而且前面提到的方案并没有充分利用带宽。可由于高度并行化的服务器实在少见,这个方案的优先级比较低。



为了得到最佳性能,不用多侦听命令是最理想的。请继续往下看。



单socket中的accept串行化 <br>以上言及的方案对多socket服务器是相当不错的,但只有一个socket的情况又如何呢?理论上,由于在连接请求到来之前所有子进程将阻塞在accept中,单个socket不会产生上述种种问题。但实际上,上述非阻塞解决方案所带来的“回旋(spinning)”问题在这里只不过被掩盖起来了。在绝大多数TCP协议栈的实现中,一个接请求到来时内核将唤醒所有阻塞在accept中的进程。它们之一将得到此请求并返回用户空间,其余的进程将返回内核重新休眠。这将带来与多socket非阻塞解决方案相同的资源浪费。



由于这点原因,我们发现如果为socket串行化,许多系统表现得更“友好”——即使是一个socket的情况。这是单个socket串行化作为绝大多数情况的缺省配置的原因。在Linux上不甚精确的(Linux 2.0.30 / 双Pentium Pro 166 w / 128Mb内存)实验表明,对每次请求而言,串行化的单个socket仅比没有串行化的socket损失不到3%的性能。但未串行化的socket显示出每次连接请求100毫秒的延时。这也可能仅仅由于过长的通讯距离造成的。如果您不想串行化单个socket,可以定义宏SINGLE_LISTEN_UNSERIALIZED_ACCEPT。这样,仅有一个socket的服务器将不会串行化。



延迟关闭(Lingering Close) <br>就象draft-ietf-http-connection-00.txt第8节讨论的那样,为了使服务器能够可靠地实现HTTP协议,有必要独立地关闭每个方向上的通讯(每个TCP连接有两个方向,每个方向是分别独立的)。这个事实往往被其他服务器所忽视,而Apache 1.2就已经正确地处理了。



当这个特性增加到Apache中时却在许多版本的Unix中引起了问题。这是TCP规范的短见造成的——它没有声明FIN_WAIT_2有超时,但也没有阻止这样的实现。在没有超时的系统中,Apache 1.2将导致许多socket将永远处于FIN_WAIT_2的状态。这可以简单地用打最新TCP/IP补丁的方法避免。然而在提供商从不发行补丁的系统上(也就是SunOS4——虽然得到源代码许可证的人可以自己打补丁),我们决定不直接使用这一特性。



有两种实现这个特性的办法:一是socket的SO_LINGER选项。但似乎是命中注定,在多数TCP/IP协议栈中它从来不能正确地实现。即使是在提供了正确实现的平台(即Linux 2.0.31)上,这种方法也要比第二种方法代价(指CPU时间)高得多。



大多数情况下,Apache在一个叫lingering_close的函数中实现了它(在 http_main.c)。这个函数大致如下所示:



void lingering_close (int s) <br>{ <br>char junk_buffer[2048];



/* shutdown the sending side */ <br>shutdown (s, 1);



signal (SIGALRM, lingering_death); <br>alarm (30);



for (;;) { <br>select (s for reading, 2 second timeout); <br>if (error) break; <br>if (s is ready for reading) { <br>read (s, junk_buffer, sizeof (junk_buffer)); <br>/* just toss away whatever is here */ <br>} <br>}



close (s); <br>} <br>这自然增加了连接结束时的开销,但它是可靠的实现所必需的。随着HTTP/1.1的日益盛行,所有连接都是持久的,这种开销将被众多的连接请求抵消。如果您想冒险禁止这一特性的话,可以定义宏NO_LINGCLOSE,但这显然是不被推荐的。实际上,由于在HTTP/1.0中持久的管道式连接越来越普遍,lingering_close几乎是必须的选择。(管道式连接非常高效,所以您还是希望支持它的吧)



记分板文件 <br>Apache利用一种叫做记分板(scoreboard)的技术在父、子进程间通讯。它的理想实现是在共享内存中。有的操作系统允许我们直接访问共享内存,或者提供它们的确切端口。在这些系统中的典型实现就是共享内存记分板。其他的系统则将磁盘上的文件作为缺省实现。磁盘文件不仅低效而且不稳定(又没有什么优势)。请为您的操作系统仔细阅读src/main/conf.h文件,并在其中寻找USE_MMAP_SCOREBOARD或者USE_SHMGET_SCOREBOARD。定义它们之一(以及相应的HAVE_MMAP和HAVE_SHMGET)将允许Apache使用共享内存。如果您系统的内存共享机制与众不同,请编辑src/main/http_main.c并增加Apache所需的挂钩函数(同时请把补丁寄给我们)



注:直到1.2版,Apache的Linux版才开始使用共享内存。这一疏忽使得以前版本的Apache在Linux上表现得很不理想。



DYNAMIC_MODULE_LIMIT <br>如果您不打算支持动态加载模块的话(准备榨出最后一滴性能的您可能希望如此),编译服务器时请设定参数-DDYNAMIC_MODULE_LIMIT=0。这将节省出为动态加载模块而分配的内存。



附录:对某次跟踪状况的详细分析 <br>本附录描述了运行在Linux上的Apache 1.3系统调用的跟踪情况。运行时(run-time)配置文件中除了必要的缺省选项外还增加了:



<Directory /> <br>AllowOverride none <br>Options FollowSymLinks <br></Directory> <br>被请求的文件是一个6K的静态网页,其中不包含特殊内容。对于非静态或者伴随有内容协商的请求,跟踪结果将有明显的不同(一些情况下会十分晦涩)。我们将首先列出完整的跟踪结果,然后逐条进行分析。(它是由strace跟踪程序生成的,其他类似的程序包括truss、ktrace和par)



accept(15, {sin_family=AF_INET, sin_port=htons(22283), sin_addr=inet_addr("127.0.0.1")}, [16]) = 3 <br>flock(18, LOCK_UN) = 0 <br>sigaction(SIGUSR1, {SIG_IGN}, {0x8059954, [], SA_INTERRUPT}) = 0 <br>getsockname(3, {sin_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("127.0.0.1")}, [16]) = 0 <br>setsockopt(3, IPPROTO_TCP1, [1], 4) = 0 <br>read(3, "GET /6k HTTP/1.0rnUser-Agent: "..., 4096) = 60 <br>sigaction(SIGUSR1, {SIG_IGN}, {SIG_IGN}) = 0 <br>time(NULL) = 873959960 <br>gettimeofday({873959960, 404935}, NULL) = 0 <br>stat("/home/dgaudet/ap/apachen/htdocs/6k", {st_mode=S_IFREG|0644, st_size=6144, ...}) = 0 <br>open("/home/dgaudet/ap/apachen/htdocs/6k", O_RDONLY) = 4 <br>mmap(0, 6144, PROT_READ, MAP_PRIVATE, 4, 0) = 0x400ee000 <br>writev(3, [{"HTTP/1.1 200 OKrnDate: Thu, 11"..., 245}, {"



kehui 

相关新闻
v 如何规划一个高容量的Apache服务器 2002-01-21 18:12:12
v Linux+Apache实现用户身份认证 2002-01-21 18:12:12
v 在Linux防火墙上编译Apache+PHP4+Mysql 2002-01-21 18:12:12
v 基于Apache 配置文件的Web页面访问权限控制 2002-01-21 18:12:12
v Apache服务器配置全攻略(三) 2002-01-21 18:12:12
v Apache服务器配置全攻略(五) 2002-01-21 18:12:12
v 超级服务器apache+mysql+php+ssl之完全安装攻略 2002-01-21 18:12:12
v Apache管理好帮手—Apache Launcher 2002-01-21 18:12:12
v Apache重负荷服务器应如何优化 2002-01-21 18:12:12
v Apache服务器配置全攻略(一) 2002-01-21 18:12:12
  最新新闻
智慧家居
智慧家居颠覆传统智能家居
智慧云谷让智能家居变成有智慧的
智慧云谷引领智慧家居新生活
科技改变生活 智慧云谷智慧家居系
智慧家居领航者,智慧云谷助你玩
智能家居如何赢得市场美誉度?
智慧云谷智慧家居:创业者有无限
WiFi智能家居你还在用?这样的智
互联网+助推智能家居产业
智慧云谷为您打造真正的智能家居
智能家居产业需要的不是单品,而
新家如何选择开关?智慧云谷iWis
智能传感器-世界首款“智”为你的
智慧云谷开关智能安防智能空气质
智能开关品牌,如何选择智能开关
秋季干燥,智慧家居温湿度传感器
传感器助力智慧家居 感知爱家
iWiscloud智能触摸开关缔造家居装

  最新帖子
 ※室内空气污染的危害及  [sensor]
 ※超声波风速传感器在生  [sensor]
 ※这么冷清  [gabc111]
 ※手机APP操作有问题  [ssy11407]
 ※智慧云谷智慧家居将在  [cici]
 ※上传下载  [cici]
 ※下载智慧家居  [apple2008]
 ※秋季干燥,智慧家居温  [apple2008]
 ※智慧家居紧扣热点 安全  [apple2008]
 ※办公大楼如何智慧化管  [apple2008]
 ※智慧云谷工业自控的优  [apple2008]
 ※传感器助力智慧家居 感  [apple2008]
 ※智能开关品牌,如何选  [apple2008]
 ※智慧云谷开关智能安防  [apple2008]
 ※没有专业人员,如何安  [apple2008]
 ※烟台智慧云谷董事长任  [apple2008]
 ※互联网+助推智能家居产  [apple2008]
 ※WiFi智能家居你还在用  [apple2008]
 ※智慧云谷智慧家居:创  [apple2008]
 ※智能家居如何赢得市场  [apple2008]
钯碳回收 硝酸银回收 银浆回收 银焊条回收 回收银浆 氯化钯回收 氯化钯回收 氧化钯回收 回收硝酸钯 钯水回收价格 海绵钯回收 钯炭回收价格 回收镀金板 深圳钯碳回收 镇江氯化钯回收 杭州钯浆回收 银浆回收多少钱 回收钯碳公司 硝酸银的价格 那里有回收金 氯化钯回收价格 江苏擦银布回收 硝酸银价格 德州钯粉回收 银铜回收 回收钯粉 回收铂碳催化剂 佛山钯碳回收 金盐回收价格 海绵钯回收 钯碳高价回收 钯回收价格 钯炭回收