Go项目比较大(主要是说代码多,参与者多),你可能会遇到类似下面这样的问题:崩溃了,醒不来,场景早就丢失了,压力测试不知道怎么定位。压力开启后,会出现随机问题。可能是两个小时或五个小时后。能等到崩溃的那一刻吗?有些连锁故障,最后离开现场,并不能帮助我们准确判断问题根源。当出现问题时,我们需要在第一时间到达现场。虽然Go内置的pprof是问题定位的神器,但是你没有办法在问题发生的准确时间保存相应的场景进行分析。特别是一些随机内存泄漏和CPU抖动。当你发现有泄漏的时候,程序可能已经OOM被kill掉了。至于CPU抖动,你可以蹲一个星期,不一定能搞定。解决这个问题的最佳方案是持续剖析,但这个概念需要公司监控系统的配合。在达到最终目标之前,我们可以向前迈出一小步,看看如何以相对较低的成本解决这个问题。.从现象来看,问题的症状可以简单分为几类:cpu抖动:模块中可能存在一些比较冷门的逻辑,触发概率比较低,比如半夜的定时脚本.触发后,你还在睡觉。说到定位,就比较麻烦了。内存使用抖动:有很多情况会导致内存使用抖动,比如突然涌入大量请求,导致创建了过多的对象。它也可能是goroutine泄漏。也有可能是突然的锁冲突,或者是突然的IO抖动。原因太多了,不可能猜出根本原因。goroutine数量猛增,可能是死锁,数据生产完channel可能没有关闭,或者IO震动什么的。CPU占用率、内存占用率、goroutine数量都可以用数值表示,所以不管是“膨胀”还是抖动,都可以用简单的规则来表示:xx突然比正常平均值高25%xx超过了这两条规则模块在正常情况下的最高水位可以描述大多数情况下的异常。规则1可以代表瞬时的、严重的抖动,然后可能很快恢复;规则2可以用来表示那些上升缓慢,但最终超过系统负载的情况,比如1s泄漏1兆内存,直到几小时后OOM。而与均值的diff,在没有历史数据的情况下,只能在程序本身收集,比如goroutine数据,我们可以每隔x秒运行一次收集,把最近N个周期的goroutine计数保存在内存中,并继续与之前记录的goroutine数据的平均值进行diff:time-Page-2例如,如图所示,前十个周期采集的goroutine数量在1300左右波动,而最新采集的数据cycle是2w+,很明显是瞬时触发导致的异常情况,那么此时我们可以自动做一些事情,比如:dump当前goroutinestack,dump当前cpuprofile,dump当前off-cpuprofile,如果不怕死,也可以把trace文件dump几秒保存一下,模块挂掉也没关系。后来喝茶的时候,发现线上出现了crash,就去上网机调取文件,边喝茶边分析,比较清闲。这里有点麻烦的是CPU和内存使用率的收集。当前应用可能运行在物理机上,也可能运行在docker中。所以在获取usage的时候,我们需要自己做一些定制。物理机中的数据采集可以使用gopsutil[1],docker中的数据采集可以参考少量cgroups中的实现[2]。
