tomcat假死原因
以前遇到tomcat莫名奇妙的假死了,沒有任何的響應,然後重啟後又可以了,隔段時間又假死了。以前不懂的處理和排除原因,糾結的半死。無從入手,都想砸電腦,小夥伴們有遇到過,我遇到過4次。
其實tomcat假死引起的原因有很多,要具體分析一下和排查一下。
tomcat假死有以下幾種可能的原因:
- redis的連接池資源沒釋放掉(tcp沒釋放掉,tcp狀態為close_wait)
- 數據庫連接池資源沒釋放掉(tcp沒釋放掉,tcp狀態為close_wait)
- 上傳文件資源沒關閉掉 (tcp沒釋放掉,tcp狀態為close_wait)
- httpclient請求沒關閉掉 (tcp沒釋放掉,tcp狀態為close_wait)
- 線程死鎖
- 線程被阻塞了,沒繼續往下執行
tomcat的假死的原因有很多,很多是由於tcp沒有釋放掉。具體怎麼排查原因,下面會介紹一下。
案例1
順便說我遇到的一個坑,我曾經部署專門做定時任務項目,這個是用spring的quartz做的。發現定時任務執行一段時間後,居然沒響應了,tomcat沒掛掉,cpu和內存也正常。就是定時任務不執行。查看了線程,也沒有死鎖。後面查了很久,居然quartz的定時任務調度居然開啟的是單線程的。由於我其中一個定時任務要執行消息寫入數據庫,這個量非常大,而且服務器配置比較差,寫入比較慢。所以導致這個任務要執行差不多1天,導致其他定時任務不執行,所以還以為tomcat掛掉了。所以有時候要排查線程數是否足夠。
案例2
當服務器掛掉的時候,查看一下日誌看能不能查出問題。日誌查看不出來的時候,查看運行時候cpu和內存的波動,如果cpu和內存波動很大,就去查看堆棧信息,看哪個線程佔用的cpu和內存比較高。dump線程信息,查看一下具體代碼哪個位置引起的。
查看堆棧信息可以使用以下工具
- jdk自帶的console.exe (window)
- jdk自帶的jvisualvm.exe (window)
- jstack(linux)
- alibaba/arthas 阿里巴巴的插件,挺好用的,推薦使用,還可以反編譯源碼
首先查看cpu和內存
用jvisualvm(也可以遠程連接到linux上面,改天我寫個文章)
如果是linux系統的話,可以用top -c 查看cpu和內存
top -c
查看線程信息
linux
jstack java的進程id
如果要查看有沒有線程死鎖,你可以按照下面命令做
jstack java的進程id >1.txt
然後在1.txt文件查找一下有沒有DeadThread關鍵詞。沒有就是沒死鎖
jconsole比較簡單
當cpu和內存劇增的時候,可以查看是哪幾個線程引起的,定位到這個線程。查看線程名稱,基本上可以定位到問題。
top c
可看出PID為7149的java進程佔用cpu最高,達到了98%
查看進程中最耗cpu的子線程
top -p 7149 -H
如下圖:可看出PID為7166的線程佔用cpu最高,達到了97.7%
將最耗cpu的線程id轉換為16進制輸出
printf "%x \\n" 7166
查詢具體出現問題的代碼位置
jstack 7149 | grep 1bfe -A 30
如下圖:可看出是JVMLearnApplication類的第18行出現問題
我曾經遇到過一個坑,就是activemq的廣播消息太多,然後程序用線程池消費,線程池線程數配置太小,消費速度跟不上,就會堆到線程池隊列中排隊,內存劇增,cpu最後也跟這劇增。導致tomcat掛掉。最後把線程池的線程數配置大點,後面就正常了。
案例3
當cpu和內存正常的時候,線程也沒有死鎖,tomcat也沒有死掉,就是訪問不了,全部訪問狀態502超時。這個是怎麼回事呢。別急,下面介紹怎麼解決。
當出現這種情況的時候,先去查一下tcp連接情況,
Docker容器中安裝netstat命令,如果沒有netstat命令
apt-get update
apt-get install net-tools
linux查看方式如下:
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
如果出現大量的close_wait的狀態。那表示tcp沒有正確釋放。
定位到是tcp問題了,那接下來怎麼辦呢
http請求進來,都是502超時響應,那就表示處理http請求的線程都阻塞了,查看具體哪行代碼阻塞就行了。
查找線程http-nio-28001-exec這個些線程的線程信息,dump下來,看具體是請求什麼阻塞了。
如果是請求redis線程池資源阻塞了,那看一下redis資源為什麼沒釋放,或者什麼不夠用了。查看一下最近代碼都對redis進行什麼操作。
數據庫和上傳文件和httpclient請求沒關閉掉 都是跟redis一樣的道理。
我之前遇到的坑就是redis引起的。我一個同事用redisTemplate.getConnect()的到一個管道連接,然後在連接裡面有for循環調用redisTempate.get()方法,這樣導致了redis連接池不夠用。管道沒釋放。然後http請求都是用經過權限校驗的,是shiro+redis做的。所有請求經過redis的時候就阻塞了。所有tomcat假死了。
總之tomcat假死大概就這些原因,還有需要注意點,有寫批量操作,最好都用同個管道,不要每個都請求一個資源,這樣會導致資源不夠用,tcp都是close_wait狀態。
改天介紹一下alibaba/arthas的這個插件怎麼使用,真的非常方便
閱讀更多 java小老頭 的文章