超时处理程序将ServeHTTP执行移到新的goroutine上,但是在计时器结束后无法终止该goroutine。在每个请求上,它都会创建两个goroutine,但是ServeHTTP goroutine永远不会因上下文而终止。
无法找到杀死goroutine的方法。
编辑带时间的循环。睡眠功能代表了超出我们计时器的巨大计算量。可以用其他任何功能替换它。
package main
import (
"fmt"
"io"
"net/http"
"runtime"
"time"
)
type api struct{}
func (a api) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// For-loop block represents huge computation and usually takes more time
// Can replace with any code
i := 0
for {
if i == 500 {
break
}
fmt.Printf("#goroutines: %d\n", runtime.NumGoroutine())
time.Sleep(1 * time.Second)
i++
}
_, _ = io.WriteString(w, "Hello World!")
}
func main() {
var a api
s := http.NewServeMux()
s.Handle("/", a)
h := http.TimeoutHandler(s, 1*time.Second, `Timeout`)
fmt.Printf("#goroutines: %d\n", runtime.NumGoroutine())
_ = http.ListenAndServe(":8080", h)
}
ServeHTTP goroutine应该与请求上下文一起杀死,通常不会发生。
答案 0 :(得分:1)
使用context.Context指示go-routine终止其功能。当然,例程必须侦听此类取消事件。
因此,对于您的代码,请执行以下操作:
ctx := req.Context() // this will be implicitly canceled by your TimeoutHandler after 1s
i := 0
for {
if i == 500 {
break
}
// for any long wait (1s etc.) always check the state of your context
select {
case <-time.After(1 * time.Second): // no cancelation, so keep going
case <-ctx.Done():
fmt.Println("request context has been canceled:", ctx.Err())
return // terminates go-routine
}
i++
}
游乐场:https://play.golang.org/p/VEnW0vsItXm
注意: Context
设计为可链接的-允许以级联方式取消多个级别的子任务。
在典型的REST调用中,将启动数据库请求。因此,为了确保及时完成这种阻塞和/或缓慢的调用,应该使用Query而不是使用QueryContext-将http请求的上下文作为第一个参数传递。
答案 1 :(得分:0)
我发现,如果您无法访问频道,则无法在运行goroutine时终止它。
在大型计算任务中,您必须按特定间隔或在特定任务完成后观看频道。