golang[95]-golang-优秀代码赏析-检测goroutine溢出

在latern中看到了这段代码,防止协程溢出。

原理是利用了pprof.lookup() 此会将存在的goroutine的stacktrace打印出来。

通过对比代码前与代码后的goroutine number。 即可得知是否有新的goroutine创建出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// Package grtrack provides a utility that helps check for goroutine leaks.
package grtrack

import (
"bytes"
"regexp"
"runtime/pprof"
"strings"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

var (
goroutineNumber = regexp.MustCompile(`goroutine ([0-9]+)`)
)

// Object that can be used to check whether goroutines have leaked at any point
// in time.
type Checker interface {
// Check immediately checks whether there's been a leak
Check(t *testing.T)

// CheckAfter waits wait and then checks
CheckAfter(t *testing.T, wait time.Duration)
}

type checker struct {
check func(t *testing.T)
}

func Start() Checker {
var buf bytes.Buffer
_ = pprof.Lookup("goroutine").WriteTo(&buf, 2)
before := buf.String()

check := func(t *testing.T) {
var buf bytes.Buffer
_ = pprof.Lookup("goroutine").WriteTo(&buf, 2)
after := buf.String()

beforeGoroutines := make(map[string]bool)
beforeMatches := goroutineNumber.FindAllStringSubmatch(before, -1)
for _, match := range beforeMatches {
beforeGoroutines[match[1]] = true
}

afterMatches := goroutineNumber.FindAllStringSubmatchIndex(after, -1)
for i := 0; i < len(afterMatches); i++ {
idx := afterMatches[i][0]
nextIdx := len(after)
last := i == len(afterMatches)-1
if !last {
nextIdx = afterMatches[i+1][0]
}
matches := goroutineNumber.FindAllStringSubmatch(after[idx:], 1)
num := matches[0][1]
_, exists := beforeGoroutines[num]
if !exists {
delta := after[idx:nextIdx]
if !strings.Contains(delta, "net/http/server.go") {
assert.Fail(t, "Leaked Goroutine", delta)
}
}
}
}

return &checker{check}
}

func (c *checker) Check(t *testing.T) {
c.check(t)
}

func (c *checker) CheckAfter(t *testing.T, wait time.Duration) {
time.Sleep(wait)
c.check(t)
}