问题描述
这个问题可以用下面简单的go代码片段重现:
The problem can be reproduced with the below simple go code snippet:
简单的 go http 服务器:
Simple go http server:
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func handler(w http.ResponseWriter, r *http.Request) {
go func(done <-chan struct{}) {
<-done
fmt.Println("message", "client connection has gone away, request got cancelled")
}(r.Context().Done())
time.Sleep(30 * time.Second)
fmt.Fprintf(w, "Hi there, I love %s!
", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
从 http 服务器开始,如果我使用 curl(邮递员)发送一个简单的 GET
请求,例如:
Start above http server, if i send one simple GET
request with curl(postman also) like:
curl -X GET http://localhost:8080/
然后按 Ctrl+C
终止请求,然后我可以在服务器端看到打印的消息:
then press Ctrl+C
to terminate the request, then i am able to see the printed message at server side:
message client connection has gone away, request got cancelled
以上是我期望的正确行为:模拟客户端离开时服务器可以捕获它并尽早取消所有不必要的工作的情况.
above is the correct behaviour i expect: to simulate the situation that when client is gone the server can capture it and then cancel all the unnecessary work as early as it can.
但是当我发送一个带有请求正文的 POST 请求时,这种预期的行为不会发生,<-done
信号被捕获,直到请求截止日期满足.
But when i send one POST request with request body, this expected behaviour doesn't happen, the <-done
signal was captured until the request deadline meet.
curl -X POST http://localhost:8080/ -H 'Content-Type: application/json' -d '{}'
总结一下我的问题:
- 为什么以及如何 curl(postman)
GET
、POST
(有或没有请求正文)请求产生如此大的差异? - 我应该如何使用 go context 包正确处理这种情况,我的意思是尽快捕获客户端消失的信号,从而进一步取消服务器端不必要的工作以尽早释放资源.
- Why and how the curl(postman)
GET
,POST
(with or without request body) request make such a difference? - How should i handle this case properly with go context package, i mean to capture the client is gone signal as soon as it can, and so further to cancel the server side unnecessary work to release the resources as early as it can.
推荐答案
读取请求体检测客户端何时关闭连接:
Read the request body to detect when the client closes the connection:
func handler(w http.ResponseWriter, r *http.Request) {
go func(done <-chan struct{}) {
<-done
fmt.Println("message", "client connection has gone away, request got cancelled")
}(r.Context().Done())
io.Copy(ioutil.Discard, r.Body) // <-- read the body
time.Sleep(30 * time.Second)
fmt.Fprintf(w, "Hi there, I love %s!
", r.URL.Path[1:])
}
net/http 服务器通过读取连接来检查关闭的连接.在应用程序开始读取请求正文(如果有)之前不会开始读取.
The net/http server checks for closed connections by reading the connection. No reads are started until the application starts reading the request body (if any).
这篇关于当请求有正文(卷曲,邮递员)时,Go http上下文无法捕获取消信号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!