基于规则的自动 Golang Profile Dumper——holmes
作为一名"懒惰"的程序员,如何避免在线上Go系统半夜宕机时起床保存现场呢?又或者如何dump压测时性能尖刺时刻的profile文件呢?
设计
holmes 每隔一段时间收集一次以下应用指标:
Dump Goroutine profile
Dump Goroutine profile
Dump Heap Memory Profile
Dump Heap Memory Profile
在预热阶段(应用启动后,holmes会收集十次指标)结束后,holmes 会比较当前指标是否满足用户所设置的阈值/规则,如果满足的话,则 dump profile, 以日志或者二进制文件的格式保留现场。
3、使用
go get mosn.io/holmes
监控 cpu、内存、Goroutine
func main() {
h := initHolmes(true, true, true)
// start the metrics collect and dump loop
h.Start()
// quit the application and stop the dumper
h.Stop()
}
func initHolmes(CpuEnable, MemEnable, GoroutineEnable bool) *holmes.Holmes {
h, _ := holmes.New(
holmes.WithCollectInterval("5s"), // 每5s采集一次当前应用的各项指标
holmes.WithDumpPath("/tmp"), // profile文件保存路径
)
if CpuEnable {
// 开启 cpu dump
// min最小30%,diff变化值大于40%,abs大于70%,coolDown 10min内不会重复dump
if err := h.Set(holmes.WithCPUDump(30, 40, 70, time.Duration(10)*time.Minute)); err == nil {
holmes.WithCPUMax(90) // cpu 的 profiling 操作大约会有5%的性能损耗, 所以当在cpu过高时(90),不应当进行profiling操作,否则会拖垮系统。
h.EnableCPUDump()
}
} else {
h.DisableCPUDump()
}
if MemEnable {
// 开启内存监控
if err := h.Set(holmes.WithMemDump(60, 50, 90, time.Duration(10)*time.Minute)); err == nil {
h.EnableMemDump()
}
} else {
h.DisableMemDump()
}
if GoroutineEnable {
// 开启Goroutine监控
// 当大于Max 时,会跳过dump操作,因为当goroutine number很大时, dump goroutine profile操作成本很高(STW && dump),有可能拖垮应用。当Max=0 时代表没有限制。
// current_goroutine_num > 10 && current_goroutine_num < 100*1000 || current_goroutine_num > 2000 才触发dump
if err := h.Set(holmes.WithGoroutineDump(10, 20, 2000, 100*1000, time.Duration(10)*time.Minute)); err == nil {
h.EnableGoroutineDump()
}
} else {
h.DisableGoroutineDump()
}
return h
}