查看数据我们可以发现请求返回后, goroutine 其实并未回收,但是如果不阻塞的话是会立即回收的
goroutine profile: total 29 24 @ 0x103b125 0x106cc9f 0x1374110 0x12b9584 0x12bb4ad 0x12c7fbf 0x106fd01看它的源码,超时控制主要在 ServeHTTP 中实现,我删掉了部分不关键的数据, 我们可以看到函数内部启动了一个 goroutine 去处理请求逻辑,然后再外面等待,但是这里的问题是,当 context 超时之后 ServeHTTP 这个函数就直接返回了,在这里面启动的这个 goroutine 就没人管了
func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request) { ctx := h.testContext if ctx == nil { var cancelCtx context.CancelFunc ctx, cancelCtx = context.WithTimeout(r.Context(), h.dt) defer cancelCtx() } r = r.WithContext(ctx) done := make(chan struct{}) tw := &timeoutWriter{ w: w, h: make(Header), req: r, } panicChan := make(chan interface{}, 1) go func() { defer func() { if p := recover(); p != nil { panicChan <- p } }() h.handler.ServeHTTP(tw, r) close(done) }() select { case p := <-panicChan: panic(p) case <-done: // ... case <-ctx.Done(): // ... } } 4.3 总结context 是一个优缺点都十分明显的包,这个包目前基本上已经成为了在 go 中做超时控制错误取消的标准做法,但是为了添加超时取消我们需要去修改所有的函数签名,对代码的侵入性比较大,如果之前一直都没有使用后续再添加的话还是会有一些改造成本
这篇真的很长
五.参考文献context · pkg.go.dev
Go 语言实战笔记(二十)| Go Context
https://lailin.xyz/post/go-training-week3-context.htm
https://www.topgoer.cn/docs/gozhuanjia/chapter055.3-context