問題描述:
? ? ? ? 服務調用遠程rest接口 報錯,發生too many open files 異常,系統句柄資源耗盡,導致服務不可用。
??
排查經過:
? ? ? ? 1、針對報錯代碼進行本地構建,構造異常,并進行壓測。問題未復現
? ? ? ? 2、經過討論分析,問題發生根因為:句柄資源耗盡,那么必然存在進程持續消耗句柄資源,且不進行釋放。
? ? ? ? 3、因此,開始排查服務進程句柄信息?
# 統計進程句柄總數
lsof -p <PID> | wc -l?
# 統計進程目錄句柄總數 若持續增加則存在泄漏
lsof -p <PID> | grep DIR??
# 統計進程delete文件刪除句柄總數 若持續增加則存在泄漏
lsof -p <PID> | grep delete
?通過統計觀察各個進程的句柄總數,分析查找到問題根源
原因最終確認:
問題代碼:loadDirectory函數加載指定目錄下所有文件,并返回Path列表。Files.list函數為安全關閉文件句柄,導致該方法頻繁調用時未釋放目錄句柄資源。導致系統句柄資源耗盡
public static List<Path> loadDirectory(String fileDirectory) {try {final Path pathDirectory = Path.of(fileDirectory);if (Files.isDirectory(pathDirectory)) {return Files.list(pathDirectory).collect(Collectors.toList());} else {throw new NotDirectoryException(fileDirectory);}} catch (IOException e) {log.error("file write error:", e);}return new ArrayList<>(0);}
問題修復 :使用try-resource-with 方式主動關閉資源
public static List<Path> loadDirectory(String fileDirectory) throws CheckingException {try (Stream<Path> stream = Files.list(Paths.get(fileDirectory))) {return stream.collect(Collectors.toList());} catch (IOException e) {log.error("fileDirectory is not directory:", e);throw new CheckingException("can not load directory:" + fileDirectory);}}
問題復盤:創建系統句柄監控,進程句柄如果超過300,則進行告警,超過1000,則提示高危進程
#!/bin/bash
# 高效進程句柄監控腳本 v2.0
THRESHOLD=300 # 單個進程句柄閾值
HIGH_THRESHOLD=2000 # 高危進程閾值
INTERVAL=20 # 監控間隔(秒)
LOG_DIR="logs"
LOG_FILE="${LOG_DIR}/handle_monitor_$(date +%Y%m%d).log"
MAX_LOG_DAYS=30 # 日志保留天數# 創建日志目錄
mkdir -p ${LOG_DIR}# 清理舊日志
find ${LOG_DIR} -name "handle_monitor_*.log" -mtime +${MAX_LOG_DAYS} -delete# 日志函數
log() {echo "$(date "+%Y-%m-%d %H:%M:%S") $1" >> ${LOG_FILE}
}# 獲取進程FD數量的高效方法
get_fd_count() {local pid=$1if [ -d /proc/$pid/fd ]; thenecho $(ls -1 /proc/$pid/fd 2>/dev/null | wc -l)elseecho 0fi
}# 主監控循環
while true; dolog "===== 開始監控 ====="total_warning=0# 高效獲取所有PIDpids=$(find /proc -maxdepth 1 -type d -name '[0-9]*' -printf "%f\n" 2>/dev/null)# 遍歷PIDfor pid in $pids; dofd_count=$(get_fd_count $pid)if [[ $fd_count -ge $THRESHOLD ]]; thenproc_name=$(ps -p $pid -o comm= 2>/dev/null || echo "未知進程")log "警告: PID $pid ($proc_name) - $fd_count 個文件描述符"((total_warning++))# 高危進程詳細記錄if [[ $fd_count -ge $HIGH_THRESHOLD ]]; thenlog "高危進程詳情:"lsof -p $pid +c 0 2>/dev/null | head -50 >> ${LOG_FILE}log "(僅顯示前50個FD,完整列表請單獨檢查)"fifidone# 系統級統計sys_stats=$(cat /proc/sys/fs/file-nr 2>/dev/null)log "系統FD統計: ${sys_stats:-無法獲取}"log "警告進程總數: $total_warning"log "===== 監控結束 ====="sleep $INTERVAL
done
? ? ? ?使用說明
使用說明:
- 保存為?
fd_monitor.sh
- 賦予執行權限:
chmod +x fd_monitor.sh
- 后臺運行:
nohup ./fd_monitor.sh &
- 查看日志:
tail -f logs/handle_monitor_xxx.log