golang

Go:协程泄漏探测器

2021-04-18  本文已影响0人  Go语言由浅入深

【译文】原文地址


使用APM监控goroutine的数量,可以容易的发现goroutine泄漏。下图是来自NewRelic(APM工具开发商)的一张监控goroutines的图片。

协程泄漏会导致协程数量持续增长直到服务器奔溃。然而,有很多方法来避免泄漏即使在代码部署之前也可以。

探测泄漏

Uber的Go团队在Github上面是一个很活跃的团队,开发了一个Goroutine泄漏探测器,是一个用于和单元测试集成的工具。这个包实际上是监测单元测试代码运行时协程的泄漏。以下是一个函数存在goroutine泄漏:

func leak() error {
    go func() {
        time.Sleep(time.Minute)
    }()
    return nil
}

下面的代码是该函数的测试代码:

func TestLeakFunction(t *testing.T)  {
    defer goleak.VerifyNone(t)
    if err := leak(); err != nil{
        t.Fatal("error not expected")
    }
}

运行测试代码会发现协程泄漏:

=== RUN   TestLeakFunction
    leaks.go:78: found unexpected goroutines:
        [Goroutine 19 in state sleep, with time.Sleep on top of the stack:
        goroutine 19 [sleep]:

上面的错误提示了两个错误信息:

泄漏的携程被监测到后,而且还有关于泄漏的信息。现在我们明白它是如何工作的,因此需注意这个监测的缺陷。

内部

要能监测泄露,唯一的要求就是在测试代码最后面调用该库来检查泄漏的携程即可。实际上,它会监测所有goroutine而不仅仅是泄漏的goroutine。

首先检测器列出所有创建的goroutine。以下就是上面的列子所有goroutine列表:



Goroutine的栈信息来源于导出函数runtime.Stack(go标准库函数)。因此,任何人都可以拿到这些信息。

然后,从这个协程列表中,泄漏检测器就可以通过对这些信息进行分析,然后将属于标准库的协程删除。如下:

这个工具不是完美的,但是了解其中的可能性和限制有助于通过测试来检测泄漏,避免对已经部署到生产环境中的代码进行调试。

有趣的是这个方法被广泛用在了net/http包来检测泄漏协程。如下是一个例子:



再次看到,内部函数afterTest查看goroutine栈来检测存在的泄漏。

上一篇 下一篇

猜你喜欢

热点阅读