Go request.context无法关闭连接
我正在使用context.context取消http请求 我发现虽然我得到了上下文取消,但底层套接字连接仍然可用,几秒钟后我就可以得到响应。它是否设计成在发出请求后读取响应 这是密码Go request.context无法关闭连接,go,http.client,Go,Http.client,我正在使用context.context取消http请求 我发现虽然我得到了上下文取消,但底层套接字连接仍然可用,几秒钟后我就可以得到响应。它是否设计成在发出请求后读取响应 这是密码 func SendRequest(ctx context.Context, url string) { req, err := http.NewRequest("GET", url, nil) if err != nil { fmt.Println(err) } re
func SendRequest(ctx context.Context, url string) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println(err)
}
req = req.WithContext(ctx)
res, err := client.Do(req)
select {
case <-ctx.Done():
fmt.Printf("%s Canceled\n", url)
//client.Transport.(*http.Transport).CancelRequest(req)
//client.Transport.(*http.Transport).CloseIdleConnections()
}
if res != nil {
defer res.Body.Close()
}
if err != nil {
fmt.Printf("Failed: %v\n", err)
} else {
io.Copy(ioutil.Discard, res.Body)
fmt.Printf("return status: %d\n", url, res.StatusCode)
}
}
我请求的URL将在几秒钟后返回,因此我仍然可以读取响应正文,并且在进程退出后连接被关闭
下面是一个简单的代码来重现这个问题
func client() {
ctx, cancel := context.WithCancel(context.Background())
client := http.DefaultClient
request, _ := http.NewRequest("GET", "http://127.0.0.1:9090", nil)
req := request.WithContext(ctx)
go func() {
client.Do(req)
}()
time.Sleep(time.Duration(1) * time.Second)
cancel()
<-time.After(time.Duration(10) * time.Second)
}
func sayhelloName(w http.ResponseWriter, r *http.Request) {
time.Sleep(time.Duration(10) * time.Second)
fmt.Fprintf(w, "Hello world!")
}
func server() {
http.HandleFunc("/", sayhelloName)
err := http.ListenAndServe(":9090", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
连接在恰好10秒后关闭
当上下文被取消时,您不需要自己做任何事情来取消请求。这已经由标准http包处理 您所需要的只是:
func SendRequest(ctx context.Context, url string) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err)
}
req = req.WithContext(ctx)
res, err := client.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
io.Copy(ioutil.Discard, res.Body)
fmt.Printf("return status: %d\n", url, res.StatusCode)
}
编辑
您的TCP转储确认一切都正常工作,正如我上面所描述的
要分解它:
您的出站请求确实在一秒钟后被取消,TCP转储确认:
6 2017-12-04 06:10:21.864955 0.0993000 40 TCP 17503 → 9090 [FIN, ACK] Seq=96 Ack=1 Win=8192 Len=0
7 2017-12-04 06:10:21.864955 0.0000000 40 TCP 9090 → 17503 [ACK] Seq=1 Ack=97 Win-7936 Len=0
第6行的FIN意味着HTTP客户端正在告诉服务器我已经和您谈完了。第7行的ACK表示服务器响应了请求并关闭了连接
但是,HTTP处理程序忽略已取消的请求,并尝试响应,生成第8行:
8 2017-12-04 06:10:30.868955 9.004000 169 HTTP HTTP/1.1 200 OK (text/plain)
但随后服务器收到连接无效的消息:
9 2017-12-04 06:10:30.868955 0.000000 40 TCP 17503 → 8080 [RST, ACK] Seq=97 Ack=130 Win=0 Len=0
RST表示连接已重置,当前无效。这是因为连接提前9秒终止
你看,你的请求马上被取消了,这是应该的
您可以添加到代码中的唯一改进是让服务器中的HTTP处理程序在传入请求被取消时提前退出,从而实际检测并执行此类取消。当上下文被取消时,您不需要自己做任何事情来取消请求。这已经由标准http包处理 您所需要的只是:
func SendRequest(ctx context.Context, url string) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err)
}
req = req.WithContext(ctx)
res, err := client.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
io.Copy(ioutil.Discard, res.Body)
fmt.Printf("return status: %d\n", url, res.StatusCode)
}
编辑
您的TCP转储确认一切都正常工作,正如我上面所描述的
要分解它:
您的出站请求确实在一秒钟后被取消,TCP转储确认:
6 2017-12-04 06:10:21.864955 0.0993000 40 TCP 17503 → 9090 [FIN, ACK] Seq=96 Ack=1 Win=8192 Len=0
7 2017-12-04 06:10:21.864955 0.0000000 40 TCP 9090 → 17503 [ACK] Seq=1 Ack=97 Win-7936 Len=0
第6行的FIN意味着HTTP客户端正在告诉服务器我已经和您谈完了。第7行的ACK表示服务器响应了请求并关闭了连接
但是,HTTP处理程序忽略已取消的请求,并尝试响应,生成第8行:
8 2017-12-04 06:10:30.868955 9.004000 169 HTTP HTTP/1.1 200 OK (text/plain)
但随后服务器收到连接无效的消息:
9 2017-12-04 06:10:30.868955 0.000000 40 TCP 17503 → 8080 [RST, ACK] Seq=97 Ack=130 Win=0 Len=0
RST表示连接已重置,当前无效。这是因为连接提前9秒终止
你看,你的请求马上被取消了,这是应该的
您可以添加到代码中的唯一改进是让服务器中的HTTP处理程序在传入请求被取消时提前退出,从而实际检测并执行此类取消操作。我想做的是在取消上下文后关闭连接。Transport.CancelRequest可以帮助关闭文档中所述的连接,但它看起来不是这样的,而且它已被弃用。因此,如果Request.WithContext无法帮助关闭连接,我该怎么做?谢谢@艾萨克:正如我在回答中所说的:这已经由标准http包处理了。取消上下文后,连接将关闭。此外,CancelRequest已被弃用。别再用了。好的,@Flimzy,谢谢你的回复。我明白你想说什么。但我想知道的是,当我取消连接时,是否可以立即关闭连接?显然Request.WithContext无法实现这一点。我可以在取消后得到响应,然后连接才会关闭。强制关闭连接是我的意图。@isaac:你有什么证据表明连接没有立即关闭?看起来我误解了FIN和RST标志的含义,你是对的,一旦我取消上下文,连接就关闭了。感谢您的回复。我要做的是在取消上下文后关闭连接。Transport.CancelRequest可以帮助关闭文档中所述的连接,但它看起来不是这样的,而且它已被弃用。因此,如果Request.WithContext无法帮助关闭连接,我该怎么做?谢谢@艾萨克:正如我在回答中所说的:这已经由标准http包处理了。取消上下文后,连接将关闭。此外,CancelRequest已被弃用。别再用了。好的,@Flimzy,谢谢你的回复。我明白你想说什么。但我想知道的是,当我取消连接时,是否可以立即关闭连接?显然Request.WithContext无法实现这一点。我可以在取消后得到响应,然后连接才会关闭。关闭连接的力是我的int
注意。@isaac:你有什么证据表明连接没有立即关闭?看起来我误解了FIN和RST标志的含义,你是对的,一旦我取消上下文,连接就关闭了。谢谢你的回复。