Go的协程使用起来很方便,但是有有些坑需要注意: 1. 使用goroutine时加上panic recovery机制,避免服务直接不可用 2. 不要创建都无法退出的goroutine,会导致goroutine 泄漏
启动goroutine时加上panic recovery机制#
错误示例#
使用协程不recover捕获,会导致主程序挂掉
1
2
3
4
5
6
7
8
9
|
func (a *API) Recover(c *gin.Context) {
go doJob()
response.SuccessJSONData(c, nil)
}
func doJob() {
channel := []uint8{1, 2, 3, 4}
fmt.Println(channel[10])
}
|
1
2
3
4
5
6
7
8
|
panic: runtime error: index out of range [10] with length 4
goroutine 36 [running]:
go-study/internal/api/restful/demo.doJob()
/data/go-project/src/go-study/internal/api/restful/demo/goroutine.go:17 +0x1d
created by go-study/internal/api/restful/demo.(*API).Recover
/data/go-project/src/go-study/internal/api/restful/demo/goroutine.go:11 +0x29
exit status 2
|
正确做法#
切记:只要有用到协程的地方,都需要捕获panic错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
func (a *API) Recover(c *gin.Context) {
goWithRecover(doJob)
response.SuccessJSONData(c, nil)
}
func doJob() {
channel := []uint8{1, 2, 3, 4}
fmt.Println(channel[10])
}
func goWithRecover(fn func()) {
go runSafe(fn)
}
func runSafe(fn func()) {
defer func() {
if p := recover(); p != nil {
fmt.Println("panic recover", "panic", p, "stack", string(debug.Stack()))
}
}()
fn()
}
|
不要创建都无法退出的goroutine#
协程数量会一直增加,到最后肯能就OOM
1
2
3
4
5
6
7
8
9
10
11
12
|
func (a *API) Block(c *gin.Context) {
doBlock()
response.SuccessJSONData(c, runtime.NumGoroutine())
}
func doBlock() {
ch := make(chan bool, 0)
goWithRecover(func() {
fmt.Println("异步任务做一些操作")
<-ch
})
}
|
如果在测试阶段发现问题,可以使用uber开源项目goleak#
1
2
3
4
5
6
7
8
9
10
|
import (
"testing"
"go.uber.org/goleak"
)
func TestBlock(t *testing.T) {
defer goleak.VerifyNone(t)
doBlock()
}
|