生产环境的web服务器,从5月份时不时报错,不得不重启tomcat来对应。
针对这个bug的调查,整理一下过程
背景
从某一个新的改善发布到生产环境之后,每隔一段时间,尤其是用户最多的时间段,nagios监控报警中会同时出现下面两条。
java.net.UnknownHostException
java.net.SocketException: Too many open files
第一条一直怀疑是云服务网络问题。第一次出现报警的时候就是在云服务网络问题出现的第二天。
第二条,显然是tomcat打开文件数过多,超过的系统限制。
那么就以第二条为突破口调查一下。
开始调查
首先确定一下当前系统的文件句柄数
[root@web001 tmp]# ulimit -n
1024
[root@web001 tmp]# ulimit -Sn
1024
[root@web001 tmp]# ulimit -Hn
4096
可以看到系统“硬”句柄数,是4096。
而tomcat会默认配置到系统“硬”句柄数。
接下来看一下系统中tomcat打开的文件句柄数。
# lsof -c tomcat #这是错误的
lsof命令参数很齐全,可以根据进程名或者PID来查询。
所以想当然地以进程名tomcat,结果结果不正确。
# lsof -c java #这是正确的,需要在root权限下执行
障害发生是还可以导出详细来分析
# lsof -c java >/tmp/lsof.txt #这是正确的,需要在root权限下执行
但是,障害并不是随时发生,只能写个cron来定时监控了。
一分钟一次输出 lsof -c java的记录条数,当超过4000的时候,输出详细。
cron设置如下:
[root@web001 ~]# crontab -e
# HEADER: This file was autogenerated at Wed Jun 27 18:50:18 +0900 2018 by puppet.
# HEADER: While it can still be managed manually, it is definitely not recommended.
# HEADER: Note particularly that the comments starting with 'Puppet Name' should
# HEADER: not be deleted, as doing so could cause duplicate cron jobs.
# 中间省略
* * * * * bash /home/xxxuser/lsof.sh
lsof.sh 脚本内容
输出java的lsof总数和命令执行时间。
- 如果超过3500的话,输出lsof详细。
- 如果超过4000的话,直接重启tomcat。(防止生产环境出问题的临时对策)
# cat /home/xxxuser/lsof.sh
#!/bin/bash
lsofnum=$(/usr/sbin/lsof -c java | wc -l)
result=$(date;/usr/sbin/lsof -c java | wc -l)
echo $result >> /tmp/lsof.txt
if [ $lsofnum -ge 3500 ]; then
date >> /tmp/lsof-more.txt
/usr/sbin/lsof -c java >> /tmp/lsof-more.txt
fi
if [ $lsofnum -ge 4000 ]; then
date >> /tmp/tomcat-restart.log
/etc/init.d/tomcat7 restart >> /tmp/tomcat-restart.log
fi
最后监控一天后发现输出的详细中,[can't identify protocol]状态位的占比会越来越高。几乎达到95%。google这个状态,基本判断出事java代码中socket没有强制close造成的。
大量垃圾socket没有强制关闭,导致后来的访问进入不了web,也就引起文章开始的两个报警。
调查代码后果真如此。接下来就是修改作业了。
lsof 命令 简介
格式:lsof(选项)
-a:列出打开文件存在的进程;
-c<进程名>:列出指定进程所打开的文件;
-g:列出GID号进程详情;
-d<文件号>:列出占用该文件号的进程;
+d<目录>:列出目录下被打开的文件;
+D<目录>:递归列出目录下被打开的文件;
-n<目录>:列出使用NFS的文件;
-i<条件>:列出符合条件的进程。(4、6、协议、:端口、 @ip )
-p<进程号>:列出指定进程号所打开的文件;
-u:列出UID号进程详情;
-h:显示帮助信息;
-v:显示版本信息。
加粗是常用选项
ulimit命令简介
格式:ulimit(选项)
选项:
-a:显示目前资源限制的设定;
-c
-d <数据节区大小>:程序数据节区的最大值,单位为KB;
-f <文件大小>:shell所能建立的最大文件,单位为区块;
-H:设定资源的硬性限制,也就是管理员所设下的限制;
-m <内存大小>:指定可使用内存的上限,单位为KB;
-n <文件数目>:指定同一时间最多可开启的文件数;
-p <缓冲区大小>:指定管道缓冲区的大小,单位512字节;
-s <堆叠大小>:指定堆叠的上限,单位为KB;
-S:设定资源的弹性限制;
-t
-u <程序数目>:用户最多可开启的程序数目;
-v <虚拟内存大小>:指定可使用的虚拟内存上限,单位为KB。
加粗是常用选项