1. 程式人生 > >記一次由 echo '' > 引發的問題

記一次由 echo '' > 引發的問題

本篇文章大概閱讀時間2分鐘

最近公司生產環境伺服器經常記憶體報警,而且多發生在凌晨,見下圖。

記憶體報警

由於一直報警,因此寫了一個記憶體監控的指令碼,見上篇文章。最終發現是由於filebeat導致,懷疑是由於json相關配置導致,然而發現並非如此。那我們書接前文。

查了下網上關於filebeat記憶體暴增的資料,發現這樣一篇文章:filebeat 實踐 - 記憶體佔用 - 最大記憶體佔用.

文章闡述了filebeat記憶體暴增的原因,記憶體公式為:

$$ bytes_each_log * spool_size * M + a * N $$

bytes_each_log是指單條日誌大小,spool_size是配置檔案裡的配置項,M是單條日誌在記憶體裡的溢價係數(> 1),N表示採集檔案的個數,a為常數

spool_size的預設值是2048,我們的配置檔案裡也沒設定。具體比照見下表:

10MB 為 filebeat 支援的單條日誌最大長度,超過的將會被截斷丟棄

因此如果有單條日誌過大的情況,記憶體會突增。所以要設定合適的spool_size值,建議是128或者256.

你以為這樣就結束了嗎,我們還要深挖原因,為什麼會有單條過大的日誌。這個服務平時壓力並不大,日誌量也很小,不應該出現這種情況。我們查詢日誌發現有很多二進位制字元,如下圖:

日誌中異常字元

日誌檔案是stdout.log,這個檔案是記錄程式執行的所有控制檯輸出,命令如下:

nohup java ${JAVA_OPTS} -cp ${CLASS_PATH} -jar ${DEFAULT_JAR} --spring.profiles.active=${SERVER_ENVIROMENT} ${APP_NAME}-app >$LOG_PATH/stdout.log 2>&1 &

由於擔心時間長檔案無限制增長,就定了個計劃任務,每隔兩天清空檔案,命令如下:

# clean stdout log
20 02 */2 * * echo "" > /data0/logs/xxxx/stdout.log

正常情況下程式不會輸出這樣的內容。而且用file命令看檔案已經變為了二進位制檔案,並未文字檔案。如下圖:

檔案格式

我們繼續檢查為什麼會出現這種情況,懷疑是由於 echo 清理導致,這時網上發現如下資料:Oh!MongoDB 日誌從文字穿越成了圖片?咋整! 裡面的碰到的情況跟我們類似,檔案變為 PCX ver. 2.5 image data 。文章提到:

  • 使用 echo > log
    時,會往檔案頭部插入 \n 即16進位制的 0a
  • 在程式正常執行時,log檔案是加了鎖的,因此強制執行 echo > log 是無法覆蓋的,會將所有資料置為0
  • 因此檔案頭部變成了無數的空白

那麼如何解決呢,暫時沒找到好辦法,除非應用重啟切割日誌,好在這個日誌只是控制檯輸出,其他日誌中