V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
howellz
V2EX  ›  Go 编程语言

golang 的 http 请求, transport.CancelRequest 与 client.Timeout 有冲突

  •  
  •   howellz · 2020-06-22 15:36:57 +08:00 · 1548 次点击
    这是一个创建于 1403 天前的主题,其中的信息可能已经有所发展或是发生改变。

    两个需求:

    1. 用户可以随时终止请求。
    2. 用户可以修改超时值,比如 20s ;

    对于第一个需求,设置了 client.Transport,并在合适的位置调用其 CancelRequest()接口; 对于第二个需求,设置了 client.Timeout ;

    但是调试中发现,如果设置了 client.Timeout,则 Transport.CancelRequest()就不再起效。 注释掉 Timeout 就可以生效。

    代码如下:

    package main
    
    import (
    	"fmt"
    	"net/http"
    	"time"
    )
    
    func main() {
    	trans := &http.Transport{}
    	client := &http.Client{
    		Transport: trans,
    		Timeout:   time.Second * (30), // 这一行不注释就无法 CancelRequest
    	}
    	req, err := http.NewRequest("GET", "https://www.google.com", nil)
    	if err != nil {
    		panic(err)
    	}
    
    	go func() {
    		time.Sleep(time.Second * time.Duration(1))
    		fmt.Printf("%v: abort\n", time.Now())
    		trans.CancelRequest(req)
    	}()
    
    	fmt.Printf("%v: start ...\n", time.Now())
    	_, err = client.Do(req)
    	fmt.Printf("%v: done\n", time.Now())
    	if err != nil {
    		fmt.Println(err.Error())
    	}
    }
    

    请问我该怎么办?

    5 条回复    2020-09-10 19:00:19 +08:00
    howellz
        1
    howellz  
    OP
       2020-06-22 15:47:40 +08:00
    对了,自己不希望用 Timeou 来调用 CanceRequest,还是希望用 client 自己的 timeout 可以么?
    guonaihong
        2
    guonaihong  
       2020-06-22 16:04:35 +08:00
    了解下 WithContext 。我在 gout 里面的超时就是用这个 API 实现的。context 可超时取消,可以调用 cancel 取消。
    这是官方 WithContext 的文档
    https://golang.google.cn/pkg/net/http/#Request.WithContext

    ## 这是 gout 里面使用 WithContext 的例子
    https://github.com/guonaihong/gout/blob/master/_example/05b-cancel.go
    SingeeKing
        3
    SingeeKing  
       2020-06-22 16:20:58 +08:00
    Deprecated: Use Request.WithContext to create a request with a cancelable context instead
    reus
        4
    reus  
       2020-06-22 20:56:15 +08:00
    howellz
        5
    howellz  
    OP
       2020-09-10 19:00:19 +08:00
    谢谢各位的回复,我找到了一个比较简单的方法:

    ```
    c := make(chan struct{})
    req.Cancel = c

    go func() {
    time.Sleep(time.Second * time.Duration(1))
    fmt.Printf("%v: abort\n", time.Now())
    //trans.CancelRequest(req)
    close(c) // 用这句取代 CancelRequest
    }()

    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5642 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 03:24 · PVG 11:24 · LAX 20:24 · JFK 23:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.