Apache性能提示[2]

[入库:2005年9月21日] [更新:2007年3月24日]

本文简介:

对于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秒。否则大部分好处将变成损失。    

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

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

for (; {    
for (; {    
fd_set accept_fds;    

FD_ZERO (&accept_fds);    
for (i = first_socket; i <= last_socket; ++i) { 
FD_SET (i, &accept_fds); 

rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL); 
if (rc < 1) continue; 
new_connection = -1; 
for (i = first_socket; i <= last_socket; ++i) { 
if (FD_ISSET (i, &accept_fds)) { 
new_connection = accept (i, NULL, NULL); 
if (new_connection != -1) break; 


if (new_connection != -1) break; 

process the new_connection; 

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

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

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

for (; { 
accept_mutex_on (); 
for (; { 
fd_set accept_fds; 

FD_ZERO (&accept_fds); 
for (i = first_socket; i <= last_socket; ++i) { 
FD_SET (i, &accept_fds); 

rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL); 
if (rc < 1) continue; 
new_connection = -1; 
for (i = first_socket; i <= last_socket; ++i) { 
if (FD_ISSET (i, &accept_fds)) { 
new_connection = accept (i, NULL, NULL); 
if (new_connection != -1) break; 


if (new_connection != -1) break; 

本文关键:Apache性能提示
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top