文|Junlong Liu
Shopee Digital Purchase & Local Services Engineering
本文1743字 閱讀 6分鐘
貢獻者前言
我是在開發工作過程中瞭解到 Holmes 的,為了保障系統穩定性需要一個效能排查工具,因此也需要一個保留現場的效能監控工具。當我在網上查詢該方面的開源庫時,發現可用的並不多。後續找到 MOSN 社群的 Holmes ,發現這個開源庫功能基本齊全、擴充套件性也高,特別是 GCHeapDump 這個業界領先的功能,對解決記憶體升高的問題十分有用。
2021 年年末了解到的 Holmes 元件,然後開始瞭解 Holmes 所在的 MOSN 社群。Holmes 作為效能排查工具,核心功能是及時發現效能指標異常,並對系統進行 Profiling。
由於 Holmes 還處於萌芽期,除了 Readme 之外的文件資料並不多。還有一些 Holmes 當時不支援的功能,比如動態配置調整與上報。Holmes 當時也還沒釋出第一個版本,但是自己對這方面也有興趣和理解,於是在 GitHub 上提了幾個 Issue 討論,社群回覆的速度十分快。後續在社群前輩們的指導下提了 PR,也因此通過 Holmes 的程式碼設計學習到了很多關於開源元件的設計理念。
因此我決定參與開源社群並貢獻程式碼,以解決實際需求。有了一定的瞭解和經驗之後,通過和人德前輩討論,總結這樣一篇分享文章。
本文將介紹 Holmes 的使用場景、快速開始案例、多個監控型別、設計原理、擴充套件功能與如何藉助 Holmes 搭建起一套簡單的效能排查系統,歡迎大家留言指導。
Holmes 使用場景
對於系統的效能尖刺問題,我們通常使用 Go 官方內建的 pprof 包進行分析,但是難點是對於一閃而過的“尖刺”,開發人員很難及時儲存現場:當你收到告警資訊,從被窩中爬起來,開啟電腦連結 VPN,系統說不定都已經重啟三四趟了。
MOSN 社群的 Holmes 是一個基於 Golang 實現的輕量級效能監控系統,當應用的效能指標發生了異常波動時,Holmes 會在第一時間保留現場,讓你第二天上班可以一邊從容地喝著枸杞茶,一邊追查問題的根因。
Quick Start
使用 Holmes 的方式十分簡單,只需要在您的系統初始化邏輯內新增以下程式碼:
// 配置規則
h, _ := holmes.New(
holmes.WithCollectInterval("5s"), // 指標採集時間間隔
holmes.WithDumpPath("/tmp"), // profile儲存路徑
holmes.WithCPUDump(10, 25, 80, 2 * time.Minute), // 配置CPU的效能監控規則
holmes.WithMemDump(30, 25, 80, 2 * time.Minute),// 配置Heap Memory 效能監控規則
holmes.WithGCHeapDump(10, 20, 40, 2 * time.Minute), // 配置基於GC週期的Heap Memory 效能監控規則
holmes.WithGoroutineDump(500, 25, 20000, 100*1000, 2 * time.Minute), //配置Goroutine數量的監控規則
)
// enable all
h.EnableCPUDump().
EnableGoroutineDump().
EnableMemDump().
EnableGCHeapDump().Start()
類似於 holmes.WithGoroutineDump(min, diff, abs,max,2 * time.Minute) 的 API 含義為:
當 Goroutine 指標滿足以下條件時,將會觸發 Dump 操作。
當 Goroutine 數大於 Max 時,Holmes 會跳過本次 Dump 操作,因為當 Goroutine 數過大時,Goroutine Dump 操作成本很高。
2 * time.Minute 是兩次 Dump 操作之間最小時間間隔,避免頻繁 Profiling 對效能產生的影響。
更多使用案例見文末的 Holmes 使用案例文件。
Profile Types
Holmes 支援以下五種 Profile 型別,使用者可以按需配置。
Mem: 記憶體分配
CPU: CPU 使用率
Thread: 執行緒數
Goroutine: 協程數
GCHeap: 基於 GC 週期監控的記憶體分配
指標採集
Mem、CPU、Thread、Goroutine 這四種型別是根據使用者配置的 CollectInterval,每隔一段時間採集一次應用當前的效能指標,而 gcHeap 時基於 GC 週期採集效能指標。
本小節會分析一下兩種指標。
根據 CollectInterval 週期採集
Holmes 每隔一段時間採集應用各項指標,並使用一個固定大小的迴圈連結串列來儲存它們。
根據 GC 週期採集
在一些場景下,我們無法通過定時的 memory dump 保留到現場。比如應用在一個 CollectInterval 週期內分配了大量記憶體,又快速回收了它們。此時 Holmes 在週期前後的採集到記憶體使用率沒有產生過大波動,與實際情況不符。
為了解決這種情況,Holmes 開發了基於 GC 週期的 Profile 型別,它會在堆記憶體使用率飆高的前後兩個 GC 週期內各 Dump 一次 Profile,然後開發人員可以使用 pprof --base 命令去對比兩個時刻堆記憶體之間的差異。
根據 GC 週期採集到的資料也會放在迴圈列表中。
規則判斷
本小節介紹 Holmes 是如何根據規則判斷系統出現異常的。
閾值含義
每個 Profile 都可以配置 min、diff、abs、coolDown 四個指標,含義如下:
當前指標小於 min 時,不視為異常。
當前指標大於 (100+diff)100% 歷史指標,說明系統此時產生了波動,視為異常。
當前指標大於 abs (絕對值)時,視為異常。
CPU 和 Goroutine 這兩個 Profile 型別提供 Max 引數配置,基於以下考慮:
CPU 的 Profiling 操作大約會有 5% 的效能損耗,所以當在 CPU 過高時,不應當進行 Profiling 操作,否則會拖垮系統。
當 Goroutine 數過大時,Goroutine Dump 操作成本很高,會進行 STW 操作,從而拖垮系統。(詳情見文末參考文章)
Warming up
當 Holmes 啟動時,會根據 CollectInterval 週期採集十次各項指標,在這期間內採集到的指標只會存入迴圈連結串列中,不會進行規則判斷。
擴充套件功能
除了基本的監控之外,Holmes 還提供了一些擴充套件功能:
事件上報
您可以通過實現 Reporter 來實現以下功能:
傳送告警資訊,當 Holmes 觸發 Dump 操作時。
將 Profiles 上傳到其他地方,以防例項被銷燬,從而導致 Profile 丟失,或進行分析。
type ReporterImpl struct{}
func (r *ReporterImple) Report(pType string, buf []byte, reason string, eventID string) error{
// do something
}
......
r := &ReporterImpl{} // a implement of holmes.ProfileReporter Interface.
h, _ := holmes.New(
holmes.WithProfileReporter(reporter),
holmes.WithDumpPath("/tmp"),
holmes.WithLogger(holmes.NewFileLog("/tmp/holmes.log", mlog.INFO)),
holmes.WithBinaryDump(),
holmes.WithMemoryLimit(100*1024*1024), // 100MB
holmes.WithGCHeapDump(10, 20, 40, time.Minute),
)
動態配置
您可以通過 Set 方法在應用執行時更新 Holmes 的配置。它的使用十分簡單,和初始化時的 New 方法一樣。
有些配置時不支援動態更改的,比如 Core 數。如果在系統執行期間更改這個引數,會導致 CPU 使用率產生巨大波動,從而觸發 Dump 操作。
h.Set(
WithCollectInterval("2s"),
WithGoroutineDump(10, 10, 50, 90, time.Minute))
落地案例
利用 Holmes 的 Set 方法,可以輕鬆地對接自己公司的配置中心,比如,將 Holmes 作為資料面,配置中心作為控制面。並對接告警系統(郵件/簡訊等)搭建一套簡單的監控系統。
具體架構如下:
Holmes V1.0 版本釋出
本文簡單地介紹了 Holmes 的使用方法與原理。希望 Holmes 能在您提高應用的穩定性時幫助到你。
Holmes V1.0 在幾周前正式釋出了,作為貢獻者和使用者,我十分推薦大家試用這個小巧的工具庫,有任何問題和疑問歡迎大家來社群提問~
Holmes 是 MOSN 社群開源的 GO 語言 Continous Profiling 元件,可以自動發現 CPU、 Memory、Goroutine 等資源的異常,並自動 Dump 異常現場 Profile,用於事後分析定位。也支援上傳 Profile 到自動分析平臺,實現自動問題診斷、報警。
「釋出報告」:https://github.com/mosn/holmes/releases/tag/v1.0.0
「Holmes 原理介紹」:https://mosn.io/blog/posts/mosn-holmes-design/
本文簡單地介紹了 Holmes 的使用方法與原理。希望 Holmes 能在您提高應用的穩定性時幫助到你。
「參考資料」
[1]《Holmes 文件》https://github.com/mosn/holmes
[2]《無人值守的自動 dump(一)》https://xargin.com/autodumper-for-go/
[3]《無人值守的自動 dump(二)》https://xargin.com/autodumper-for-go-ii/
[4]《go 語言 pprof heap profile 實現機制》https://uncledou.site/2022/go-pprof-heap/
[5]《goroutines pprofiling STW》https://github.com/golang/go/issues/33250
[6]《Holmes 使用案例文件》https://github.com/mosn/holmes/tree/master/example
[7]《go pprof 效能損耗》https://medium.com/google-cloud/continuous-profiling-of-go-programs-96d4416af77b