記一次線程爆滿導(dǎo)致服務(wù)器崩潰的問題排查及解決
問題介紹
測試服務(wù)器突然無法連接,ssh登錄不上。只有重啟才能解決。重啟一天后,又連接不上了。
于是有了下面的排查過程,最終發(fā)現(xiàn)是有個(gè)java程序一直在創(chuàng)建線程,導(dǎo)致線程達(dá)到服務(wù)器最大數(shù)量,服務(wù)器崩潰。
1. 重啟服務(wù)器
重啟后,ssh連接發(fā)現(xiàn)下面問題
fork faild:Cannot allocate memory

以為是內(nèi)存滿了
于是,free -h,查看內(nèi)存情況,還有,觀察一段時(shí)間后,內(nèi)存沒多大變化

2. 修改最大線程數(shù)
經(jīng)過各種百度,都說可以通過修改服務(wù)器的最大線程數(shù)來解決,于是我也這么干了。當(dāng)時(shí)做的時(shí)候沒有截圖,所以下面截圖是網(wǎng)上找的,湊合看看。
查看最大進(jìn)程數(shù) sysctl kernel.pid_max

ps -eLf | wc -l查看 進(jìn) 程數(shù)

修改最大 進(jìn) 程數(shù)后系統(tǒng)恢復(fù)
echo 1000000 > /proc/sys/kernel/pid_max
永久生效
echo "kernel.pid_max=1000000 " >> /etc/sysctl.conf sysctl -p
3. 查找線程最大的java程序
上一步擴(kuò)大了線程數(shù)量后,感覺有點(diǎn)不對(duì),因?yàn)橹皼]有這么配置都可以正常運(yùn)行,為什么突然服務(wù)器掛了呢?肯定是有程序在作怪。
于是決定找出占用線程最多的程序?;仡欁罱鼛滋?,服務(wù)器中只部署了幾個(gè)springboot程序。問題一定出在它們之中。
查看線程數(shù)量前20的java程序
ps -Lef |awk ‘{sum[$2]++}END{for(pid in sum) print pid, sum[pid]}'|sort -nr -k 2|head -n 20[root@se-test-lky01 ~]# ps -Lef |awk '{sum[$2]++}END{for(pid in sum) print pid, sum[pid]}'|sort -nr -k 2|head -n 20
16074 3100
31386 1226
20120 1072
19548 985
9697 829
3005 796
641 344
19016 324
16924 315
17870 300
6417 293
8351 171
7332 168
18259 167
19821 161
16311 157
18433 151
18048 136
14347 104
2559 100觀察一段時(shí)間后,發(fā)現(xiàn)進(jìn)程id為16074的java程序的線程數(shù)不斷增長。
4. 導(dǎo)出問題程序的線程日志
[root@se-test-lky01 ~]#jstack 16074 >thread_dump.log
分析日志,發(fā)現(xiàn)下面情況,線程數(shù)量不斷增加,代碼位置在FtpMonitorProcess.java:85
"Thread-4655" #4774 prio=5 os_prio=0 tid=0x00007f84aa2fe000 nid=0xd408b waiting for monitor entry [0x00007f802b704000] java.lang.Thread.State: BLOCKED (on object monitor) at cn.cloudwalk.bat.util.http.FtpUtil.connect(FtpUtil.java:246) - waiting to lock <0x00000006c09c1888> (a java.lang.Class for cn.cloudwalk.bat.util.http.FtpUtil) at cn.cloudwalk.bat.schedule.ftp.process.FtpMonitorProcess$1.run(FtpMonitorProcess.java:85) at java.lang.Thread.run(Thread.java:748) "Thread-4654" #4773 prio=5 os_prio=0 tid=0x00007f84aa2fc000 nid=0xd408a waiting for monitor entry [0x00007f802b805000] java.lang.Thread.State: BLOCKED (on object monitor) at cn.cloudwalk.bat.util.http.FtpUtil.connect(FtpUtil.java:246) - waiting to lock <0x00000006c09c1888> (a java.lang.Class for cn.cloudwalk.bat.util.http.FtpUtil) at cn.cloudwalk.bat.schedule.ftp.process.FtpMonitorProcess$2.run(FtpMonitorProcess.java:114) at java.lang.Thread.run(Thread.java:748)
5. 找到問題代碼
發(fā)現(xiàn)這個(gè)方法每次被調(diào)用就會(huì)創(chuàng)建一個(gè)新的線程。而這個(gè)方法是被定時(shí)任務(wù)調(diào)用的,每10秒調(diào)用一次。
問題就出在ftp沒有配置,所以線程內(nèi)執(zhí)行ftp操作時(shí),線程阻塞,沒能釋放。若ftp可用,則不會(huì)出現(xiàn)線程阻塞問題。
這就是問題根源。
private void listDeviceFiles() {
new Thread(new Runnable() {
@Override
public void run() {
logger.debug("開始獲取[ftp-設(shè)備]文件...");
try {
String workDir = ftpConfig.getWorkdir();
// 連接
FTPClient ftpClient = FtpUtil.connect(ftpConfig);
ftpClient.changeWorkingDirectory(workDir);
ftpClient.changeWorkingDirectory(SubscribeDataTypeEnum.DEVICE_INFO.getKey().toString());
FTPFile[] files = ftpClient.listFiles();
for(FTPFile file : files) {
decomposeFile(file,ftpClient);
}
ftpClient.logout();
} catch (Exception e) {
logger.error("ftp獲取文件名出錯(cuò):" + e.getMessage());
}
}
}).start();
}6. 解決方案
不建議手動(dòng)創(chuàng)建線程,改用使用線程池。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot Java后端實(shí)現(xiàn)okhttp3超時(shí)設(shè)置的方法實(shí)例
Okhttp的使用沒有httpClient廣泛,網(wǎng)上關(guān)于Okhttp設(shè)置代理的方法很少,下面這篇文章主要給大家介紹了關(guān)于SpringBoot Java后端實(shí)現(xiàn)okhttp3超時(shí)設(shè)置的相關(guān)資料,需要的朋友可以參考下2021-10-10
Java實(shí)戰(zhàn)之醫(yī)院管理系統(tǒng)的實(shí)現(xiàn)
這篇文章主要介紹了如何利用Java實(shí)現(xiàn)醫(yī)院管理系統(tǒng),文中用到的技術(shù)有:SpringBoot、Layui、Freemaker等,感興趣的同學(xué)可以了解一下2022-04-04
為什么SpringMVC中請(qǐng)求的body不支持多次讀取
這篇文章主要介紹了為什么SpringMVC中請(qǐng)求的body不支持多次讀取,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12

