11.29 springboot tomcat 掛掉,假死無響應原因總結和解決方案

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上面,改天我寫個文章)

springboot tomcat 掛掉,假死無響應原因總結和解決方案

如果是linux系統的話,可以用top -c 查看cpu和內存

top -c
springboot tomcat 掛掉,假死無響應原因總結和解決方案

查看線程信息

linux

jstack java的進程id


springboot tomcat 掛掉,假死無響應原因總結和解決方案

如果要查看有沒有線程死鎖,你可以按照下面命令做

jstack java的進程id >1.txt

然後在1.txt文件查找一下有沒有DeadThread關鍵詞。沒有就是沒死鎖


jconsole比較簡單

springboot tomcat 掛掉,假死無響應原因總結和解決方案

當cpu和內存劇增的時候,可以查看是哪幾個線程引起的,定位到這個線程。查看線程名稱,基本上可以定位到問題。

top c

可看出PID為7149的java進程佔用cpu最高,達到了98%

springboot tomcat 掛掉,假死無響應原因總結和解決方案


查看進程中最耗cpu的子線程

top -p 7149 -H

如下圖:可看出PID為7166的線程佔用cpu最高,達到了97.7%

springboot tomcat 掛掉,假死無響應原因總結和解決方案


將最耗cpu的線程id轉換為16進制輸出

 printf "%x \\n" 7166


springboot tomcat 掛掉,假死無響應原因總結和解決方案


查詢具體出現問題的代碼位置

jstack 7149 | grep 1bfe -A 30

如下圖:可看出是JVMLearnApplication類的第18行出現問題

springboot tomcat 掛掉,假死無響應原因總結和解決方案


我曾經遇到過一個坑,就是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]}'


springboot tomcat 掛掉,假死無響應原因總結和解決方案

如果出現大量的close_wait的狀態。那表示tcp沒有正確釋放。

定位到是tcp問題了,那接下來怎麼辦呢

http請求進來,都是502超時響應,那就表示處理http請求的線程都阻塞了,查看具體哪行代碼阻塞就行了。

查找線程http-nio-28001-exec這個些線程的線程信息,dump下來,看具體是請求什麼阻塞了。


springboot tomcat 掛掉,假死無響應原因總結和解決方案

如果是請求redis線程池資源阻塞了,那看一下redis資源為什麼沒釋放,或者什麼不夠用了。查看一下最近代碼都對redis進行什麼操作。

數據庫和上傳文件和httpclient請求沒關閉掉 都是跟redis一樣的道理。

我之前遇到的坑就是redis引起的。我一個同事用redisTemplate.getConnect()的到一個管道連接,然後在連接裡面有for循環調用redisTempate.get()方法,這樣導致了redis連接池不夠用。管道沒釋放。然後http請求都是用經過權限校驗的,是shiro+redis做的。所有請求經過redis的時候就阻塞了。所有tomcat假死了。

總之tomcat假死大概就這些原因,還有需要注意點,有寫批量操作,最好都用同個管道,不要每個都請求一個資源,這樣會導致資源不夠用,tcp都是close_wait狀態。

改天介紹一下alibaba/arthas的這個插件怎麼使用,真的非常方便


分享到:


相關文章: