golang 程序出現too many open files的排查過程
1. 現象
服務的cpu跑滿(golang實現), 並大量報too many open files錯誤.服務使用systemd來執行,部署在阿里ecs上.
2.分析
從日誌來看,cpu的上升主要為到達檔案數限制引起的,但之前已經更改過系統的檔案數及所有使用者的檔案數,按道理是不應該出現這個問題的,後來查閱資料發現,檔案數可以從三個維度限制分別為作業系統限制,使用者態限制,以及程序限制,對於這三個維度選取最小值生效.於是對系統進行分析.
首先檢視當前開啟檔案數, 程序佔用的檔案數並不多.
lsof -n|awk '{print $2}'|sort|uniq -c|sort -nr|more
然後獲取系統級檔案數限制
輸入命令
cat /etc/sysctl.conf
得到
fs.file-max = 1000000
查詢使用者級檔案數限制
cat /etc/security/limits.conf
得到
* soft nofile 655350
* hard nofile 655350
單獨獲取程式檔案數限制(9928為程序id)
cat /proc/9928/limits
得到
Max open files 1024 4096 files
如上可以發現, 雖然系統及使用者的檔案數調大了,但是程式的仍然是一個很低的值, 這裡程序的檔案數一般情況下是預設繼承使用者級的值的,而這裡卻沒有繼承,一開始懷疑是systemd啟動的問題,但是手寫了另外一個測試服務,發現該服務又繼承了使用者檔案數.
百思不得其解的情況下在systemd的啟動腳本里加了檔案數的初始化值.
如下:
[Service]
Type=simple
LimitNOFILE=40960
LimitNPROC=40960
單獨獲取程式(9928為程序id)
cat /proc/9928/limits
得到
Max open files 40960 40960 files
發現檔案數被設定成了啟動時的初始化值.至於為什麼沒有繼承使用者級的值,懷疑是程式裡做了引數設定,這裡如果有人知道golang裡具體情況的話,還望不吝賜教.
3. 總結
歸結來說出現檔案描述符的錯誤的排查步驟如下:
首先,判斷配置引數是否正確,這裡涉及到對上面提到的三個維度的檢查,特別時程序維度的,如果只是ulimit -n 一下就完事了,那估計舊要像我一樣進坑了.
如果引數都正確,那麼檢視一下當前系統被使用了多少檔案數,如果使用的確實多,那要看一下使用在什麼地方,這裡一般有兩種情況,大量連線未關閉,或者大量讀檔案的控制代碼未關閉.具體原因相信到這裡就可以排查出來了.