返回介绍

5.1 常见系统故障案例

发布于 2025-04-21 21:33:22 字数 16980 浏览 0 评论 0 收藏

本章主要介绍 Linux 系统运维过程中常见的一些故障和案例,这些案例都来自实际的生产环境,每个案例都会讲述一个小问题,这些问题非常简单,相信很多读者也都遇到过。但是需要说明的是,解决问题不是本章讲述的重点,系统运维过程中的错误也千差万别,不可能把每个问题都讲述一遍,这里重点讲述的是解决问题的思路。对每个问题的讲述,我们都从错误现象开始介绍,然后是解决问题的思路。接着是问题排查过程,最后给出解决问题的方法。通过这样抽丝剥茧的介绍,最终使读者掌握解决问题的统一方法和思路,有了这样的思路,相信所有问题都能迎刃而解。

5.1.1 su 切换用户带来的疑惑

这是一个客户的案例,客户的一台 Oracle 数据库服务器突然宕机了,由于在线业务的需要,客户没有考虑太多就直接重启了服务器,系统重新启动后没有出现问题,可是接下来,当客户准备切换到 oracle 用户下启动数据库时,怎么都无法进行 su 切换,于是问题出现了。

1.案例现象

在 root 用户下,su 切换到一个普通用户 oracle 却发生了错误,如图 5-1 所示。

图 5-1 su 切换发生 Permission denied 错误

于是,尝试直接通过 oracle 用户登录系统,发现此时的 oracle 用户也无法登录了,出现与上面同样的错误。

2.解决思路

从上面错误提示可知是权限出现了问题,那么可以从权限入手进行排查,基本思路如下:

- 用户目录/home/oracle 权限问题。

- su 程序执行权限问题。

- 程序依赖的共享库权限问题。

- SELinux 问题导致。

- 系统根空间问题。

3.排查问题

根据上面的思路,逐一检查,考虑到 su 在切换到 oracle 用户时会读取 oracle 目录下的环境变量配置文件,因此,首先检查/home/oracle 目录的权限是否存在问题:

[root@localhost home]# ls -al /home|grep oracle
drwx------  4 oracle   oinstall 4096 01-31 10:45 oracle

从输出可知,/home/oracle 目录的属主是 oracle 用户,而 oracle 用户对这个目录具有“rwx”权限,因此,oracle 用户目录的权限设置是正确的,可以排除这个问题。

接着检查 su 执行权限问题:

[root@localhost home]# ll /bin/su
-rwsr-xr-x 1 root root 24120 2007-11-30 /bin/su

可见 su 命令执行权限也没有问题,这个问题也排除了。

继续检查 su 依赖的共享库权限,使用 ldd 命令检查 su 命令依赖的共享库文件,如图 5-2 所示。

根据上面的操作,依次检查 su 命令依赖的每个库文件的权限,发现也都是正常的,都具有可执行权限,因此,共享库的问题也排除了。

根据上面的思路,继续检查 SELinux 的设置,执行命令如图 5-3 所示。

由输出可知,SELinux 处于关闭状态,这个原因也排除了。

图 5-2 通过 ldd 命令检查 su 命令依赖的共享库文件

图 5-3 检查 SELinux 的设置

到目前为止,问题变得扑朔迷离,到底是哪里出现问题了呢?作为 Linux 运维人员,例行检查系统根分区状态是非常必要的,那么首先检查一下根分区的磁盘空间大小,发现剩余空间还有很多,排除空间问题。既然报的错误是权限有问题,那么只要以权限为线索,不偏离这个核心就没错,于是继续尝试检查/home 目录下各个用户的权限,执行命令如图 5-4 所示。

图 5-4 检查/home 目录下各个用户的权限

从输出看每个用户的目录权限,都是“rwx------”,即“700”,完全没有问题。仔细检查思路,发现当前的目光一直停留在用户对应的目录上,而忽略了其他输出信息,而问题就藏在之前没有关注的信息中。在这个命令输出的前两行中,第一行权限对应的目录是“.”,代表当前目录,也就是/home 目录,权限为“rwxr-xr-x”,即“755”,第二行权限对应的目录是“..”,也就是根目录,权限却为“rw-rw-rw-”,即“666”,此时,问题终于查找到了,原来是根目录权限问题。

将根目录权限设置为“rw-rw-rw-”,显然是不正常的。在正常情况下根目录的权限应该是“755”,为何设置成了这样,很大的可能是误操作。通过 ls 命令查看根目录的权限时展示不是很清楚,也容易被很多运维人员忽略,其实我们可以通过另一个命令 stat 来详细查看每个目录的权限,如图 5-5 所示。

图 5-5 通过 stat 命令查看根目录权限

通过这个命令,可以很清晰地看到,根目录的权限是“0666”,这才是导致 su 执行失败的根本原因。

4.解决问题

知道了问题产生的原因,解决问题就非常简单,执行如下命令:

[root@localhost ~]# chmod 755 /

然后就可顺利执行 su 切换命令。

最后,做个简单的总结,这个问题主要是由于根目录没有可执行权限,而 Linux 下所有的操作都是在根目录下进行的,进而导致/home/oracle 目录没有执行权限。其实根目录权限的丢失对于系统中运行的每个用户存在同样的影响。因此,在权限出现问题时,一定要注意根目录的权限。

5.1.2 “Read-only file system”错误与解决方法

这个问题经常发生在有大量磁盘读写操作且磁盘分区很大的环境中,下面简单描述下此案例的应用环境。这是一个 Web 服务器故障案例,客户利用两台服务器加一个磁盘阵列做了一个双机热备的 Web 系统,所有网站数据都存储在磁盘阵列中,两台服务器共享一个磁盘阵列分区,在正常情况下主机挂载磁盘阵列,分区提供网站服务,主机故障时备机接管磁盘阵列分区继续提供网站服务。

1.案例现象

接到客户电话说他们的网站无法添加数据了,不过网站还可以正常访问,服务器和磁盘阵列也没有任何告警信息。

2.解决思路

根据这个简单信息,基本排查思路如下:

- 网站程序可能出现问题了。

- 服务器磁盘故障。

3.排查问题

首先通知研发人员对网站程序问题进行排查。经过检查,并没有发现程序有问题,而在程序日志中发现了一条信息:

java.lang.RuntimeException: Cannot make directory: file:/www/data/html/2013-03-30

根据这个输出可知,程序不能创建目录,那么尝试手动创建一个目录,登录 Web 服务器,在/www/data/html 目录下创建一个目录 test,操作如下:

[root@localhost html]# mkdir test  
mkdir: cannot create directory `test': Read-only file system

从这个输出信息可知,/www/data/html 目录所在的磁盘分区出现了问题,通过检查发现,/www/data/html 目录正是挂载的磁盘阵列分区,于是问题原因找到了。

4.解决问题

磁盘出现“Read-only file system”的原因有很多种,可能是文件系统数据块出现不一致导致的,也有可能是磁盘故障造成的。主流的 ext3、ext4 文件系统都有很强的自我修复机制,对于简单的错误,文件系统一般可自行修复,当遇到致命错误无法修复时,文件系统为了保证数据一致性和安全,会暂时屏蔽文件系统的写操作,将文件系统变为只读,进而出现了上面的“Read-only file system”现象。

手工修复文件系统错误的命令是 fsck,在修复文件系统前,最好卸载文件系统所在的磁盘分区:

[root@localhost ~]# umount /www/data
umount: /www/data: device is busy

提示无法卸载,可能这个磁盘中还有文件对应的进程在运行,检查如下:

[root@localhost ~]# fuser -m /dev/sdb1  
/dev/sdb1:           8800 

接着检查一下 8800 端口对应的是什么进程,如图 5-6 所示。

图 5-6 查询 8800 端口对应的进程信息

原来是系统的 apache 进程还没有停止,停止 apache,成功卸载磁盘,操作如下:

[root@localhost ~]# /usr/local/apache2/bin/apachectl stop
[root@localhost ~]# umount /www/data

最后,执行修复操作,如图 5-7 所示。

图 5-7 用 fsck 命令修复磁盘分区

修复过程比较简单,上面省略了很多输出信息。修复的时间根据磁盘大小和文件系统损坏程度而定。如果有些数据无法修复,会提示是否删除,此时可根据情况选择。修复完成后,被删除的文件会保留在对应磁盘分区挂载点的 lost+found 目录中。

修复完成后,执行挂载操作:

  [root@localhost ~]# mount /dev/sdb1 /www/data

最后,在/www/data 目录下验证是否可以成功创建文件,至此,问题圆满解决。

5.1.3 “Argument list too long”错误与解决方法

作为一名运维人员,对这个错误并不陌生,在执行 rm、cp、mv 等命令时,如果要操作的文件数很多,可能会使用通配符批量处理大量文件,这时就可能会出现“Argument list too long”这个问题。

1.错误现象

这是一台 MySQL 数据库服务器,在系统中运行了很多定时任务,某天通过如下 crontab 命令又添加了一个计划任务,退出时系统报错。

#crontab -e

编辑完成后,保存退出,就出现如图 5-8 所示的错误。

图 5-8 crontab 命令编辑完成后发生的错误

2.解决思路

根据上面的报错信息,基本判定是磁盘空间满了,那么首先从检查服务器的磁盘空间开始,先检查/tmp 磁盘空间,然后检查根分区的磁盘空间,最后检查系统其他分区的磁盘空间。

3.问题排查

通过 df 命令查看了这个服务器上所有磁盘分区的情况,/tmp 分区空间还有很多,根分区也还有很大剩余空间,都不存在问题,最后发现是/var 磁盘分区空间已经达到 100%了。至此已经定位了问题,是/var 磁盘空间爆满导致的,因为 crontab 会在保存时将文件信息写到/var 目录下,然而这个磁盘没有空间了,所以报错也是理所当然了。

4.解决问题

接着通过“du -sh”命令检查/var 目录下所有文件或目录的大小,发现/var/spool/clientmqueue 目录占用了/var 整个分区大小的 90%,那么/var/spool/clientmqueue 目录下的文件都是怎么产生的呢,能否删除?下面简单介绍下/var/spool/clientmqueue 目录的文件是怎么生成的。

打开/var/spool/clientmqueue 目录下的一些文件,发现都是一些邮件信息,邮件内容大多是关于 Cron Daemon 的,其实/var/spool/clientmqueue 就是一个邮件暂存的目录。Linux 服务器在默认情况下会发一些邮件,比如当 cron 执行的程序有输出内容时,就会发送邮件信息到执行 cron 进程的用户。在发送邮件时,系统首先会把邮件复制到/var/spool/clientmqueue 目录下,然后等待 MTA(Mail Transfer Agent)程序来处理。而 MTA 主要的功能是把这个目录中的邮件转移到/var/spool/mqueue 目录下,然后再通过 sendmail 服务发送到真正的目的地。检查这个服务器的 sendmail 服务,发现其没有开启,这样/var/spool/clientmqueue 目录非常大的原因就找到了:没有发送邮件的客户端服务,所有邮件就都堆积在这个目录下了。

在确认完这些邮件内容都没用后,切换到/var/spool/clientmqueue 目录下,执行 rm 命令删除所有的文件时,出现如下错误:

[root@localhost clientmqueue]# rm *
/bin/rm: argument list too long

此时出现了本节开头谈到的问题。

当在 Linux 系统中试图传递太多参数给一个系统命令时,就会出现“Argument list too long”错误。这是 Linux 系统一直以来都有的限制。查看这个限制可以通过命令“getconf ARG_MAX”来实现,如图 5-9 所示。

这是 CentOS 6.x 版本的一个最大值,而在 CentOS 5.x 中,这个值相对较小,如图 5-10 所示。

所以这个问题更多的是发生在 Linux 低版本中。

图 5-9 查看 CentOS 6.3 版本的 Linux 系统传递参数限制

图 5-10 查看 CentOS 5.8 版本的 Linux 系统传递参数限制

知道了产生问题的原因,解决方法就很多了,这里提供四种解决此问题的方法,下面分别进行介绍。

(1)手动把命令行参数分成较小的部分

例如:

rm [a-n]* -rf
rm [o-z]* -rf

这种方法最简单,但是相对较低级,因为必须知道怎么平均分割文件,同时对于文件数目极多的情况,需要输入很多次命令。

(2)使用 find 命令删除

基本原理是通过 find 命令筛选文件列表,把符合要求的文件传递给一系列命令。这种方法是最简洁的,也是最有效的。

例如:

find /var/spool/clientmqueue -type f -print -exec rm -f {} \;

但是这种方法也有缺点,需要遍历所有文件,因而在文件数量极多时比较耗时。

(3)通过 shell 脚本

这种方法是通过编写一个 shell 脚本,然后通过循序语句实现,与 find 方法类似。

例如,可以编写如下脚本:

#!/bin/bash
# 
设定需要删除的文件夹
RM_DIR='/var/spool/clientmqueue'
cd $RM_DIR
for I in `ls`
do
 rm -f $I
done

(4)重新编译 Linux 内核

这种方法需要手动增加内核中分配给命令行参数的页数,打开 kernel source 下面的 include/linux/binfmts.h 文件,找到如下行:

# define MAX_ARG_PAGES  32

将“32”改为更大的值,例如 64 或 128,然后重新编译内核。

此种方法永久有效,可以彻底解决问题,但是比较复杂,推荐给高级用户使用,没有 Linux 经验的用户不建议用这种方法。

5.1.4 inode 耗尽导致应用故障

1.错误现象

客户的一台 Oracle 数据库服务器在关机重启后,Oracle 监听无法启动,提示错误如图 5-11 所示。

图 5-11 inode 耗尽故障现象

从输出信息判断,应该是磁盘空间耗尽导致 Oracle 监听无法启动,因为 Oracle 在启动监听时需要创建监听日志文件,而上面三个 TNS 错误产生的原因都是由最后一行错误导致的,于是首先检查系统磁盘空间,如图 5-12 所示。

图 5-12 查看故障服务器磁盘空间信息

从磁盘输出信息可知,所有分区磁盘空间都还有不少剩余,而 Oracle 监听写日志的路径在/var 分区下,虽然/var 分区仅剩下 3.2GB 可用磁盘空间,但是这对于写一个监听日志文件来说足够了,为什么还提示空间不足呢?

2.解决思路

既然错误提示与磁盘空间有关,那就深入研究下关于磁盘空间的问题,在 Linux 系统中对磁盘空间的占用分为三个部分:第一个是物理磁盘空间,第二个是 inode 节点所占用的磁盘空间,第三个是 Linux 用来存放信号量的空间,而平时接触较多的是物理磁盘空间,对第二个和第三个空间的问题接触较少。既然不是物理磁盘空间的问题,接着检查是否是 inode 节点耗尽的问题,通过执行“df -i”查看系统可用的 inode 节点,如图 5-13 所示。

图 5-13 查看磁盘分区的 inode 使用信息

由输出可知,果然是 inode 节点耗尽导致无法写日志文件。由于 inode 被全部用完了,虽然还有可用磁盘空间,但是文件系统已经无法再记录这些空余空间了,因此也就不能再创建新文件或文件夹了。由于涉及了 inode 知识,接下来就简单介绍下 Linux 中 inode 的概念。

在 Linux 系统中,文件由数据块和元数据组成,数据块就是多个连续性的扇区,是文件存取的最小单位。块(block)的大小,最常见的是 4KB,即连续 8 个 sector 组成一个 block。而元数据用来记录文件的创建者、文件的创建日期、文件的大小等,这种存储文件元数据信息的区域就叫做 inode,或者称为“索引节点”。

由于 inode 也是用来存储文件相关属性信息的,因此 inode 也会消耗硬盘空间,在硬盘格式化的时候,操作系统会自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是 inode 区(inode table),存放 inode 所包含的信息。

每个 inode 节点的大小一般是 128B 或 256B。inode 节点的总数在格式化文件系统的时候,就已经确定,可以通过如下命令查看某个磁盘分区 inode 的总数:

[root@localhost ~]# dumpe2fs -h /dev/sda3|grep 'Inode count' 
dumpe2fs 1.39 (29-May-2006)
Inode count:              5244736

举个形象的例子,如果将文件系统比作一本书,那么,inode 就是这本书的目录。在格式化文件系统的时候,这本书的最大目录数已经确定了。在写书(保存文件到磁盘)的过程中,可能发生这样的情况:纸用完了(磁盘空间不足),此时肯定无法保存新的文件;但是还存在另外一种情况,就是目录写完了(inode 节点全部分配完了),在这种情况下,虽然还有纸(磁盘空间),但是目录(inode)已经没有了,在文件系统上当然不能新建文件了,因为没有了目录,就无法通过索引找到文件。

另外,每个 inode 都有一个号码,操作系统用 inode 号码来区分不同的文件。通过“ls -i”命令,可以查看文件名对应的 inode 号,例如:

[root@localhost ~]# ls -i install.log
325762 install.log

如果要查看这个文件更详细的 inode 信息,可以通过 stat 命令实现,如图 5-14 所示。

图 5-14 查看某文件 inode 的详细信息

3.解决问题

知道这个故障是由 inode 导致的后,接下来就要查看/var 目录下为何耗尽了 inode,通过检查发现/var/spool/clientmqueue/这个目录里面的文件仅 500 多万个,至于产生的原因,分析后确定应该是系统的 crontab 导致的,因为系统开了多个 crontab 任务,而如果 crontab 任务没有重定向,默认就会在这个目录下创建一个文件,日积月累,此目录下的小文件就会超级多。解决的方法很简单,删除这些没用的文件即可。删除的方法是直接使用 rm 命令,这时肯定会提示“Argument list too long”的错误,而解决这个的方法前面章节已经有过详细介绍,通过如下命令即可完成:

[root@localhost ~]# find /var/spool/clientmqueue/ -name "*" -exec rm -rf {} \;

删除日志文件后,再次启动 Oracle 监听,可以顺利实现启动,查看发现新的监听日志文件已经生成,至此,问题得到圆满解决。

5.1.5 文件已删除但空间不释放的原因

1.错误现象

运维的监控系统发来通知,报告一台服务器空间满了,登录服务器查看,根分区确实没有空间了,如图 5-15 所示。

图 5-15 查看服务器磁盘空间

这里首先说明一下服务器的一些删除策略,由于 Linux 没有回收站功能,所以线上服务器上所有要删除的文件都会先移动到系统/tmp 目录下,然后定期清除/tmp 目录下的数据。这个策略本身没有问题,但是通过检查发现这台服务器的系统分区中并没有单独划分/tmp 分区,这样/tmp 下的数据其实占用了根分区的空间。既然找到了问题,那么删除/tmp 目录下一些占空间较大的数据文件即可,检查/tmp 下最大的三个数据文件,如图 5-16 所示。

图 5-16 查看/tmp 下最大的前三个数据文件

通过命令输出发现在/tmp 目录下有个 66GB 大小的文件 access_log,这个文件应该是 Apache 产生的访问日志文件,从日志大小来看,应该是很久没有清理 Apache 日志文件了,基本判定是这个文件导致的根空间爆满,在确认此文件可以删除后,执行如下删除操作:

[root@localhost ~]#  rm  /tmp/access_log

接着查看系统根分区空间是否释放,如图 5-17 所示。

图 5-17 查看磁盘空间是否释放

从输出可以看到,根分区空间仍然没有释放,这是怎么回事?

2.解决思路

一般来说不会出现删除文件后空间不释放的情况,但是也存在例外,比如文件被进程锁定,或者有进程一直在向这个文件写数据等,要理解这个问题,就需要知道 Linux 下文件的存储机制和存储结构。

一个文件在文件系统中的存放分为两个部分:数据部分和指针部分,指针位于文件系统的 meta-data 中,在将数据删除后,这个指针就从 meta-data 中清除了,而数据部分存储在磁盘中。在将数据对应的指针从 meta-data 中清除后,文件数据部分占用的空间就可以被覆盖并写入新的内容,之所以在出现删除 access_log 文件后,空间还没释放,就是因为 httpd 进程还在一直向这个文件写入内容,导致虽然删除了 access_log 文件,但是由于进程锁定,文件对应的指针部分并未从 meta-data 中清除,而由于指针并未删除,系统内核就认为文件并未删除,因此通过 df 命令查询空间并未释放也就不足为奇了。

3.问题排查

既然有了解决问题的思路,那么接下来看看是否有进程一直在向 access_log 文件中写数据,这里需要用到 Linux 下的 lsof 命令,通过这个命令可以获取一个仍然被应用程序占用的已删除文件列表,命令执行如图 5-18 所示。

图 5-18 查看被应用程序锁定的已删除文件列表

从输出结果可以看到,/tmp/access_log 文件被进程 httpd 锁定,而 httpd 进程还一直向这个文件写入日志数据。从第 7 列可知,这个日志文件大小约 70GB,而系统根分区总大小才 100GB,由此可知,这个文件就是导致系统根分区空间耗尽的罪魁祸首。最后一列的“deleted”状态说明这个日志文件已经被删除,但由于进程还在一直向此文件写入数据,因此空间并未释放。

4.解决问题

到这里问题就基本排查清楚了,解决这一类问题的方法有很多种,最简单的方法是关闭或重启 httpd 进程,当然也可以重启操作系统,不过这些并不是最好的方法。对待这种进程不停对文件写日志的操作,要释放文件占用的磁盘空间,最好的方法是在线清空这个文件,具体可以通过如下命令完成:

[root@localhost ~]# echo " " >/tmp/access_log

通过这种方法,磁盘空间不但可以马上释放,也可保障进程继续向文件写入日志,这种方法经常用于在线清理 Apache、Tomcat、Nginx 等 Web 服务产生的日志文件。

5.1.6 “Too many open files”错误与解决方法

1.问题现象

这是一个基于 Java 的 Web 应用系统,在后台添加数据时提示无法添加,于是登录服务器查看 Tomcat 日志,发现如下异常信息:

java.io.IOException: Too many open files

通过这个报错信息,基本判断是系统可用的文件描述符不够了,由于 Tomcat 服务是系统 www 用户启动的,于是以 www 用户登录系统,通过“ulimit -n”命令查看系统可以打开最大文件描述符的数量,输出如下:

[www@tomcatserver ~]$ ulimit  -n
65535

可以看到这台服务器设置的最大可打开的文件描述符已经是 65535 了,这么大的值应该够用了,但是为什么提示这样的错误呢?

2.解决思路

这个案例涉及 Linux 下 ulimit 命令的使用,这里简单介绍下 ulimit 的作用和使用技巧。ulimit 主要用来限制进程对资源的使用情况,它支持各种类型的限制,常用的有:

- 内核文件的大小限制。

- 进程数据块的大小限制。

- shell 进程创建文件大小限制。

- 可加锁内存大小限制。

- 常驻内存集的大小限制。

- 打开文件句柄数限制。

- 分配堆栈的最大大小限制。

- CPU 占用时间限制用户最大可用的进程数限制。

- shell 进程所能使用的最大虚拟内存限制。

ulimit 使用的基本格式为:

ulimit [options] [limit]

具体的 ulimit 参数(即 options)含义如表 5-1 所示。

表 5-1 ulimit 参数的含义

在使用 ulimit 时,有以下几种使用方法。

(1)在用户环境变量中加入

如果用户使用的是 bash,那么可以在用户目录的环境变量文件.bashrc 或.bash_profile 中加入“ulimit -u 128”来限制用户最多可以使用 128 个进程。

(2)在应用程序的启动脚本中加入

如果应用程序是 Tomcat,那么可以在 Tomcat 的启动脚本 startup.sh 中加入“ulimit -n 65535”来限制用户最多可以使用 65535 个文件描述符。

(3)直接在 shell 命令终端执行 ulimit 命令

这种方法的资源限制仅仅在执行命令的终端生效,在退出或关闭终端后,设置失效,并且这个设置不影响其他 shell 终端。

有时候为了方便起见,也可以将用户资源的限制统一由一个文件来配置,这个文件就是/etc/security/limits.conf,该文件不但能对指定用户的资源进行限制,还能对指定组的资源进行限制。该文件的使用规则如下:

<domain> <type> <item> <value>

其中:

- domain 表示用户或用户组的名字,还可以使用“*”作为通配符,表示任何用户或用户组。

- type 表示限制的类型,可以有两个值:soft 和 hard,分别表示软、硬资源限制。

- item 表示需要限定的资源名称,常用的有 nofile、cpu、stack 等。分别表示最大打开句柄数、占用的 CPU 时间、最大的堆栈大小。

- value 表示限制各种资源的具体数值。

除了 limits.conf 文件之外,还有一个/etc/security/limits.d 目录,可以将资源限制创建一个文件放到这个目录中,系统默认会首先读取这个目录下的所有文件,然后才去读取 limits.conf 文件。在所有资源限制设置完成后,退出 shell 终端,再次登录 shell 终端后,ulimit 设置即可自动生效。

3.解决问题

在了解 ulimit 知识后,接着上面的案例,既然 ulimit 设置没问题,那么一定是设置没有生效导致的。接下来检查下启动 Tomcat 的 www 用户环境变量下是否添加了 ulimit 限制,检查后发现,www 用户下并无 ulimit 资源限制。于是继续检查 Tomcat 启动脚本 startup.sh 文件是否添加了 ulimit 限制,检查后发现也没有添加。最后考虑是否将限制加到了 limits.conf 文件中,于是检查 limits.conf 文件,操作如下:

[root@tomcatserver ~]# cat /etc/security/limits.conf|grep www
www soft nofile 65535
www hard nofile 65535

从输出可知,ulimit 限制加在了 limits.conf 文件中,既然限制已经加了,配置也没有错,为何还会报错呢?经过长时间思考,判断只有一种可能,那就是 Tomcat 的启动时间早于 ulimit 资源限制的添加时间,于是首先查看下 Tomcat 的启动时间,操作如下:

[root@tomcatserver ~]# more /etc/issue
CentOS release 6.3 (Final)
Kernel \r on an \m
[root@tomcatserver ~]# uptime
 15:10:19 up 283 days,  5:37,  4 users,  load average: 1.20, 1.41, 1.35
[root@tomcatserver ~]# pgrep 
–f  tomcat                     
4667
[root@tomcatserver ~]# ps -eo pid,lstart,etime|grep 4667
4667 Sat Jul  6 09:33:39 2013 77-05:26:02

从输出可以看出,这台服务器已经有 283 天没有重启过了,而 Tomcat 是在 2013 年 7 月 6 日 9 点多启动的,启动了近 77 天零 5 个半小时了,接着继续看看 limits.conf 文件的修改时间,操作如图 5-19 所示。

图 5-19 查看 limits.conf 文件的最后修改时间

通过 stat 命令可以很清楚地看出,limits.conf 文件最后的修改时间是 2013-07-12,通过查问相关的 Linux 系统管理人员,基本确认就是在这个时候添加的 ulimit 资源限制,这样此案例的问题就很明确了。由于 ulimit 限制的添加时间晚于 Tomcat 最后一次的启动时间,而在此期间内,Tomcat 服务一直未重启过,操作系统也一直未重启过,那么 ulimit 资源限制对于 Tomcat 来说始终是不生效的,同时,由于此操作系统是 CentOS 6.3,系统默认的最大可用句柄数是 1024,java 进程采用的是 Linux 默认的这个值,因此出现“Too many open files”的错误也是合乎情理的。

清楚问题之后,解决问题的方法非常简单,重启 Tomcat 服务即可。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。