Go語言http.Get()逾時設定

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

本文中的方案是有缺陷的,本文目前只當成記錄,完整方案請參考(這篇)[http://1234n.com/?post/mwsw2r]

Go內建的http包中提供了很完整的HTTP用戶端和服務端功能。最近項目有幾個需求需要從遊戲服務端發起HTTP請求來調用電訊廠商提供的介面。用Go語言實現起來超簡單,http.Get()調一下就行了。

但是,http.Get()是沒提供參數讓調用者設定串連和讀寫的逾時,項目線上上就遇到了永久阻塞在http.Get()不返回的情況。

上網找了一下資料,最後解決了這個問題,以下是實驗代碼,先貼代碼再分析原理(gist串連):

//// How to set timeout for http.Get() in golang//package mainimport (    "io"    "io/ioutil"    "log"    "net"    "net/http"    "time")func StartTestServer() string {    http.HandleFunc("/normal", func(w http.ResponseWriter, req *http.Request) {        time.Sleep(1000 * time.Millisecond)        io.WriteString(w, "ok")    })    http.HandleFunc("/timeout", func(w http.ResponseWriter, req *http.Request) {        time.Sleep(2500 * time.Millisecond)        io.WriteString(w, "ok")    })    listener, err := net.Listen("tcp", ":0")    if err != nil {        log.Fatalf("failed to listen - %s", err.Error())    }    go func() {        err = http.Serve(listener, nil)        if err != nil {            log.Fatalf("failed to start HTTP server - %s", err.Error())        }    }()    log.Printf("start http server at http://%s/", listener.Addr())    return listener.Addr().String()}func main() {    addr := StartTestServer()    client := &http.Client{        Transport: &http.Transport{            Dial: func(netw, addr string) (net.Conn, error) {                conn, err := net.DialTimeout(netw, addr, time.Second*2)                if err != nil {                    return nil, err                }                conn.SetDeadline(time.Now().Add(time.Second * 2))                return conn, nil            },            ResponseHeaderTimeout: time.Second * 2,        },    }    // 1st request    if resp, err := client.Get("http://" + addr + "/normal"); err != nil {        log.Fatalf("1st request failed - %s", err)    } else {        result, err2 := ioutil.ReadAll(resp.Body)        if err2 != nil {            log.Fatalf("1st response read failed - %s", err2)        }        resp.Body.Close()        log.Printf("1st request - %s", result)    }    // 2nd request    if _, err := client.Get("http://" + addr + "/timeout"); err == nil {        log.Fatalf("2nd not timeout")    } else {        log.Printf("2nd request - %s", err)    }    // 3rd request    if resp, err := client.Get("http://" + addr + "/normal"); err != nil {        log.Fatalf("3rd request - %s", err)    } else {        result, err2 := ioutil.ReadAll(resp.Body)        if err2 != nil {            log.Fatalf("3rd response read failed - %s", err2)        }        resp.Body.Close()        log.Printf("3rd request - %s", result)    }}

代碼中最主要的是建立http.Client的那一段,其中自訂了http.Client的Transport,而Transport建立時指定了一個撥號回調,在撥號回調中,使用DialTimeout來支援連線逾時,當串連成功後,利用SetDeadline來讓串連支援讀寫逾時。

http包提供的http.Get實際上調用的是事先建立好的DefaultClient,而DefaultClient使用的則是預設的Transport,這一調用關係很容易從http包的代碼中看出來。

預設的Client和Transport都沒有做逾時設定,所以我們需要自己建立http.Client來實現帶逾時功能的http客戶。

http包還有一個比較容易坑到新手的坑點,就是請求後返回的http.Response,用完必須調用Body.Close(),文檔上有寫了,但是很容易被忽略,並且Close方法不是在Response類型上的,而是在Body屬性上。

如果沒有調用Body.Close(),http請求所用的tcp串連就不會釋放,最後就會出現連數過多。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.