配置、调优和调试 Apache
正如长期用户所知,Linux、Apache 和其他自由软件包比其闭源 counterparts 更稳定且更易于配置。然而,这显然并不意味着自由软件倡导者可以免受软件错误和配置问题的影响。实际上,许多开源软件的复杂性有时会令人沮丧;通常不清楚应该修改哪个选项,甚至从哪里开始。
本月,我们将探讨网站管理员在尝试配置、调优和调试其 Apache 配置时可以使用的一些工具和技术。这不可能是一个全面的列表,仅仅是因为可能出错的事情太多了。然而,能够识别问题的原因通常可以相对容易地提出解决方案,或者至少开始朝着解决方案努力。
我使用 Red Hat Linux 大约五年了,从 Red Hat 还是康涅狄格州一家小公司的时候就开始使用,而不是现在连我母亲都认识的知名初创公司。在那段时间里,我成为了 Red Hat Package Manager (RPM) 的忠实粉丝。我仍然记得我必须手动修改 makefile 才能编译从互联网下载的软件;我可以下载程序的二进制版本,用一个命令安装它,然后同样轻松地删除它,这个事实仍然让我感到惊讶。(我听说 Debian 的软件包管理系统甚至更复杂,但我还没有机会尝试。)
学会喜爱 RPM 的系统管理员通常不愿意开始从源代码编译程序。这不仅需要更多的时间和更多的知识,而且从源代码编译和安装程序会使几个月或几年后的删除变得困难。然而,尽管存在固有的缺点,但我坚持从源代码编译某些程序。其中之一是 Apache,它通常作为 Red Hat 安装的一部分安装。
Apache 本身是一个相对较小的程序。大部分功能都驻留在各个模块中,这些模块通常在编译时并入 Apache。因此,如果您希望 Apache 自动更正拼写错误的 URL,您可以将 mod_speling(没错,这不是笔误)包含在您的编译中。如果您将运行一个没有任何 CGI 程序的服务器,您可以删除 mod_cgi。列表还在继续,使您可以根据您的确切需求自定义 Apache。
因此,我强烈建议每个人都从头开始编译 Apache。这个过程通常非常简单,在典型的现代计算机上应该不超过几分钟。编译 Apache 的第一步是删除系统上可能已有的任何 RPM,以避免混淆 RPM 数据库或您自己关于文件确切来源的信息。
要从 Red Hat 系统中删除 Apache RPM,请使用以下命令:rpm -e apache。
为了更好地了解您的磁盘驱动器在运行时的情况,您可以打开“非常详细”的选项:rpm -evv apache。
在大多数系统上,此命令本身是不够的。RPM 不仅跟踪安装了哪些文件,还跟踪软件包之间的依赖关系。由于典型的 Red Hat 安装包括 mod_perl(通常已损坏)和 mod_php 的 RPM,您可能还需要擦除这些 RPM
rpm -evv apache mod_perl mod_php
如果删除软件包会破坏依赖关系,RPM 将退出并显示致命错误,指示如果您删除有问题的软件包,哪些依赖关系将无法满足。然后,您应该考虑软件包(例如,mod_perl)是否仅对另一个软件包(例如,Apache)有用,或者删除它是否会引起太多其他问题。
删除任何现有的 Apache RPM 后,您可以开始在您的系统上编译和安装 Apache。(当然,您始终可以在 RPM 存在的情况下编译 Apache,在安装编译版本之前删除 RPM。)从 https://apache.ac.cn/ 的本地镜像下载最新版本,在撰写本文时,最新版本为 1.3.12。完成后,您可以使用以下命令解压缩它
tar -zxvvf apache_1.3.12.tar.gz
z 选项在使用前使用 gunzip 解压缩 tar 存档,vv 标志要求“非常详细”的输出。这两个选项仅适用于 GNU tar,它是 Linux 系统上的标准 tar 版本。
解压缩源代码后,您可以使用以下命令编译 Apache
cd apache_1.3.12 # Switch into the Apache directory ./configure # Get a default configuration make # Compile the source code make install # Install Apache under /usr/local/apache/
以上四个步骤会将与 Apache 相关的程序放置在 /usr/local/apache/bin 中,日志文件放置在 /usr/local/apache/logs 中,HTML 文件放置在 /usr/local/apache/htdocs 中,CGI 程序放置在 /usr/local/apache/cgi-bin 中。“make install”命令必须以 root 用户身份执行。
要运行 Apache,请使用默认安装在 /usr/local/apache/bin 中的 apachectl 程序。apachectl 是一个 shell 脚本,它可以相对容易地启动或停止 Apache,确保在给定时间系统上只运行一个 Apache 进程。要启动 Apache,请使用
/usr/local/bin/apachectl start
You may need to insert this line in one of your startup files, such as
/etc/rc.d/rc.local, to ensure that Apache starts
up when your
computer is booted. This is especially true if you removed the RPM
version of Apache using the above instructions, since the RPM comes
with a startup file that is automatically placed (and invoked) inside of
/etc/rc.d/init.d. apachectl can also be used to shut down the server:
/usr/local/bin/apachectl stop
Apache 启动时做的第一件事之一是查看其配置文件,传统上称为 httpd.conf,默认情况下位于 /usr/local/apache/conf 中。此文件包含许多命令,称为“指令”,后跟一个或多个值。因此,我们可以使用指令显式设置服务器的名称
ServerName www.lerner.co.il每个 Apache 模块都允许定义自己的指令,这些指令在调用该模块时控制程序的行为。例如,mod_userdir 安装 UserDir 指令,指示应将哪个目录名称在每个用户的 home 目录中特殊处理,以便在 Web 上创建站点。
但是,如果未安装 mod_userdir 会发生什么?那么 Apache 将不知道如何处理 UserDir 指令,并将退出并显示致命错误。解决方案是运行 apache configtest,它读取 httpd.conf 并检查它以确保所有指令都是已知的并且具有合法的值。如果所有指令都具有合法的定义,则 apachectl 将响应“OK”。
解决此问题的另一个部分解决方案是将所有特定于模块的指令放在 <IfModule> 部分内。<IfModule> 告诉 Apache 只有在实际加载特定模块时才应注意一个或多个指令。因此,我们可以始终定义我们的 UserDir 指令,假设它被包装在以下部分中
<IfModule mod_userdir.c> UserDir public_html </IfModule>
当使用作为 DSO 编译的模块时,<IfModule> 特别有用,因为它增加了灵活性。
一旦您的 Apache 服务器被编译、安装、配置和运行,您可能仍然会遇到一些问题。apachectl configtest 是确保所有指令都合法的好方法,但这并不能确保它们会工作,也不能确保它们是您真正想要的值。
检查服务器是否以正确方式工作的良好工具是 telnet。Telnet 对于大多数 Linux 用户来说可能很熟悉,它是从一台计算机登录到另一台计算机的方式。但是,telnet 可以打开任何端口上的 TCP 连接,而不仅仅是用于“telnet”协议的默认端口 23。因此,您可以使用 telnet 进行 SMTP(端口 25)、POP(端口 110),甚至 HTTP(端口 80)。这是测试 Web 服务器以查看它们是否正在运行以及运行一些基本测试的好方法。
此技术的关键是理解每个 TCP/IP 服务都与源计算机和目标计算机上的 IP 地址和端口相关联。因此,两台计算机之间的 telnet 连接几乎肯定包括两个 IP 地址、客户端上的任意端口和服务器上的众所周知的端口 23。同样,在两台计算机之间传输电子邮件的 SMTP 连接通常包括从客户端上的任意端口到服务器上的端口 25 的连接。有关众所周知的端口列表,包括许多不应在没有充分理由的情况下更改的端口(例如 FTP、SMTP、telnet),请查看任何运行 Linux 的计算机上的标准文件 /etc/services。
要使用 telnet 程序连接到任意端口,只需指示端口号(或名称,如果在 /etc/services 中出现)。例如,要连接到计算机 www.lerner.co.il 的端口 80 上运行的 HTTP 服务器,只需说 telnet www.lerner.co.il 80。
如果服务器在不同的端口上运行,如 Apache Listen 和 Port 指令所定义,只需指定不同的数字。例如,如果服务器在端口 8080 上运行,我们可以使用 telnet www.lerner.co.il 8080 连接到它。
当然,只要在 httpd.conf 中正确指定,单个 Apache 进程就可以处理两个或多个端口号上的输入。
如果在指定的端口上没有服务器运行,则 telnet 将退出并显示致命的“连接被拒绝”错误。在这种情况下,请查阅 Apache 错误日志,通常放在 /usr/local/apache/logs/error_log 中,以确定问题的原因。
如果 HTTP 服务器确实在指定的端口上运行,则 telnet 将显示一条消息,指示如何退出回到 telnet 命令提示符(通常使用 CONTROL-]),然后将您连接。您看到的提示符取决于您连接到的服务器类型;虽然 SMTP、FTP 和 POP 都以服务器计算机的名称欢迎传入的用户,但 HTTP 异常沉默。假设您知道您已连接到的服务器的名称,并且连接显然是成功的。
此时,您可以发出 HTTP 请求。最简单的请求形式是 GET /,后跟一个换行符。这是最原始的 HTTP 请求,不再使用,但内置于 HTTP 服务器中,以确保与旧客户端的向后兼容性。按下 ENTER 后不久,您应该看到您连接到的站点的根(或“索引”)页面的内容。起初可能很难阅读未解析的 HTML,但请记住,这是一种简单的调试方法。
更复杂的请求使用 HTTP/1.0,HTTP 的第一个版本,它在请求和响应中包含标头。请求和响应都以一个或多个“标头”行开头,其中包含标头名称、冒号和文本字符串。标头和请求/响应正文之间用一个空行分隔。
因此,我们可以将我们的简单请求重写为 GET / HTTP/1.0,后一部分附加在后面,以指示我们的客户端理解更复杂的 HTTP 版本。然后我们必须按两次 ENTER,一次表示此行结束,第二次表示我们不想在命令行和 HTTP 请求正文之间发送任何标头。
您还可以使用 telnet 将名称-值对提交到服务器,用“=”分隔每个名称与其对应的值,并用 & 符号分隔每对名称-值对。通常,此类参数在 GET 请求中传递给 CGI 程序或其他动态内容生成器。例如
GET /cgi-bin/foo.pl?name1=val1&name2=val2 HTTP/1.0
After pressing ENTER twice, you should see a set of response
headers, followed by whatever output is produced by the CGI program
foo.pl.
You can also pass headers along with the request. After pressing
ENTER following the initial GET line, type one or more
headers, with a new line after each one. For example:
GET /cgi-bin/foo.pl?name1=val1&name2=val2 HTTP/1.0 Accepts: text/html Accept-language: text/html Following the final header, press ENTER twice—once to finish the header, and again to indicate that the request is complete.
即使是中等流行的网站也倾向于同时运行多个 Apache 副本。旧的服务器会等到新的连接进入后再决定是否“fork”出一个现有服务器进程的新副本。Apache 的作者放弃了这种方法,转而采用“pre-forking”,这意味着 Apache 在启动时立即创建大量子进程。
每个子进程一次只处理一个 HTTP 连接,这意味着运行的 Apache 进程数量必须始终至少与站点应处理的并发连接数一样多。此最大数量由 MaxClients 指令设置,默认值为 150。如果 MaxClients 设置得太低,则一个或多个用户可能最终会等待,直到 Apache 进程完成处理先前的连接,并且可以为新连接提供服务。
Apache 根据 httpd.conf 中的提示动态更改可用服务器的数量,具体取决于它收到的请求数量。MinSpareServers 和 MaxSpareServers 指令告诉 Apache 保留多少备用服务器,以准备处理传入的请求。如果备用服务器的数量低于 MinSpareServers,Apache 将生成几个新服务器。相反,如果未使用的服务器数量超过 MaxSpareServers 中定义的数量,Apache 将杀死多余的服务器。
如果服务器启动并成功响应,但接受连接的时间很长,则可能是您没有告诉 Apache 启动足够的服务器。尝试增加 MaxSpareServers 或 MaxClients,以便更少的人需要等待空闲服务器来处理他们的请求。
当然,添加新服务器可能会大量消耗计算机资源,消耗更多的 CPU 时间和内存。mod_perl 是一个特别大的内存用户;因此,在添加包含 mod_perl 的新 Apache 进程时,您应该更加保守。使用标准的 Linux free 命令,它显示可用的物理和虚拟内存量,以更好地了解内存的去向。我也喜欢使用 top,它除了其他信息外,还显示每个进程正在消耗的 CPU 和内存量。
由于 Web 服务器需要尽可能快地响应请求,并且由于虚拟内存比物理 RAM 慢得多,因此您应特别注意虚拟内存的使用并尽量减少其使用。
在与 Web 服务器相同的计算机上运行一个或多个数据库服务器(例如 MySQL 或 PostgreSQL)的小型网站可能会发现自己处于令人羡慕的困境。随着动态生成站点的访问者数量增加,Apache 进程的数量也必须增加。但是为了服务所有这些访问者,数据库连接的数量也必须增加。在某个时候,站点会成为自身成功的受害者,数据库和网站争夺系统资源。因此,大多数流行的数据库支持的站点将这两个功能至少分离到两台计算机上,一台或多台数据库服务器连接到一台或多台 HTTP 服务器。
获取当前 Apache 状态快照的一种好方法是使用 mod_status 模块。mod_status 描述了每个 HTTP 服务器的当前状态,无论它是正在等待新连接、读取请求、处理请求还是写入响应。
mod_status 默认编译到 Apache 中,这意味着您需要做的就是设置适当的指令,并将默认处理程序或请求处理子例程设置为“server-status”。然后,任何定义为具有“server-status”处理程序的 URL 都会生成状态列表,忽略用户的其余请求。
因此,mod_status 最常见的做法是仅为一个 URL 激活。例如,我们可以在我们的 Web 服务器上创建虚拟的“/server-status”URL,这样任何访问 /server-status 的人都会看到 mod_status 的输出。我们还指示 Apache 始终生成完整状态列表,而不是简单版本。这是一个简单的配置示例
<Location /server-status> SetHandler server-status </Location> ExtendedStatus On
一旦我将这四行放在 httpd.conf 中并重新启动 Apache,或者向其发送 HUP 信号,我就会从 /server-status URL 获得以下输出
Server Version: Apache/1.3.12 (UNIX) mod_perl/1.24 Server Built: Mar 29 2000 12:25:42 Current Time: Friday, 21-Jul-2000 16:02:51 IDT Restart Time: Friday, 21-Jul-2000 16:02:48 IDT Parent Server Generation: 2 Server uptime: 3 seconds Total accesses: 0 - Total Traffic: 0 kB CPU Usage: u0 s0 cu0 cs0 0 requests/sec - 0 B/second - 1 requests currently being processed, 4 idle servers状态信息首先是一段相当多的文本,指示服务器已运行多长时间,以及人们访问服务器的次数。它还指示此 Web 进程正在服务的字节数以及有多少服务器处于空闲状态。mod_status 因此为我们提供了一个了解 Apache 服务器世界的良好窗口,使我们能够查看我们是否以最节省资源的方式定义了 MaxSpareServers。
mod_status 然后以以下格式生成输出,乍一看可能看起来很神秘
W____............................................. ................................................. ................................................. .................................................
每个“.”字符代表一个当前未运行的潜在 Apache 服务器进程。那些正在等待新连接的进程由“_”表示;那些正在从用户的 HTTP 请求中读取输入的进程由“R”表示;那些正在将其输出写入用户浏览器的进程由“W”表示。并非所有字母都可以在给定时间可见;当前的 Apache 状态动态变化,您从 mod_status 看到的输出将发生变化以反映这一点。
在此显示之后,我们获得了每个活动进程正在执行的操作的逐行视图。我们可以看到哪些连接处理时间过长,哪些连接是每月最受欢迎的连接,以及几乎任何其他方面。
当然,通常将您的状态信息向全世界公开是一个坏主意。幸运的是,您可以使用“Order”、“Deny”和“Allow”指令来限制对一组 IP 地址或整个域的访问。例如
<Location /server-status> SetHandler server-status Order deny,allow Deny from all Allow from .lerner.co.il </Location>
使用上述配置,mod_status 将仅显示我的域中的 IP 地址的结果。来自另一个域的请求将收到 HTTP 响应,指示禁止访问。
到目前为止,我们假设我们的 Apache 安装是静态编译的,所有模块都在编译时放置在 Apache 内部。这是编译 Apache 的传统方式,也是您只需执行“./configure”时的默认方式。
上述配置的问题在于它相对不灵活。如果您发现您忘记配置 Apache 以包含特定模块会发生什么?您将必须重新编译整个程序,指定在运行 configure 时您想要和不想要包含哪些模块。乍一看这似乎没什么不好,毕竟,您多久会想要向系统添加新模块?
但是,问题比这更深入,至少在两个方面。为什么 Apache 应该为可能不使用的模块消耗内存?此外,为什么我每次仅发布一个模块的新版本时都必须重新编译 Apache?
为了解决这个问题,Apache 开发人员现在支持一个名为 DSO 或“动态共享对象”的系统。DSO 使编译 Apache 时仅包含两个静态链接的模块成为可能,即 mod_core(提供核心功能)和 mod_so(处理 DSO 的加载)。所有其他模块都可以构建为仅在必要时加载。由于模块不是 Apache 的固有部分,因此也可以在无需重新编译 Web 服务器本身的情况下升级模块。正如我们将在下面看到的那样,如果您需要升级或调试已经运行和配置的 Apache 服务器,此功能可能会派上用场。
要使用 DSO 编译 Apache,您需要决定要静态编译哪些模块,以及要作为 DSO 编译哪些模块。我的偏好是将所有内容都设为 DSO,但仅编译 Apache 默认安装的模块。我们可以通过更改我们对“configure”的调用来做到这一点
./configure --enable-shared=max
这将自动启用 mod_so,并将编译包含所有默认模块的 Apache。在使用 make 编译 Apache 并使用 make install 安装它之后,Apache 将继续像以前一样工作。唯一的区别是它现在可以动态加载模块,根据需要向已编译的 HTTP 服务器添加新模块。
使用 --enable-shared 编译的 Apache 服务器创建的默认 httpd.conf 与为静态编译的 Apache 服务器创建的默认 httpd.conf 略有不同。首先,大多数指令都隐藏在 <IfModule> 部分内,使 Apache 即使某些模块尚未加载也可以加载。此外,每个模块必须首先使用 LoadModule 指令加载,然后使用 AddModule 指令启用。例如
LoadModule perl_module libexec/libperl.so AddModule mod_perl.c
LoadModule 接受两个参数,模块的名称和 .so 文件所在的文件夹。名称必须与编译 DSO 模块时使用的名称匹配,而文件名应指向相对于 /usr/local/apache 的目录。在上面的示例中,默认情况下,DSO 模块放置在 /usr/local/apache/libexec 中。
与 httpd.conf 中的大多数其他指令相反,LoadModule 和 AddModule 对它们放置的顺序敏感。LoadModule 必须在 AddModule 之前,并且每个模块必须在加载和添加后,其指令才能工作。此外,某些模块必须在其他模块之前加载,否则将无法工作。如果可以使用自动配置工具来处理 LoadModule 和 AddModule 的插入,请让它这样做。这可以使您的 Web 服务器免受难以追踪的配置问题的影响。
一旦 Apache 使用 DSO 支持编译,就可以随时添加新模块。但是,这些模块必须使用编译 Apache 服务器本身时存在的相同配置信息进行编译。这由 Ralf S. Engelschall 编写的“Apache Extension”程序 apxs 自动处理。apxs 使将 Apache 模块编译为 DSO(.so 文件),然后将其安装到适当的 Apache 配置中成为可能。不幸的是,似乎几乎没有关于 apxs 的文档,这意味着可能难以理解此程序的确切作用或工作方式。
例如,让我们假设我们已经使用 DSO 支持编译并安装了 Apache。几周后,我们注意到我们网站上的许多点击都导致“文件未找到”错误,因为用户无法拼写我们在 URL 中使用的奇怪名称。一种解决方案显然是修改站点,以便用户能够更轻松地拼写内容。但更简单的解决方案是安装 mod_speling,这样在很大程度上可以忽略大小写和拼写错误。
要将 mod_speling 编译为 DSO,请键入
/usr/local/apache/bin/apxs -c mod_speling.oc
apxs 将调用 gcc,编译 mod_speling。结果将不是直接可执行的,而是一个可以由 Apache 调用的库。如果编译成功,那么我们可以使用以下命令安装 mod_speling
/usr/local/apache/bin/apxs -i -n -a mod_speling.so安装新的 DSO 模块时,使用 apachectl configtest 特别有用。它可以确保我们添加的模块确实存在并且正在工作,并且 Apache 现在理解我们在 <IfModule> 部分之外添加的任何新指令。
mod_perl,Apache 模块,它使可以使用 Perl 编写新模块并使用流行的语言配置现有模块成为可能,可以编译为 DSO。这需要像 mod_speling 的情况一样使用 apxs。但是,mod_perl 比单个模块复杂得多,并且依赖于许多外部信息进行编译。mod_perl 因此以类似于独立 Perl 模块的方式配置和编译,使用 perl Makefile.PL,然后是 make、make test 和 make install。
要将新版本的 mod_perl 编译到现有版本的 Apache 中,请发出以下命令
perl Makefile.PL \ USE_APXS=1 WITH_APXS=/usr/local/apache/bin/apxs
如果您想为所有不同的 Apache 处理程序启用 mod_perl,而不仅仅是默认的 PerlHandler(用于创建动态内容),请打开 EVERYTHING 开关。例如
perl Makefile.PL \ USE_APXS=1 WITH_APXS=/usr/local/apache/bin/apxs \ EVERYTHING=1以这种方式创建 Makefile 后,您可以使用以下命令将 mod_perl 编译并安装到现有的 Apache 服务器中
make make test make install此方法不仅适用于将 mod_perl 的新副本安装到 Apache 中,还适用于升级现有副本。然后,您可以通过 telnet 连接到服务器的地址和端口号,并发出以下命令来测试 mod_perl 是否已编译到服务器中
HEAD / HTTP/1.0
This will return the HTTP headers associated with the / document on
the server. Among other things, there should be a "Server" header
indicating what kind of server is running. mod_perl adds a tag to
this output string, so you should see output like the following:
Server: Apache/1.3.12 (UNIX) mod_perl/1.24由于 mod_perl 更新的发布时间与 Apache 更新的发布时间不同,因此我发现以这种方式安装和升级 mod_perl 非常有用。
虽然 mod_status 可以告诉我们每个 Apache 进程正在发生什么,但它无法告诉我们特定模块内部正在发生什么。一般来说,这并不是一件坏事;我真的关心 mod_mime 或 mod_speling 内部发生了什么吗?
但是在 mod_perl 的情况下,其中发生了许多复杂的事情,如果能够找出正在发生的事情,那就太好了。Perl Apache::Status 模块与 mod_perl 一起工作,提供此信息。
为了激活 Apache::Status,我们需要在 httpd.conf 中插入另一个部分。与 mod_status 一样,我们将创建一个新的 Location 部分,该部分将特定处理程序与虚拟 URL 相关联,传统上称为“/perl-status”
PerlModule Apache::Status <Location /perl-status> SetHandler perl-script PerlHandler Apache::Status </Location>
一旦我们重新启动服务器或向其发送 HUP 信号,请求 URL /perl-status 将生成一个选项菜单,例如“Environment”和“Inheritance tree”。用于 mod_perl 的其他 Perl 模块(例如 HTML::Mason)可以为 Apache::Status 安装自己的钩子,从而可以查看其环境。例如,Mason 提供了一个简单的界面,用于查看当前配置,以及已编译和缓存的组件列表。
在我撰写本专栏之前的几天里,我使用了上述所有技术来追踪 HTML::Mason 安装中的问题。我帮助运行的托管服务器在处理不断增长的负载时遇到了一些问题。每隔几个小时,服务器上所有基于 Mason 的站点都会失败,随后一两个小时后,非 Mason 站点也会失败。实际上,“失败”这个词太强烈了,浏览器会向服务器发送请求,但会超时(大约十分钟后)等待接收连接。到底发生了什么,我该如何解决它?
我的第一反应是认为我们的服务器内存不足。我使用“top”和“free”来检查系统,但没有看到任何异常。一方面,这是一种解脱。与此同时,这意味着我们不知何故耗尽了可用服务器,即使我已经将系统配置为最多 150 个并发客户端。我的服务器可能很受欢迎,但即使对我来说,150 个并发访问也非常不寻常。显然还有其他事情发生了。
显然与 Mason 有某种联系,可能与 mod_perl 也有联系。我决定是时候将 mod_perl 升级到最新版本 (1.24) 了,使用我上面描述的技术。所以我升级了 HTML::Mason 和其他几个相关模块的副本,使用 apachectl 重新启动了 Apache,并希望问题会消失。
不幸的是,简单的升级并没有奏效。系统在活动几个小时后仍然停止响应请求。我使用 Apache::Status 查看 mod_perl 的状态,似乎没有任何异常。
我使用 mod_status 查看了系统状态,发现随着时间的推移,许多 Apache 进程卡在了“write”状态。换句话说,Apache 进程列表正在缓慢但肯定地从“.”(未分配)进程列表转换为“W”(正在写入 HTTP 响应)进程列表。系统上每次调用 Mason 组件都会占用另一个 Apache 进程,并且永远不会释放它!因此,系统在短短几个小时后就锁定是不足为奇的。如果站点的 Mason 相关部分更受欢迎,系统将会更快崩溃。
我查看了 Mason 配置文件,并确定我对 Apache::Session 模块的使用(它允许 mod_perl 程序跟踪用户的移动和操作,正如我在 8 月份的《Linux Journal》中的文章“使用 Mason 进行会话管理”中看到的那样)未能返回。因此,每次调用 Mason 组件时,一切都会正常工作,直到组件需要返回,此时执行的程序只会空转,等待连接到 MySQL 数据库。
我的解决方案不是特别优雅,但确实奏效了:我决定停止使用 Apache::Session 的 MySQL 版本(称为 Apache::Session::MySQL),并开始使用简单的基于文件的版本(称为 Apache::Session::File)。我重启了服务器,并且很高兴地发现一切都按应有的方式工作了。
