歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Tomcat報異常:Too many open files 的解決之路

Tomcat報異常:Too many open files 的解決之路

日期:2017/2/27 15:46:44   编辑:Linux教程
Tomcat
Jul 21, 2015 8:45:23 AM org.apache.tomcat.util.net.JIoEndpoint$Acceptor run
SEVERE: Socket accept failed
java.net.SocketException: Too many open files
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:398)
at java.net.ServerSocket.implAccept(ServerSocket.java:530)
at java.net.ServerSocket.accept(ServerSocket.java:498)
at org.apache.tomcat.util.net.DefaultServerSocketFactory.acceptSocket(DefaultServerSocketFactory.java:60)
at org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run(JIoEndpoint.java:216)
at java.lang.Thread.run(Thread.java:745) 
解決的思路:
一眼異常立刻想到ulimit,於是使用
ulimit -a
查看Linux內核允許的最大資源
open files (-n) 1024
需要調大限制
ulimit -n 65535
經過觀察,問題依舊...

通過
lsof|grep tomcat|wc -l
看到tomcat的io居然有1082個,立馬想到了nginx與tomcat的不協調造成的,據網絡資料顯示:tomcat端默認開啟了 keepalive,而nginx把連接交給了tomcat並不關系是否關閉,因此tomcat需要等待"connectionTimeout"設置的超 時時間來關閉,所以最好設置“maxKeepAliveRequests”為1,讓每個連接只相應一次就關閉,這樣就不會等待timeout了。因此設置 tomcat:
<Connector port="8080" protocol="HTTP/1.1"
                maxKeepAliveRequests="1"
              connectionTimeout="20000"
              URIEncoding="UTF-8"
              redirectPort="8443" />
   <!-- A "Connector" using the shared thread pool-->
   <!--
   <Connector executor="tomcatThreadPool"
              port="8080" protocol="HTTP/1.1"
               maxKeepAliveRequests="1"
              connectionTimeout="20000"
              redirectPort="8443" />
重啟tomcat,觀察,問題依舊!

翻頁查看lsof
lsof|grep tomcat|more

發現有大量的memcached的IO,因此把問題定位在memcached上,為了方便觀察,新建memcached實例,發現memcached的IO數量逐步增加,最終導致tomcat崩潰!
嗨,終於找到問題了 ^O^

通過code review,發現每次調用memcached的API,都會new一個XMemcachedClient(一個memcached客戶端),每次new的時候都會設置一個尺寸的連接池,而連接是預建立的,因此導致memcached的IO數目劇增。

問題終於解決了,memcached的IO數目穩定了,tomcat也運行良好。。。

不經意間,發現memcached的IO數目以連接池的尺寸在遞增,心裡一落千丈,拔涼拔涼的,貌似還是沒解決!

查看tomcat的日志,發現了端倪:
Jul 21, 2015 3:06:40 PM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [/MobileService] appears to have started a thread named [Xmemcached-Reactor-0] but has failed to stop it. This is very likely to create a memory leak. 
原來在每次重新部署webapp時,tomcat殺不掉XmemcachedClient的連接進程,由於使用的是spring,所以需要在memcachedClient的bean裡增加"destroy-method"。
OK,到此為止tomcat的IO以及memcached的IO都穩定了,並在合理范圍之內,tomcat的是160左右。
為了擔心“maxKeepAliveRequests”的設置對tomcat的性能有影響,暫時刪除了“ maxKeepAliveRequests”,需要對tomcat的整體性能優化進行了解才去配置。
小插曲:由於使用了spring框架的監聽配置Log4j,如果下面的順序顛倒了會造成不能寫日志的問題
<listener>
	<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
經過一天的推理、懷疑、實驗、否定,終於搞定,又過了一次"福爾摩斯"的偵探瘾,作此標記,留給後來人!

今天發現打開的IO仍然在增加,雖然幅度比較少,但最高也達到了960+,因此需要優化Tomcat配置,增加線程池,並配置keepAliveTimeout和maxKeepAliveRequests
<Executor name="mobileThreadPool" namePrefix="catalina-exec-"
maxThreads="600" minSpareThreads="20" maxIdleTime="60000" />
 
<Connector executor="mobileThreadPool" port="8080" protocol="HTTP/1.1"
	connectionTimeout="20000"
	keepAliveTimeout="15000"
	maxKeepAliveRequests="1"
	URIEncoding="UTF-8"
	redirectPort="8443" />

今天使用壓力測試,依然出現“Too many open files”,使用ulimit發現“open files (-n)”仍然是1024,那麼之前設置的65535是沒起作用,而切換到root用戶發現卻是65535;而在非root用戶下
ulimit -n 65535
會報異常:
ulimit: max user processes: cannot modify limit
原來是被 /etc/security/limits.conf 限制了,打開此文件即可看到,對默認用戶是有限制的,因此可以加入
*        soft    noproc 65535
*        hard    noproc 65535
*        soft    nofile 65535
*        hard    nofile 65535

這樣就非root用戶就可以設置ulimit為65535了。

設置了ulimit後,我在root下啟動tomcat,壓力還會容易出現“Too many open files”,但其實使用“lsof -u root|wc -l”並不多,才3000+;root後來我單獨建立了個用戶來啟動,暫時沒出現問題。

PS:

/proc/sys/fs/file-max表示kernel中維護的最大文件描述符數目。不管這個系統上有多少了用戶登錄,有多少個進程在運行,所有打開的文件數目總合都不能超過這個數字。

/etc/security/limit.conf用來設置每個用戶最多可以打開的文件數目。

普通用戶可以通過ulimit -n 命令來設置hard limit(只能改小) 和soft limit(可以改大和改小).

把上面的內容概括成3條就是:

  1. /proc/sys/fs/file-max 控制整個系統。
  2. /etc/security/limit.conf控制每個用戶, 且受到(1)的限制。
  3. ulimit -n用來控制shell及其子進程, 且受到(2)的限制。
Copyright © Linux教程網 All Rights Reserved