最近在工作上进展蛮顺利的,在技术上有了更深一层的体会
虽然主题要说的这两个东西其实蛮简单的,不过直到最近才在实战上遇到,才有了更深层的体会
还有想说抱着打听消息的心态去面试就被问到了
没有事先準备,凭着在公司的经验凭空作画的写出来,好险用了3年的 Go 没有白费
前言
刚使用 goroutine 的时候对产生 leak 的场景其实是很陌生的
想着的是我的程式都照着预期的执行会有什么问题?
不过有这个想法的下一秒我就想到,万一出了问题怎么办?
章节
Goroutine leak 的情境加上案例防範 goroutine leak利用 code 发现 goroutine leak利用 pprof 发现 goroutine leak(下篇待续)Goroutine leak
今天当程式码在操作的时候,会遇到几种情形
处理只针对自身 server,并且耦合度很低于其他部分没什么关连对 database 的请求对别的伺服器的请求(http request)channel block接着就可以开始探讨为什么会出现 leak 的状况
状况是情境1,那当然没什么问题,例如你只需要 a+b 之后做回传
那如果是2、3、4呢?goroutine 也是正在运行的协程(coroutine)
需要等待某个部分处理完毕才能够继续运行下去,此时在等待处理的资源如果永远都处理不完
那这个 goroutine 就永远不会处理完并且不会释放资源,造成 leak
Example 1
启动 http server
然后做出一只 API 请求另一个伺服器,并且永远不会回传也没有 timeout,就会发生这个场景
Example 2
channel block 又是怎么回事?看看 graceful shutdown 就知道了
func main() {c := make(chan os.Signal, 1)signal.Notify(c, os.Interrupt)log.Println("Go main process working")v := <-clog.Println(v)}
平常会利用 graceful shutdown 来监听闭关事件
所以会有个 channel block 住以防止 main process 直接运行到底直接结束
那反过来说,要是在使用 goroutine 的时候 channel block,那就永远卡住了
Example 3
package mainimport ("github.com/gin-gonic/gin""log""runtime""time")var totalClick intfunc main() {go checkGoroutine()r := gin.Default()r.GET("myTestPath", goroutineTest)r.Run(":8000")}func checkGoroutine() {for {time.Sleep(1 * time.Second)log.Printf("total leaked goroutines:%v", runtime.NumGoroutine())}}func goroutineTest(ctx *gin.Context) {log.Println("got a http request")// senderch := make(chan int)go sendNum(ch) // leak// receiver, you can mark or unmark bottom 1 lines to get goroutine obvious log changesgo releaseChan(ch, ctx) // try to mark this linectx.JSON(200, nil)}func sendNum(ch chan<- int) {totalClick++ch <- totalClicklog.Println("this line is not executed until ch is unblocked") // blocked here}func releaseChan(ch <-chan int, ctx *gin.Context) {for {select {case <-ctx.Request.Context().Done():log.Printf("unblocked chan that value is %v", <-ch)return}}}
防範 Goroutine leak
从发生情境我们可以看到有几种会发生的问题点,总结起来防範的方法是有规律的
防範机制
增加 timeout 或其他终止机制观察方法
自製监听方法观察 leak透过 pprof 观察参考连结1内有介绍etcd
uber-go
这两个 package 实作检查 goroutine leak 的方法可以利用 https://github.com/uber-go/goleak 检查 leak(拆包即用)透过
runtime.NumGoroutine()
粗略观察