用go写了一个守护进程程序:用于检测redis的存活状态并将结果写到zookeeper中,部署到redis机器上,对于每个redis实例会有一个goroutine每隔固定时间去检测其状态,由主goroutine负责信号处理等,再接收到信号时kill其他的goroutine。程序运行了一段时间发现,有些redis实例的对应zookeeper的信息不更新,通过日志发现对应redis的goroutine挂掉了。阅读源码发现貌似是zk的第三方库抛出一个非预期的异常导致。
为了解决这个问题,对逻辑重构:由主goroutine每隔固定时间,对于每个redis实例启动一个goroutine去进行检测,避免出现非预期异常导致goroutine挂掉,从而状态信息不更新的情况。由于goroutine的创建开销很低,并且golang官方推荐使用大量的goroutine来抗并发,所以这种方式实现也很合理。重构完,上线测试发现存在内存泄露。
(1)观察GC
首先对代码review,因为半年前写的,并且最近都没用golang,所以没有发现bug。接着,就想看下gc相关的信息,也许可能透漏些东西。网上查了golang gc相关,在runtime的doc中描述了,通过设置环境变量GODEBUG='gctrace=1'可以让go的运行时把gc信息打印到stderr。
GODEBUG='gctrace=1' ./sentinel-agent >gc.log &
gc.log的输出如下:
gc781(1): 1+2385+17891+0 us, 60 -> 60 MB, 21971 (3503906-348

本文通过一个Go守护进程程序遇到的内存泄露问题,详细分析了如何通过观察GC、内存剖析、监控goroutine数量来定位问题。最终发现是由于goroutine泄露导致,通过调整goroutine管理方式解决了内存泄露。总结了goroutine管理和避免内存泄露的重要性。
2043

被折叠的 条评论
为什么被折叠?



