<p>I was reading the <a href="https://blog.golang.org/context" rel="nofollow">Go Concurrency Patterns: Context</a> blog post and while most of it made sense I was a bit confused by how to cancel a request with contexts, especially in the example given:</p>
<pre><code>func httpDo(ctx context.Context, req *http.Request, f func(*http.Response, error) error) error {
// Run the HTTP request in a goroutine and pass the response to f.
tr := &http.Transport{}
client := &http.Client{Transport: tr}
c := make(chan error, 1)
go func() { c <- f(client.Do(req)) }()
select {
case <-ctx.Done():
tr.CancelRequest(req)
<-c // Wait for f to return.
return ctx.Err()
case err := <-c:
return err
}
}
</code></pre>
<p>From my understand the <strong>httpDo</strong> function make a request in another Go routine then blocks in the select statement until either something is returned either on the context Done channel or the error channel. If something on the Done channel is received (like a timeout or cancelation), you wait for the Go routine to finish, then return an error. That all makes sense and is nice code.</p>
<p>However looking at the documentation for <strong>CancelRequest</strong> seems to indicate that it should not be used anymore.</p>
<blockquote>
<p>func (t *Transport) CancelRequest(req *Request)</p>
<p>CancelRequest cancels an in-flight request by closing its connection. CancelRequest should only be called after RoundTrip has returned.</p>
<p>Deprecated: Use Request.WithContext to create a request with a cancelable context instead. CancelRequest cannot cancel HTTP/2 requests.</p>
</blockquote>
<p>The correct way seems to be to call the <strong>context.CancelFunc</strong> that is returned when you create a context. If that were the case, wouldn't the correct version of <strong>httpDo</strong> be something along the lines of the following:</p>
<pre><code>func httpDo(ctx context.Context, req *http.Request, f func(*http.Response, error) error) error {
// Run the HTTP request in a goroutine and pass the response to f.
tr := &http.Transport{}
client := &http.Client{Transport: tr}
c := make(chan error, 1)
innerCtx, cancel := context.WithValue(ctx, nil, nil)
defer cancel()
req = req.WithContext(innerCtx)
go func() { c <- f(client.Do(req)) }()
select {
case <-innerCtx.Done():
cancel()
<-c // Wait for f to return.
return ctx.Err()
case err := <-c:
return err
}
}
</code></pre>
<p>Any suggestions about where I am going wrong in understanding the blog post example?</p>
<hr/>**评论:**<br/><br/>pbo_: <pre><p>When this blog post was written, std library didn't have support for contexts, so they had to handle request cancelation manually. Nowadays, the correct way is to use Request.WithContext.</p></pre>pkieltyka: <pre><p>I suggest trying the <code>golang.org/x/net/context/ctxhttp</code> package, source at: <a href="https://github.com/golang/net/tree/master/context/ctxhttp" rel="nofollow">https://github.com/golang/net/tree/master/context/ctxhttp</a> </p>
<p>think of it as net/http + context for client requests, and look at the _test.go files for example usage.</p></pre>pkieltyka: <pre><p>and for an example on how to handle http request cancellation / timeout look at.. <a href="https://github.com/pressly/chi/blob/master/_examples/limits/main.go#L56" rel="nofollow">https://github.com/pressly/chi/blob/master/_examples/limits/main.go#L56</a> and <a href="https://github.com/pressly/chi/blob/master/middleware/timeout.go" rel="nofollow">https://github.com/pressly/chi/blob/master/middleware/timeout.go</a></p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传