go项目-校验是否存在协程泄露

360 阅读1分钟

事情的起因是因为 自己之前的 记一次go项目协程泄露问题排查, 然后了解到了 goleak 这个项目,于是记录一下吧,方便之后写代码时可以提前预测。

首先准备环境

  • redis
  • go

redis可借助docker一键搭建

docker rm -f redis
docker run -itd -p 6379:6379 --name=redis  redis:latest

更多的docker搭建可关注docker系列-脚本sh总结

编写代码

  1. 创建 go.mod 文件
go mod init go_case

文件内容为

module go_case

go 1.22.6

require (
   github.com/go-redis/redis v6.15.9+incompatible
   go.uber.org/goleak v1.3.0
)

2. 创建 goleak_test.go 文件,并编写以下代码

package about_goroutine

import (
   "fmt"
   "github.com/go-redis/redis"
   "go.uber.org/goleak"
   "testing"
)

func TestGoRedisLeak(t *testing.T) {
   client := redis.NewClient(&redis.Options{
      Addr: "localhost:6379",
      DB:   0,
   })
   defer client.Close()

   pong, err := client.Ping().Result()
   fmt.Println(pong, err)
}

然后就运行 TestGoRedisLeak 方法就可以了,最终会得到如下结果 image.png

为什么发生了协程泄露,主要原因就是因为 go-redis/redis v6.15.9+incompatible 版本代码

image.png

会导致当 client 退出时协程还没退出,因此可以试着调小一点 idleCheckFrequency,调整为 100ms(本文仅演示,生产环境不建议)

func TestGoRedisLeak(t *testing.T) {
   client := redis.NewClient(&redis.Options{
      Addr:               "localhost:6379",
      DB:                 0,
      IdleCheckFrequency: 100 * time.Millisecond, //新添加的参数
   })
   defer client.Close()

   pong, err := client.Ping().Result()
   fmt.Println(pong, err)
}

此时就会得到结果如下:

image.png

感觉该功能在每次编写go代码的时候还比较重要,避免协程泄露嘛。

更多使用方法可参考 goleak