(goroutine 泄漏)http.TimeoutHandler 不会杀死相应的 ServeHTTP goroutine
(goroutine leaks) http.TimeoutHandler does not kill respective ServeHTTP goroutine
超时处理程序在新的 goroutine 上移动 ServeHTTP 执行,但无法在计时器结束后终止该 goroutine。对于每个请求,它都会创建两个 goroutine,但 ServeHTTP goroutines 永远不会用上下文杀死。
无法找到杀死 goroutines 的方法。
Edit For-loop with time.Sleep function,表示超出我们计时器的巨大计算。可以用任何其他函数替换它。
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 应该与请求上下文一起终止,通常情况下不会发生这种情况。
使用context.Context指示go-routines中止他们的功能。 go-routines当然要监听这样的取消事件。
因此,对于您的代码,请执行以下操作:
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
被设计为链接 - 允许以级联方式取消多个级别的 sub-tasks。
在典型的 REST 调用中,一个人会发起一个数据库请求。因此,为了确保这种阻塞 and/or 慢速调用及时完成,而不是使用 Query one should use QueryContext - 将 http 请求的上下文作为第一个参数传递。
我发现,如果您无法访问您的频道,那么当 goroutine 处于 运行.
时,就无法终止或停止 goroutine
在大型计算任务中,您必须在特定时间间隔或特定任务完成后观看频道。
超时处理程序在新的 goroutine 上移动 ServeHTTP 执行,但无法在计时器结束后终止该 goroutine。对于每个请求,它都会创建两个 goroutine,但 ServeHTTP goroutines 永远不会用上下文杀死。
无法找到杀死 goroutines 的方法。
Edit For-loop with time.Sleep function,表示超出我们计时器的巨大计算。可以用任何其他函数替换它。
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 应该与请求上下文一起终止,通常情况下不会发生这种情况。
使用context.Context指示go-routines中止他们的功能。 go-routines当然要监听这样的取消事件。
因此,对于您的代码,请执行以下操作:
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
被设计为链接 - 允许以级联方式取消多个级别的 sub-tasks。
在典型的 REST 调用中,一个人会发起一个数据库请求。因此,为了确保这种阻塞 and/or 慢速调用及时完成,而不是使用 Query one should use QueryContext - 将 http 请求的上下文作为第一个参数传递。
我发现,如果您无法访问您的频道,那么当 goroutine 处于 运行.
时,就无法终止或停止 goroutine在大型计算任务中,您必须在特定时间间隔或特定任务完成后观看频道。