Go的并发模型虽简化了并发程序的开发难度,但如果不了解使用方法,常常会遇到goroutine泄露的问题。我们可以可以从两方面入手解决,一是预防(了解什么样的代码会产生泄露),二是监控(通过Prometheus采集metrics来监控)
Channel试用不当引起的泄露#
发送不接收#
接收不发送#
nil channel#
Mutex 引起的泄露#
互斥锁忘记解锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
func main() {
total := 0
defer func() {
time.Sleep(time.Second)
fmt.Println("total: ", total)
fmt.Println("goroutines: ", runtime.NumGoroutine())
}()
var mutex sync.Mutex
for i := 0; i < 10; i++ {
go func() {
mutex.Lock()
total += 1
}()
}
}
|
WaitGroup 引起的泄露#
同步锁使用不当
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
func handle(v int) {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < v; i++ {
fmt.Println("~~~")
wg.Done()
}
wg.Wait()
}
func main() {
defer func() {
fmt.Println("goroutines: ", runtime.NumGoroutine())
}()
go handle(3)
time.Sleep(time.Second)
}
|
Prometheus采集metrics#
1
2
3
4
5
6
7
8
|
go func() {
prometheus.RegisterSimple(r)
routes.InitRouter(r)
if err := endless.ListenAndServe("10.0.0.100:19003", r); err != nil {
log.Println(err)
}
}()
|
只统计基础的数据
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
|
package prometheus
import (
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
type Option func(option *PromOpts)
var defaultOpt = PromOpts{
ExcludeRegexEndpoint: "^/metrics",
ExcludeRegexMethod: "^OPTIONS|^HEAD",
}
func RegisterSimple(r *gin.Engine) {
r.GET("/metrics", gin.WrapH(promhttp.Handler()))
}
//统计endpoint访问数据,路由较多占用资源大
func RegisterEndpoint(r *gin.Engine, option ...Option) {
opt := defaultOpt
for _, fn := range option {
fn(&opt)
}
r.Use(PromMiddleware(&opt))
r.GET("/metrics", PromHandler(promhttp.Handler()))
}
func WithExcludeRegexEndpoint(endpoint string) Option {
return func(option *PromOpts) {
option.ExcludeRegexEndpoint = endpoint
}
}
func WithExcludeRegexMethod(method string) Option {
return func(option *PromOpts) {
option.ExcludeRegexMethod = method
}
}
func WithExcludeRegexStatus(status string) Option {
return func(option *PromOpts) {
option.ExcludeRegexStatus = status
}
}
|
配置Prometheus#
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#targets.json
[
{
"targets":["10.0.0.100:19003"],
"labels": {
"job": "web-store",
"app": "web-store-1",
"env": "dev",
"instance": "10.0.0.100:19003"
}
}
]
|
配置Grafana#