问题背景
在go语言的http服务中,我们常常会使用到Context来取消一个请求,或者取消数据的读取。偶然的一次尝试,让我对Context有了一定的兴趣。接下来本文围绕下面的例子,分析http如何利用Context来控制请求的取消和影响数据读取。
例子
我们开启一个http服务,发送大量数据给每个请求,代码如下:
srv.go:http服务
package main
import (
"fmt"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 100*10000; i++ {
w.Write([]byte("hello world"))
}
}
func main() {
fmt.Println("listening 8888:")
http.HandleFunc("/hello", hello)
_ = http.ListenAndServe(":8888", nil)
}
client.go: 发送请求的客户端
package main
import (
"context"
"fmt"
"io"
"log"
"net/http"
"time"
)
func main() {
client := http.Client{
}
request, err := http.NewRequest(http.MethodPost, "http://127.0.0.1:8888/hello", nil)
ctx, cancelFunc := context.WithCancel(request.Context())
request = request.WithContext(ctx)
if err != nil {
return
}
response, err := client.Do(request)
if err != nil {
log.Fatal(err)
}
cache := make([]byte, 128)
timer := time.NewTimer(time.Millisecond)
go func() {
select {
case <-timer.C:
cancelFunc()
}
}()
for {
read, err := response.Body.Read(cache)
if err == nil {
fmt.Println(string(cache[:read]))
continue
}
if err == io.EOF {
fmt.Println(string(cache[:read]))
break
}
log.Fatal(err)
}
}
代码很简单,就不做注释啦。分别启动服务和client,我们将得到如下结果:

我们看到这句话Process finished with the exit code 1,程序非正常退出,那么首先是追踪这个错误,下面我们追踪这个错误。
错误追踪
首先清楚这个“context canceled” 是客户端打印出来的:
log.Fatal(err)
// 这个错误来源于读取Response中的数据时得到错误,而且这个错误非io.EOF错误
断点入口:
read, err := response.Body.Read(cache)
我们会进入transport.go文件中:
func (es *bodyEOFSignal) Read(p []byte) (n int, err error) {
// 这里表明我们读取的body是bodyEOFSignal类型
es.mu.Lock()
closed, rerr := es.closed, es.rerr
es.mu.Unlock()
if closed {
return 0, errReadOnClosedResBody
}
if rerr != nil {
return 0, rerr
}
n, err = es.body.Read(p)// 我们在这里读到了错误,这里是什么错误,在后面将会介绍
if err != nil {
es.mu.Lock()
defer es.mu.Unlock()
if es.rerr == nil {
es.rerr = err
}
err

本文深入剖析了Go语言HTTP服务中如何利用Context来取消请求,并通过一个例子展示了错误追踪的过程。当客户端使用带Cancel的Context发起请求,服务端发送大量数据时,客户端可以通过调用Cancel函数中断请求。错误追踪从客户端的Context出发,经过http.Client.Do方法,进入Transport的RoundTrip过程,最终在持久连接的readLoop中感知到Context的取消,关闭连接,导致数据读取时出现网络错误。整个过程涉及到了Context、Transport、持久连接和错误处理机制的交互,揭示了Go HTTP库的精细设计和错误处理逻辑。
最低0.47元/天 解锁文章
902

被折叠的 条评论
为什么被折叠?



