说我有一个像这样的http处理程序:
func ReallyLongFunction(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
// run code that takes a long time here
})
如果用户在不运行后续代码的情况下刷新页面或以其他方式取消请求,有没有办法可以中断此功能,我该怎么办?
我尝试这样做:
notify := r.Context().Done()
go func() {
<-notify
println("Client closed the connection")
s.downloadCleanup()
return
}()
但是无论何时我打断之后的代码仍然可以运行。
因此,实际中断处理的唯一方法是定期检查客户端是否消失(或者是否还有其他信号要停止处理)。
基本上,这相当于构造这样的处理程序func ReallyLongFunction(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
done := r.Context().Done()
// Check wheteher we're done
// do some small piece of stuff
// check whether we're done
// do another small piece of stuff
// …rinse, repeat
})
现在一种检查是否有内容写入通道,但不阻塞该操作的方法是使用“默认选择”习惯用法:
select {
case <- done:
// We're done
default:
}
[并且仅当done
被写入或关闭时(此情况就是这种情况),该状态执行程序才执行“ //我们完成”块中的代码,而其他情况则是default
中的空块执行分支。
所以我们可以将其重构为类似的东西
func ReallyLongFunction(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") done := r.Context().Done() closed := func () { select { case <- done: return true default: return false } } if closed() { return } // do some small piece of stuff if closed() { return } // do another small piece of stuff // …rinse, repeat })
package main
import (
"fmt"
"io"
"log"
"net/http"
"time"
)
func main() {
http.HandleFunc(`/`, func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
log.Println("wait a couple of seconds ...")
for i := 0; i < 10; i++ { // long calculation
select {
case <-ctx.Done():
log.Println("Client closed the connection:", ctx.Err())
return
default:
fmt.Print(".")
time.Sleep(200 * time.Millisecond) // long calculation
}
}
io.WriteString(w, `Hi`)
log.Println("Done.")
})
log.Println(http.ListenAndServe(":8081", nil))
}
这里是客户端,超时:
package main
import (
"io/ioutil"
"log"
"net/http"
"time"
)
func main() {
log.Println("HTTP GET")
client := &http.Client{
Timeout: 1 * time.Second,
}
r, err := client.Get(`http://127.0.0.1:8081/`)
if err != nil {
log.Fatal(err)
}
defer r.Body.Close()
bs, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}
log.Println("HTTP Done.")
log.Println(string(bs))
}
并且您可以使用普通的browse来检查是否不插曲。