公司在重新安装应用服务器之后,tomcat+nginx+redis之后,tomcat跑一段时间之后,差不多半个小时之后,就会服务挂掉。这个时候其实自己也不知道原因出在哪里,然后就开始看tomcat的catalina.out的日志,发现有许多内存溢出。其次,使用了netstat -lanput|grep java 发现了该服务器的close_wait状态特别多,当java的子进程达到了server.xml里面的 maxtheards之后,服务就会宕机。
出现CLOSE_WAIT的原因:TCP连接断开时需进行四次挥手,TCP连接的两端都可发起关闭连接的请求,如果对端发起了关闭连接,但本地没进行后续的关闭连接操作,那么该链接就会处于CLOSE_WAIT状态。虽然该链接已经处于半开状态,但已不能和对端通信,需及时的释放该链接。
于是我开始了在tomcat里面进行调试,
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="1000" minSpareThreads="20" maxIdleTime="10000"/>
这个是开启tomcat线程池的,在tomcat里面默认注释了server.xml里面, maxThreads的意思是能开启的线程最大数量(超过1000的之后的请求只能排队,等待有线程释放后,才能被处理),minSpareThreads是默认最小保持线程的数量,maxIdleTime=600000是默认值,代表当目前线程数量 大于 最小保持minsparethreads之后,便会启动这个机制,60秒就清除不活跃的线程。
但是这个默认值对我来说还是太大了,于是将60秒改为了10秒。connector配置如下
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
executor="tomcatThreadPool" #开启线程池
connectionTimeout="30000"
connectionUploadTimeout="120000"
disableUploadTimeout="true" #上传文件的连接时间,可以调大一些
acceptCount="800"
maxKeepAliveRequests="1"
enableLookups="false"
bufferSize="512"
socketBuffer="512"
redirectPort="8443" URIEncoding="utf-8"/>
connectionTimeout #连接超时时间建立一个socket连接后,如果一直没有收到客户端的FIN,也没有数据过来,那么此连接也必须等到30s后,才能被超时释放
acceptCount 当tomcat起动的线程数达到最大时,接受排队的请求个数,默认值为100
maxThreads 不能过大,如果太大的话cpu会消耗大量资源在切换线程,会导致服务器比较卡
从系统内核方面进行优化keepalive vim /etc/sysctl.cnf
net.ipv4.ip_forward = 1 #开启路由转发
net.ipv4.ip_nonlocal_bind = 1 #允许绑定非本机ip
net.ipv4.tcp_syncookies = 1 #表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 #表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接
net.ipv4.tcp_tw_recycle = 1 #表示开启TCP连接中TIME-WAIT sockets的快速回收
net.ipv4.tcp_fin_timeout = 30 #表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间
上面的4条是优化 time_wait过多的情况
net.ipv4.tcp_keepalive_time = 30
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl = 15
使用 watch -n1 -d 'netstat -lanput |grep java |wc -l'检测tomcat的状态
使用脚本对tomcat进行控制,每2分钟启用一次判断tomcat的状态
#!/bin/bash
tompid=`ps -ef |grep tomcat |grep '/usr/local/'|awk '{print $2}'`
#判断tomcat的pid
pingcu()
{
curl --connect-time 5 -m 10 -s tomcat应用的网址
}
#使用curl测试网页是否畅通,如果线程堆积,curl无法正常访问tomcat
pingcu >/dev/null
result=$?
#能正常访问的话echo $?的值是0 不能的话是非0值
if [ $result -ne 0 ]; then
echo `date "+%Y-%m-%d %H:%M:%S"`>> /usr/local/risingtech/tom.log
echo -e "\e[31m\e[1mtomcat is died\e[0m" >> /usr/local/risingtech/tom.log
#加上一个时间戳 判断tomcat的启动频率
kill -9 $tompid
echo "成功杀死" >> /usr/local/risingtech/tom.log
/usr/local/tomcat/bin/catalina.sh start && echo -e "\e[32m\e[1mtomcat 重启完毕\e[0m" >>/usr/local/risingtech/tom.log
exit 2
else
echo `date "+%Y-%m-%d %H:%M:%S"`>> /usr/local/risingtech/tom.log
echo -e "\e[32m\e[1mtomcat is alived\e[0m" >> /usr/local/risingtech/tom.log
fi
最好定时任务时间错开,避免2个同时重启,导致无法正常访问
最好还是通过代码上的修改解决问题
最终问题已经解决了。
在修改之后过了一段时间,访问过大的情况还是会出现close_wait特别多的情况,今天在测试的时候发现,查看nginx的访问log,提取了一个文件
cat /usr/local/nginx/logs/access.log |grep 项目地址(提取接口) |awk '{print $4,$7}' > jiekou.txt
cat jiekou1.txt |sort |uniq -c >jieguo.txt
#去从接口访问,排序那个接口访问的量最大
得到了一个特别多的amq访问,这个是在网页上的聊天功能,经过控制变量发现,这个东西开启了之后就会每次刷新一次首页,就会出现2个CLOSE_WAIT状态,
vim /usr/local/tomcat/webapps/cu/WEB-INF/web.xml
<!-- activeMQ -->
<!-- <servlet>
<servlet-name>AjaxServlet</servlet-name>
<servlet-class>org.apache.activemq.web.AjaxServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>AjaxServlet</servlet-name>
<url-pattern>/amq/*</url-pattern>
</servlet-mapping>
-->
注释了之后发现,close_wait没有上涨了,开发说是因为当时没有做超时限制,最终还是解决了这个问题