分享
  1. 首页
  2. 文章

Go语言http.Get()超时设置(更新)

达达 · · 9255 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

上次写了一遍日志分享http.Get()设置超时的方案(上篇文章),后来自己过了一遍代码发现逻辑上有问题。

在Dail之后设置了Deadline,之后就没再重新设置。这对于不重用连接的http请求是没有问题的,但是Go的http库是支持keep-alive的,可以重用TCP/IP连接。这意味着一个连接过了超时时间后再被使用,就会出现超时错误,因为没有再重置超时时间。

拿上次的实验代码,在发送请求时加一个keep-alive头,然后每次发送请求前加个Sleep,就可以重新以上情况。

怎样做到每次使用一个连接发送和接收前就设置超时呢?我想了个办法是在Dial回调返回自己包装过的TimeoutConn,间接的调用真实的Conn,这样就可以再每次Read和Write之前设置超时时间了。

以下是修改后的实验代码:

//
// How to set timeout for http.Get() in golang
//
package main
import (
 "io"
 "io/ioutil"
 "log"
 "net"
 "net/http"
 "sync"
 "time"
)
type TimeoutConn struct {
 conn net.Conn
 timeout time.Duration
}
func NewTimeoutConn(conn net.Conn, timeout time.Duration) *TimeoutConn {
 return &TimeoutConn{
 conn: conn,
 timeout: timeout,
 }
}
func (c *TimeoutConn) Read(b []byte) (n int, err error) {
 c.SetReadDeadline(time.Now().Add(c.timeout))
 return c.conn.Read(b)
}
func (c *TimeoutConn) Write(b []byte) (n int, err error) {
 c.SetWriteDeadline(time.Now().Add(c.timeout))
 return c.conn.Write(b)
}
func (c *TimeoutConn) Close() error {
 return c.conn.Close()
}
func (c *TimeoutConn) LocalAddr() net.Addr {
 return c.conn.LocalAddr()
}
func (c *TimeoutConn) RemoteAddr() net.Addr {
 return c.conn.RemoteAddr()
}
func (c *TimeoutConn) SetDeadline(t time.Time) error {
 return c.conn.SetDeadline(t)
}
func (c *TimeoutConn) SetReadDeadline(t time.Time) error {
 return c.conn.SetReadDeadline(t)
}
func (c *TimeoutConn) SetWriteDeadline(t time.Time) error {
 return c.conn.SetWriteDeadline(t)
}
func main() {
 client := &http.Client{
 Transport: &http.Transport{
 Dial: func(netw, addr string) (net.Conn, error) {
 log.Printf("dial to %s://%s", netw, addr)
 conn, err := net.DialTimeout(netw, addr, time.Second*2)
 if err != nil {
 return nil, err
 }
 return NewTimeoutConn(conn, time.Second*2), nil
 },
 ResponseHeaderTimeout: time.Second * 2,
 },
 }
 addr := StartTestServer()
 SendTestRequest(client, "1st", addr, "normal")
 SendTestRequest(client, "2st", addr, "normal")
 SendTestRequest(client, "3st", addr, "timeout")
 SendTestRequest(client, "4st", addr, "normal")
 time.Sleep(time.Second * 3)
 SendTestRequest(client, "5st", addr, "normal")
}
func StartTestServer() string {
 listener, err := net.Listen("tcp", ":0")
 if err != nil {
 log.Fatalf("failed to listen - %s", err.Error())
 }
 wg := new(sync.WaitGroup)
 wg.Add(1)
 go func() {
 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")
 })
 wg.Done()
 err = http.Serve(listener, nil)
 if err != nil {
 log.Fatalf("failed to start HTTP server - %s", err.Error())
 }
 }()
 wg.Wait()
 log.Printf("start http server at http://%s/", listener.Addr())
 return listener.Addr().String()
}
func SendTestRequest(client *http.Client, id, addr, path string) {
 req, err := http.NewRequest("GET", "http://"+addr+"/"+path, nil)
 if err != nil {
 log.Fatalf("new request failed - %s", err)
 }
 req.Header.Add("Connection", "keep-alive")
 switch path {
 case "normal":
 if resp, err := client.Do(req); err != nil {
 log.Fatalf("%s request failed - %s", id, err)
 } else {
 result, err2 := ioutil.ReadAll(resp.Body)
 if err2 != nil {
 log.Fatalf("%s response read failed - %s", id, err2)
 }
 resp.Body.Close()
 log.Printf("%s request - %s", id, result)
 }
 case "timeout":
 if _, err := client.Do(req); err == nil {
 log.Fatalf("%s request not timeout", id)
 } else {
 log.Printf("%s request - %s", id, err)
 }
 }
}

有疑问加站长微信联系(非本文作者)

本文来自:达达的主页

感谢作者:达达

查看原文:Go语言http.Get()超时设置(更新)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
9255 次点击
1 回复 | 直到 2025年05月11日 10:06:46
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏