Tomcat進程假死問題排查
1.網(wǎng)絡(luò)
1.1 檢查nginx的網(wǎng)絡(luò)情況
更改nginx的配置,讓該臺nginx請求只轉(zhuǎn)到本機器的出現(xiàn)問題的tomcat應(yīng)用上面,在access.log里看是否有網(wǎng)絡(luò)請求,結(jié)果可以查看到當(dāng)前所有的網(wǎng)絡(luò)請求,也就是說可以排除是網(wǎng)絡(luò)的問題。
1.2 檢查tomcat的網(wǎng)絡(luò)情況
分析業(yè)務(wù)配置的tomcat訪問日志xxxx.log上是否有日志訪問記錄,經(jīng)過查詢該臺tomcat應(yīng)用日志完全沒有任何訪問記錄,由于我們的部署是本機的nginx轉(zhuǎn)到本機的tomcat應(yīng)用,所以可以排除不是網(wǎng)絡(luò)問題。 到此基本可以斷定網(wǎng)絡(luò)沒有問題,tomcat 本身出現(xiàn)了假死的情況。在tomcat的日志里有報過OutOfMemoryError的異常,所以可以肯定tomcat假死的原因是OOM
!
2.Jvm內(nèi)存溢出
2.1為什么會發(fā)生內(nèi)存泄漏
在我們學(xué)習(xí)Java的時候就知道它最為方便的地方就是我們不需要管理內(nèi)存的分配和釋放,一切由JVM自己來進行處理,當(dāng)Java對象不再被應(yīng)用時,等到堆內(nèi)存不夠用時JVM會進行GC處理, 清除這些對象占用的堆內(nèi)存空間,但是如果對象一直被應(yīng)用,那么JVM是無法對其進行GC處理的,那么我們創(chuàng)建新的對象時,JVM就沒有辦法從堆中獲取足夠的內(nèi)存分配給此對象,這時就會導(dǎo)致OOM。 我們出現(xiàn)OOM原因,一般都是因為我們不斷的往容器里存放對象,然而容器沒有相應(yīng)的大小限制或清除機制,這樣就容易導(dǎo)致OOM。
2.2快速定位問題
當(dāng)我們的應(yīng)用服務(wù)器占用了過多內(nèi)存的時候,我們怎么樣才能快速的定位問題呢?要想快速定位問題,首先我們必需獲取服務(wù)器JVM某時刻的內(nèi)存快照。 Jdk里面提供了很多相應(yīng)的命令比如:jstack,jstat,jmap,jps等等. 在出現(xiàn)問題后我們應(yīng)該快速保留現(xiàn)場。
2.3 jstack查看tomcat是否出現(xiàn)死鎖
可以觀察到j(luò)vm中當(dāng)前所有線程的運行情況和線程當(dāng)前狀態(tài).
sudo jstack -F 進程ID
輸出內(nèi)容如下: 從上面的圖我們可以看到tomcat進程里面沒有死鎖的情況,而且每個線程都處理等待的狀態(tài)。這個時候我們可以telnet命令連上tomcat的端口查看tomcat進程是否有任務(wù)回應(yīng)。這時發(fā)現(xiàn)tomcat沒有任何回應(yīng)可以證明tomcat應(yīng)用已沒有響應(yīng)處理假死狀態(tài)。
在thread dump中,要留意下面幾種狀態(tài) 死鎖, ? Deadlock(重點關(guān)注) 等待資源, ? Waiting on condition(重點關(guān)注) ? 等待獲取監(jiān)視器,Waiting on monitor entry(重點關(guān)注) ? 阻塞,Blocked(重點關(guān)注) ? 執(zhí)行中,Runnable ? 暫停,Suspended ? 對象等待中,Object.wait() 或 TIMED_WAITING ? 停止,Parked
2.4 jstat查看gc運行情況
2.5 jmap獲取內(nèi)存快照
Jdk自帶的jmap可以獲取內(nèi)在某一時刻的快照
命令:
jmap -dump:format=b,file=heap.bin file:保存路徑及文件名 pid:進程編號(windows通過任務(wù)管理器查看,linux通過ps aux查看)
dump文件可以通過MemoryAnalyzer分析查看,網(wǎng)址:http://www.eclipse.org/mat/,可以查看dump時對象數(shù)量,內(nèi)存占用,線程情況等。
3. jvm GC 時間過長,導(dǎo)致應(yīng)用暫停
查看gc.log回收時間,以下為例子:
7581088.402: [Full GC (System) 7581088.402: [CMS: 661091K->669762K(7340032K), 1.7206330 secs] 848607K->669762K(8238848K), [CMS Perm : 34999K->34976K(58372K)], 1.7209480 secs] [Times: user=1.72 sys=0.00, real=1.72 secs]
最近的一次full gc 顯示,也不應(yīng)該會暫停幾分鐘的情況,這種假死可能可以排除。
4. load 太高,已經(jīng)超出服務(wù)的極限
使用top 命令查看資源使用情況,都在合理范圍,排除。
5. 大量tcp 連接 TIME_WAIT
Linux:
使用 ss -s 命令查看 tcp 鏈接狀態(tài), 發(fā)現(xiàn)TIME_WAIT 1800+, 有點高,需要修改。
打開 sysctl.conf 文件,修改以下幾個參數(shù):
[root@web01 ~]# vim /etc/sysctl.conf net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_timestamps = 1 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_fin_timeout = 30
開啟tcp_tw_reuse 和 tcp_tw_recycle 需要timestamps的支持,而且這些配置一般不建議開啟,但是對解決TIME_WAIT過多問題有效果。謹(jǐn)慎操作?。?!
然后又發(fā)現(xiàn),nginx 沒有開啟長連接。
當(dāng)使用nginx作為反向代理時,為了支持長連接,需要做到兩點:
- 從client到nginx的連接是長連接
- 從nginx到server的連接是長連接
Windows:
netstat -ano -p tcp netstat -ano | find "ESTABLISHED"
5.1、保持和client的長連接:
[root@web01 ~]# vim /etc/sysctl.conf net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_timestamps = 1 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_fin_timeout = 30
1)keepalive_timeout
語法:
keepalive_timeout timeout [header_timeout];
第一個參數(shù):設(shè)置keep-alive客戶端連接在服務(wù)器端保持開啟的超時值(默認75s);值為0會禁用keep-alive客戶端連接;第二個參數(shù):可選、在響應(yīng)的header域中設(shè)置一個值“Keep-Alive: timeout=time”;通??梢圆挥迷O(shè)置;
注:keepalive_timeout默認75s,一般情況下也夠用,對于一些請求比較大的內(nèi)部服務(wù)器通訊的場景,適當(dāng)加大為120s或者300s;
2)keepalive_requests:
keepalive_requests指令用于設(shè)置一個keep-alive連接上可以服務(wù)的請求的最大數(shù)量,當(dāng)最大請求數(shù)量達到時,連接被關(guān)閉。默認是100。這個參數(shù)的真實含義,是指一個keep alive建立之后,nginx就會為這個連接設(shè)置一個計數(shù)器,記錄這個keep alive的長連接上已經(jīng)接收并處理的客戶端請求的數(shù)量。如果達到這個參數(shù)設(shè)置的最大值時,則nginx會強行關(guān)閉這個長連接,逼迫客戶端不得不重新建立新的長連接。
大多數(shù)情況下當(dāng)QPS(每秒請求數(shù))不是很高時,默認值100湊合夠用。但是,對于一些QPS比較高(比如超過10000QPS,甚至達到30000,50000甚至更高) 的場景,默認的100就顯得太低。
簡單計算一下,QPS=10000時,客戶端每秒發(fā)送10000個請求(通常建立有多個長連接),每個連接只能最多跑100次請求,意味著平均每秒鐘就會有100個長連接因此被nginx關(guān)閉。同樣意味著為了保持QPS,客戶端不得不每秒中重新新建100個連接。因此,就會發(fā)現(xiàn)有大量的TIME_WAIT的socket連接(即使此時keep alive已經(jīng)在client和nginx之間生效)。因此對于QPS較高的場景,非常有必要加大這個參數(shù),以避免出現(xiàn)大量連接被生成再拋棄的情況,減少TIME_WAIT。
5.2、保持和server的長連接:
nginx訪問后端默認都是用的短連接(HTTP1.0)
為了讓nginx和后端server(nginx稱為upstream)之間保持長連接,location中有兩個參數(shù)需要設(shè)置:
http { server { location / { proxy_http_version 1.1; proxy_set_header Connection ""; } } }
5.3、 proxy_set_header 配置注意事項
在當(dāng)前級別的配置中沒有定義 proxy_set_header 指令時,這些指令從上級繼承。
如果當(dāng)前級別的配置中已經(jīng)定義了 proxy_set_header 指令,在上級中定義的proxy_set_header 指令在當(dāng)前級別都會失效。
舉個例子:
http { ... proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Connection ""; proxy_set_header X-Real-IP $remote_addr; upstream example.com_test { server 127.0.0.1:8080; keepalive 16; } server { server_name example.com; location ^~ /test/ { proxy_set_header test test; proxy_pass http://example.com_test; } } }
這里后端服務(wù)器不能從 Header 中獲取到 X-Real-IP。location ^~/test/ 中的proxy_set_header會覆蓋上面的配置。
正確的做法,在location 中重復(fù)配置一遍:
http { ... proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Connection ""; proxy_set_header X-Real-IP $remote_addr; upstream example.com_test { server 127.0.0.1:8080; keepalive 180; } server { server_name example.com; location ^~ /test/ { proxy_set_header test test; proxy_set_header Host $host; proxy_set_header Connection ""; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://example.com_test; } } }
6. tomcat長連接數(shù)超過最大連接數(shù)
發(fā)現(xiàn)tomcat 使用的是默認配置
tomcat默認最大連接數(shù)(線程數(shù))200個,默認每一個連接的生命周期2小時(7200秒),tomcat使用http 1.1協(xié)議,而http1.1默認是長連接。tomcat接受處理完請求后,socket沒有主動關(guān)閉,因此如果在2小時內(nèi),請求數(shù)超過200個,服務(wù)器就會出現(xiàn)上述假死現(xiàn)象。
解決辦法:
(1)檢查代碼,及時斷開socket
(2)修改tomcat配置文件,修改最大連接數(shù)(增大)
(3)修改linux的TCP超時時間(socket生命周期)限制
到此這篇關(guān)于Tomcat進程假死問題排查的文章就介紹到這了,更多相關(guān)Tomcat進程假死問題排查內(nèi)容請搜索本站以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持本站!
版權(quán)聲明:本站文章來源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。