[转载]在 Go 1.11 下调试延迟问题

诊断和调试复杂系统是一件复杂的事。经常需要多个层次的诊断数据来弄清楚延迟问题可能的原因。

一个分布式系统由多个服务器组成,这些服务器互相依赖,共同完成对用户请求的服务。在任何时候,

  • 系统中的一个进程可能会处理大量的请求。
  • 在高并发的服务器中,没有容易的途径能将一个请求生命周期内的各个事件分离出来。
  • 在高并发的服务器中,对于响应一个请求所发生的事件,我们没有很好的可见性。

随着 Go 在近些年来变成编写服务器的一门流行语言,我们意识到理解 Go 进程在一个请求的生命周期内发生了什么事的必要性。

在程序执行过程中会发生许多运行时活动:调度、内存分配、垃圾回收等等。但是要将用户代码与运行时事件关联起来,并帮助用户分析运行时事件如何影响他们的性能,这在过去是不可能的。

网站可靠工程师(SRE)可能会在诊断一个延迟问题时寻求帮助,并希望有人能帮助他们理解并优化一个特定的服务器。甚至对于那些精通 Go 的人来说,能够估计出运行时对于他们遇到的特定情况的影响,也是非常复杂的。

没有容易的方式能说明为什么某些请求延迟很高。分布式追踪让我们定位出哪个 handler 需要仔细查看,但我们需要深挖下去。是 GC,调度器还是 I/O 让我们在服务一个请求的时候等待如此之久? — SRE

在系统外部的人看来,经历延迟问题时,他们仅知道的一个事实就是自己等待了超出预期的时间(356ms)才收到响应,除此之外他们不知道别的。

image

用户使用 GET 方法访问 /messages 时等待了 356 ms 才收到响应。

从那些能够接触到分布追踪的开发者的角度来看,看到延迟的明细及每个服务对于总的延迟贡献了多少是有可能的。通过分布式追踪,我们对于境况有更好的可见性。

2.png

通过你收集到的分布式追踪来看延迟明细。

在这种情况下,为了服务 /messages, 我们写了三个内部的 RPC: auth.AccessToken, cache.Lookup 和 spanner.Query。我们可以看到每个 RPC 对延迟贡献了多少。这个时候,我们看出是 auth.AccessToken 花费了比平时长的时间。

我们成功的将问题缩小到一个特定的服务。我们可以通过关联特定的进程查看 auth.AccessToken 的源码,找到导致高延迟的代码,或者我们可以随意看看这个问题是否会在某个验证服务的实例上重现。

在 Go 1.11 下,我们将对执行追踪器有额外的支持,以便能指出 RPC 调用时的运行时事件。有了这个新特性,对于一个调用生命周期所发生的事,用户可以收集到更多的信息。

在这个案例中,我们将聚焦于 auth.AccessToken 范围内的部分。在网络上一共花费了 30 + 18 µs,阻塞的系统调用 5µs,垃圾回收 21µs,真正执行 handler 花费了 123µs,其中大部分都花在序列化和反序列化上。

3.png

通过查看这一层次的详细信息,我们最终可以说我们很不幸的在 RPC 调用期间遇到了 GC,而且我们在序列化/反序列化上竟然花费了很长的时间。然后,工程师们就可以指出最近对于 auth.AccessToken 的消息进行的修改,从而改善性能问题。他们还可以看看垃圾回收是否经常会在关键路径上对这个请求的处理造成影响,对内存分配的方式进行优化。

Go 1.11

在 Go 1.11 下,Go 的执行追踪器将引入一些新的概念、API 及追踪特性:

  • 用户事件及用户注解,参见 runtime/trace.
  • 用户代码和运行时之间的关联。
  • 执行追踪器与分布式追踪进行关联的可行性。

执行追踪器引入两个上层的概念:regiontask,以便用户来对他们的代码进行插桩。

Region 是你希望收集追踪数据的代码区域。一个 region 开始和结束在同一个 goroutine 内。另一方面,task 是一个逻辑上的群组,将相关的 region 归在一起。一个 task 的开始和结束可以在不同的 goroutine 中。

我们预期用户为每个分布式追踪的 span 都启动一个执行追踪器,通过创建 region, 当问题发生时即刻启用执行追踪器,记录一些数据,分析输出,来对他们的 RPC 框架进行全面的插桩。

亲手进行测试

尽管这些新特性只能在 Go 1.11 中使用,你依然可以按照说明从源码安装

我也推荐你在分布式追踪里试试。我最近在 Census 创建的 span 中增加了对执行追踪器中 task 的支持。

import (
    "runtime/trace"

    "go.opencensus.io/trace"
)
ctx, span := trace.StartSpan(ctx, "/messages")
defer span.End()

trace.WithRegion(ctx, "connection.init", conn.init)

如果你用的是 gRPC 或者 HTTP,那么你不需要手动创建 span,因为他们已经自动创建好了。在你的 handler 里面,你可以简单的对接收到的 context 使用 runtime/trace。

注册 pprof.Trace 的 handler,当你需要用执行追踪器的数据进行诊断时进行数据收集。

import _ "net/http/pprof"

go func() {
    log.Println(http.ListenAndServe("localhost:6060", nil))
}()

如果你需要执行追踪器的数据,那就马上记录,并启动可视化工具:

$ curl http://server:6060/debug/pprof/trace?seconds=5 -o trace.out
$ go tool trace trace.out
2018/05/04 10:39:59 Parsing trace...
2018/05/04 10:39:59 Splitting trace...
2018/05/04 10:39:59 Opening browser. Trace viewer is listening on http://127.0.0.1:51803

接着你就可以在 /usertasks 看到由 helloworld.Greeter.SayHello 创建的执行追踪器任务的一个分布。

image

RPC task 的时间分布。

你可以点击 3981µs 的那个异常的 bucket,进一步分析在那个特定 RPC 的生命周期里发生了什么。

同时,/userregions 让你列出收集到的 region。你可以看到 connection.init 这个 region 以及所对应的多条记录。(注意到 connection.init 是为了演示而手动集成到 gRPC 框架的源码中的,更多的插桩工作还在进行中。)

image

region 的时间分布。

如果你点击了任意一个链接,它会给你更多关于处于那个延迟 bucket 中的 region 的详细信息。在下面的例子中,我们看到有一个 region 位于 1000µs 的 bucket。

image

1000µs 的 region 在等待 GC 和调度器上花费了额外的时间。

这样你就看到了细粒度的延迟明细。你可以看到 1309µs 的 region 交叠了垃圾回收。这以垃圾回收和调度的形式在关键路径上增加了不少开销。除此之外,执行 handler 与处理阻塞的系统调用花费了差不多的时间。

局限

尽管新的执行追踪器的特性很强大,但还是有一些局限。

  • Region 只能在同一个 goroutine 中开始和结束。执行追踪器目前还不能自动记录跨越多个 goroutine 的数据。这就需要我们手动地插桩 region。下一个大的步伐将是在 RPC 框架及 net/http 这样的标准包里增加细粒度的插桩。
  • 执行追踪器输出的格式比较难解析,go tool trace是唯一的能理解这种格式的标准工具。并没有简单的方式能够自动将执行追踪器的数据与分布式追踪数据关联起来 - 所以我们分别搜集它们,之后再做关联。

结论

Go 正在努力成为强大的线上服务运行时。有了来自执行追踪器的数据,我们距离对线上服务器的高可见性近了一步,当问题出现时,能提供的可行数据也更多了。


via: https://medium.com/observability/debugging-latency-in-go-1-11-9f97a7910d68

作者:JBD 译者:krystollia 校对:polaris1119

本文由 GCTT 原创编译,Go语言中文网 荣誉推出

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354

推荐阅读更多精彩内容