golang使用pprof检查goroutine泄露

有一段时间,咱们的推送服务socket占用很不正常,咱们本身统计的同时在线就10w的用户,可是占用的socket居然达到30w,而后查看goroutine的数量,发现已经60w+。缓存

每一个用户占用一个socket,而一个socket,有read和write两个goroutine,简化的代码以下:并发

c, _ := listerner.Accept()

go c.run()

func (c *conn) run() {
    go c.onWrite()
    c.onRead()
}

func (c *conn) onRead() {
    stat.AddConnCount(1)

    //on something

    stat.AddConnCount(-1)

    //clear
    //notify onWrite to quit
}

当时我就怀疑,用户同时在线的统计是正确的,也就是以后的clear阶段出现了问题,致使两个goroutine都没法正常结束。在检查代码以后,咱们发现了一个可疑的地方,由于咱们不光有本身的统计,还会将一些统计信息发送到咱们公司的统计平台,代码以下:socket

ch = make([]byte, 100000)
func send(msg []byte) {
    ch <- msg
}

//在另外一个goroutine的地方,
msg <- msg
httpsend(msg)

咱们channel的缓存分配了10w,若是公司统计平台出现了问题,可能会致使channel阻塞。但究竟是不是这个缘由呢?函数

幸运的是,咱们先前已经在代码里面内置了pprof的功能,经过pprof goroutine的信息,发现大量的goroutine的当前运行函数在httpsend里面,也就是说,公司的统计平台在大并发下面服务不可用,虽然咱们有http超时的处理,可是由于发送的数据量太频繁,致使总体阻塞。ui

临时的解决办法就是关闭了统计信息的发送,后续咱们会考虑将其发送到本身的mq上面,虽然也可能会出现mq服务不可用的问题,可是说句实话,比起本身实现的mq,公司的统计平台更让我不可信。spa

这同时也给了我一个教训,访问外部服务必定要好好处理外部服务不可用的状况,即便可用,也要考虑压力问题。code

对于pprof如何查看了goroutine的问题,能够经过一个简单的例子说明:it

package main

import (
    "net/http"
    "runtime/pprof"
)

var quit chan struct{} = make(chan struct{})

func f() {
    <-quit
}

func handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain")

    p := pprof.Lookup("goroutine")
    p.WriteTo(w, 1)
}

func main() {
    for i := 0; i < 10000; i++ {
        go f()
    }

    http.HandleFunc("/", handler)
    http.ListenAndServe(":11181", nil)
}

这上面的例子中,咱们启动了10000个goroutine,并阻塞,而后经过访问http://localhost:11181/,咱们就能够获得整个goroutine的信息,仅列出关键信息:test

goroutine profile: total 10004

10000 @ 0x186f6 0x616b 0x6298 0x2033 0x188c0
#   0x2033  main.f+0x33 /Users/siddontang/test/pprof.go:11

能够看到,在main.f这个函数中,有10000个goroutine正在执行,符合咱们的预期。import

在go里面,还有不少运行时查看机制,能够很方便的帮咱们定位程序问题,不得不赞一下。

相关文章
相关标签/搜索