大型系统在线问题诊断与定位

本文是武汉 gopher meetup 的分享内容整理而成,分享内容在 “无人值守” 的两篇和其它社区分享中亦有提及。(也就是说你看过那两篇,这个可以不用看了)

先来看看苦逼的开发人员

老板说:

大型系统在线问题诊断与定位_第1张图片

队友说:

大型系统在线问题诊断与定位_第2张图片

外组同事说:

大型系统在线问题诊断与定位_第3张图片

底层团队说:

大型系统在线问题诊断与定位_第4张图片

你:

大型系统在线问题诊断与定位_第5张图片

业界的思路?

混口饭吃也是不容易,既然有问题了,我们还是要解决的。要先看看有没有现成的思路可以借鉴?

Google 在 这篇论文里提到过其内部的线上 profile 流程:

大型系统在线问题诊断与定位_第6张图片

架构图已经比较简单了,线上应用暴露 profile 接口,collector 定时将 profile 信息采集到二进制存储,由统一的在线平台展示。

这篇论文催生了一些开源项目和创业公司,例如在 这篇文章 中,对 continuous profiling 有很不错的解释。

大型系统在线问题诊断与定位_第7张图片

我们日常的 CI 和 CD 流水线可以高频次发布线上系统,当线上有 continuous profiling 系统在运行时,每次发布我们都能够得到实时的性能快照,并与发布前的性能做快速对照。

系统的性能问题和其它技术问题一样,同样是发现得越早,解决起来就越快,损失越小。

Google Cloud 上也有 profiler 相关的 产品,是由 Go 原先的 pprof 开发者开发的,她在文章中声称每分钟对应用采集 10s 的 profile,大约有 5% 的性能损失,还是可以接受的。

简单来说,continuous profiling 带给我们的优势主要就是三点:

  • 缩短性能问题反馈周期
  • 节省服务器成本
  • 改进开发者的工作流程

有了思路,再看看有没有具体的开源产品可以参考。

相关开源产品

大型系统在线问题诊断与定位_第8张图片

stackimpact-go 是社区比较早的开源项目了,不过只开源了 client 部分,所以 dashboard 和后端部分一直是一个谜,不过我们大概也能看出来,有这种曲线图形式的 profile 数据,有性能衰减时能及时发现。

大型系统在线问题诊断与定位_第9张图片

conprof 其实也差不多。

大型系统在线问题诊断与定位_第10张图片

profefe 也差不多。

看起来思路都一样,这个需求很简单,我们只要找监控团队帮忙做一套皮肤就可以了!

但监控团队也很无奈。

大型系统在线问题诊断与定位_第11张图片

我们的方案

求人不如求己,我们需要定位的是抖动问题,那我们以 5s 为单位,把进程的 CPU 使用 (gopsutil),RSS(gopsutil),goroutine 数 (runtime.NumGoroutine),用 10 大小的环形数组保存下来,每次采集到新值时,与之前多个周期的平均值进行 diff 就可以了:

大型系统在线问题诊断与定位_第12张图片

当波动率超过可以接受的范围,则认为当前进程发生了资源使用抖动,那么:

  • 若 CPU 抖动,从当前开始采集 CPU profile 5 秒
  • 若 RSS 抖动,将当前进程的 heap profile 记录下来
  • 若 goroutine 抖动,将当前进程的 goroutine profile 记录下来

一套方案做下来,还挺简单的。当我们收到模块的 CPU、内存或 goroutine 数报警时,上线到相应的实例来查看即可。

实际的案例

解码 bug

某模块突然出现了 RSS 使用飚升,上线看之后发现自动 dump 文本的 profile 中,单个对象的 inuse_space 超过了 1GB:

   // inuse_objects: inuse_space [alloc_objects : alloc_space]
1 : 1024000000 [1 : 1024000000]  git.xxx.xxx


复制代码

这是很反常的,阅读代码后发现在 decode 中没有对这些情况进行一定的防御操作,有形如下面的代码:

   var l = readLenFromPacket()
var list = make([]byte, l)


复制代码

虽然理论上 body 是可以传 1GB 的,不过内部的 RPC 框架,还是应该对这种情况进行一些限制。

CPU 尖刺

有些在线系统是定时任务调用的,所以其访问峰值非常不平均,在定时任务触发后,会有一段时间 CPU 使用非常高,有了自动 CPU profile dump,就非常容易找到具体哪里使用的 CPU 很高了。

goroutine 泄露

大型系统在线问题诊断与定位_第13张图片

类似这样的 goroutine 泄露问题,也是可以很容易发现的。

deadlock(扩展)

因为我们可以采集到所有 goroutine 的栈信息,所以理论上通过遍历我们可以发现哪些 goroutine 是持锁的,例如下列代码,当发生死锁时,我们可以直接把持锁的 goroutine dump 下来,就很容易发现死锁了:

大型系统在线问题诊断与定位_第14张图片

thread block(扩展)

大型系统在线问题诊断与定位_第15张图片,当线程因为调用 cgo 等原因发生了阻塞,会造成线程数暴涨,这时候我们可以将 goroutine 和 thread profile dump 下来,进行诊断。

总结

有了自动化的工具,日子更好过了。

你可能感兴趣的:(系统,在线,问题)