Timeout handler moves ServeHTTP execution on a new goroutine, but not able to kill that goroutine after the timer ends. On every request, it creates two goroutines, but ServeHTTP goroutines never kill with context.
package main
import (
"fmt"
"io"
"net/http"
"runtime"
"time"
)
type api struct{}
func (a api) ServeHTTP(w http.ResponseWriter, req *http.Request) {
i := 0
for {
if i == 500 {
break
}
//fmt.Printf("@time: %s\n", time.Now())
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)
}
Your handler is not stoppable from outside. You have to implement cancellation context in your handler. TimeoutHandler doesn’t/can’t kill the routine, it cancels the context and responses with 503 Service Unavailable error. You need to handle it yourself.
package main
import (
"fmt"
"io"
"net/http"
"runtime"
"time"
)
type api struct{}
func (a api) ServeHTTP(w http.ResponseWriter, req *http.Request) {
i := 0
for {
if i == 500 {
break
}
//fmt.Printf("@time: %s\n", time.Now())
fmt.Printf("i: %d #goroutines: %d\n", i, runtime.NumGoroutine())
time.Sleep(1 * time.Second)
select {
case <-req.Context().Done():
{
fmt.Println("context canceled")
return
}
default:
}
i++
}
_, _ = io.WriteString(w, "Hello World!")
}
func main() {
var a api
s := http.NewServeMux()
s.Handle("/", a)
h := http.TimeoutHandler(s, 10*time.Second, `Timeout`)
fmt.Printf("#goroutines: %d\n", runtime.NumGoroutine())
_ = http.ListenAndServe(":8081", h)
}