Làm cách nào để đặt thời gian chờ cho các yêu cầu http.Get () trong Golang?


106

Tôi đang tạo một trình tìm nạp URL trong Go và có một danh sách các URL để tìm nạp. Tôi gửi http.Get()yêu cầu đến từng URL và nhận được phản hồi của họ.

resp,fetch_err := http.Get(url)

Làm cách nào để đặt thời gian chờ tùy chỉnh cho mỗi Yêu cầu nhận? (Thời gian mặc định rất lâu và điều đó làm cho trình tìm nạp của tôi thực sự chậm.) Tôi muốn trình tìm nạp của mình có thời gian chờ khoảng 40-45 giây, sau đó nó sẽ trả về "request đã hết thời gian chờ" và chuyển sang URL tiếp theo.

Làm thế nào tôi có thể đạt được điều này?


1
Chỉ cần để cho các bạn biết rằng tôi thấy cách này thuận tiện hơn (thời gian chờ quay số không hoạt động tốt nếu có những vấn đề mạng, ít nhất là đối với tôi): blog.golang.org/context
Audrius

@Audrius Bạn có biết tại sao thời gian chờ quay số không hoạt động khi có sự cố mạng không? Tôi nghĩ tôi đang thấy điều tương tự. Tôi nghĩ đó là những gì DialTimeout dùng để làm gì?!?!
Jordan

@Jordan Khó nói vì tôi đã không đi sâu vào mã thư viện. Tôi đã đăng giải pháp của tôi dưới đây. Tôi đang sử dụng nó trong sản xuất khá lâu và cho đến nay nó vẫn "hoạt động" (tm).
Audrius

Câu trả lời:


255

Rõ ràng trong Go 1.3 http.Client có trường Timeout

client := http.Client{
    Timeout: 5 * time.Second,
}
client.Get(url)

Đó là hoàn thành thủ thuật cho tôi.


10
Chà, vậy là đủ tốt cho tôi. Vui vì tôi cuộn xuống một chút :)
James Adam

5
Có cách nào để có thời gian chờ khác cho mỗi yêu cầu không?
Arnaud Rinquin

10
Điều gì xảy ra khi hết giờ? Có Gettrả lại lỗi không? Tôi hơi bối rối vì Godoc cho Clientbiết: Bộ đếm thời gian vẫn chạy sau khi Nhận, Đầu, Đăng hoặc Thực hiện trở lại và sẽ làm gián đoạn việc đọc Phản hồi.Body. Vì vậy, điều đó có nghĩa là một trong hai Get hoặc quá trình đọc Response.Bodycó thể bị gián đoạn do lỗi?
Avi Flax,

1
Câu hỏi, sự khác biệt giữa những gì http.Client.Timeoutvs http.Transport.ResponseHeaderTimeout?
Roy Lee

2
@Roylee Một trong những điểm khác biệt chính theo tài liệu: http.Client.Timeoutbao gồm thời gian để đọc nội dung phản hồi, http.Transport.ResponseHeaderTimeoutkhông bao gồm nó.
imwill

53

Bạn cần thiết lập Ứng dụng khách của riêng mình với Giao thông vận tải của riêng bạn , sử dụng chức năng Quay số tùy chỉnh bao quanh DialTimeout .

Một cái gì đó như (hoàn toàn chưa được kiểm tra ) thế này :

var timeout = time.Duration(2 * time.Second)

func dialTimeout(network, addr string) (net.Conn, error) {
    return net.DialTimeout(network, addr, timeout)
}

func main() {
    transport := http.Transport{
        Dial: dialTimeout,
    }

    client := http.Client{
        Transport: &transport,
    }

    resp, err := client.Get("http://some.url")
}

Cảm ơn rất nhiều! Đây chính xác là thứ tôi đang tìm kiếm.
pymd

lợi thế của việc sử dụng net.DialTimeout so với Transport.ResponseHeaderTimeout được mô tả bởi câu trả lời của zzzz là gì?
Daniele B

4
@Daniel B: Bạn đang hỏi sai câu hỏi. Nó không phải là về lợi thế mà là về những gì mỗi mã làm. DialTimeouts nhảy vào nếu máy chủ không thể được bỏ qua trong khi các thời gian chờ khác bắt đầu nếu các hoạt động nhất định trên kết nối được thiết lập mất quá nhiều thời gian. Nếu máy chủ mục tiêu của bạn thiết lập kết nối nhanh chóng nhưng sau đó bắt đầu cấm bạn chậm, thời gian chờ quay số sẽ không giúp ích được gì.
Volker

1
@Volker, cảm ơn câu trả lời của bạn. Trên thực tế, tôi cũng nhận ra điều đó: có vẻ như Transport.ResponseHeaderTimeout đặt thời gian chờ đọc, đó là thời gian chờ sau khi kết nối được thiết lập, trong khi của bạn là thời gian chờ quay số. Giải pháp của dmichael giải quyết cả thời gian chờ quay số và thời gian chờ đọc.
Daniele B

1
@Jonno: Không có phôi trong cờ vây. Đây là những chuyển đổi kiểu.
Volker

31

Để thêm vào câu trả lời của Volker, nếu bạn cũng muốn đặt thời gian chờ đọc / ghi ngoài thời gian chờ kết nối, bạn có thể làm như sau

package httpclient

import (
    "net"
    "net/http"
    "time"
)

func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
    return func(netw, addr string) (net.Conn, error) {
        conn, err := net.DialTimeout(netw, addr, cTimeout)
        if err != nil {
            return nil, err
        }
        conn.SetDeadline(time.Now().Add(rwTimeout))
        return conn, nil
    }
}

func NewTimeoutClient(connectTimeout time.Duration, readWriteTimeout time.Duration) *http.Client {

    return &http.Client{
        Transport: &http.Transport{
            Dial: TimeoutDialer(connectTimeout, readWriteTimeout),
        },
    }
}

Mã này đã được thử nghiệm và đang được sản xuất. Ý chính đầy đủ với các bài kiểm tra có sẵn tại đây https://gist.github.com/dmichael/5710968

Lưu ý rằng bạn sẽ cần tạo một ứng dụng khách mới cho mỗi yêu cầu vì ứng dụng conn.SetDeadlinenày tham chiếu đến một điểm trong tương lai từtime.Now()


Bạn không nên kiểm tra giá trị trả về của conn.SetDeadline?
Eric Urban

3
Thời gian chờ này không hoạt động với các kết nối lưu giữ, đây là mặc định và những gì hầu hết mọi người nên sử dụng mà tôi tưởng tượng. Dưới đây là những gì tôi đã đưa ra để đối phó với điều này: gist.github.com/seantalts/11266762
xitrium

Cảm ơn @xitrium và Eric đã cung cấp thêm thông tin.
dmichael

Tôi cảm thấy có vẻ như không phải như bạn nói rằng chúng tôi sẽ cần tạo một khách hàng mới cho mỗi yêu cầu. Vì Quay số là một chức năng mà tôi nghĩ rằng nó sẽ nhận được cuộc gọi mỗi khi bạn gửi lại từng yêu cầu trong cùng một ứng dụng khách.
A-letubby

Bạn chắc chắn bạn cần một khách hàng mới mỗi lần? Mỗi lần quay số, thay vì sử dụng net.Dial, nó sẽ sử dụng chức năng mà TimeoutDialer xây dựng. Đó là một kết nối mới, với thời hạn được đánh giá mỗi lần, từ một thời điểm mới.
Blake Caldwell,

16

Nếu bạn muốn thực hiện theo yêu cầu, hãy bỏ qua việc xử lý lỗi cho ngắn gọn:

ctx, cncl := context.WithTimeout(context.Background(), time.Second*3)
defer cncl()

req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://google.com", nil)

resp, _ := http.DefaultClient.Do(req)

1
Thông tin bổ sung: mỗi tài liệu, thời hạn do Ngữ cảnh áp đặt bao gồm việc đọc Phần nội dung, tương tự như http.Client.Timeout.
kubanczyk

1
Nên là một câu trả lời được chấp nhận cho Go 1.7+. Đối với Go 1.13+ có thể được rút ngắn hơi sử dụng NewRequestWithContext
kubanczyk

9

Một cách nhanh chóng và bẩn thỉu:

http.DefaultTransport.(*http.Transport).ResponseHeaderTimeout = time.Second * 45

Đây là trạng thái toàn cầu thay đổi bất kỳ sự phối hợp nào. Tuy nhiên, nó có thể ổn đối với trình tìm nạp url của bạn. Nếu không, hãy tạo một phiên bản riêng của http.RoundTripper:

var myTransport http.RoundTripper = &http.Transport{
        Proxy:                 http.ProxyFromEnvironment,
        ResponseHeaderTimeout: time.Second * 45,
}

var myClient = &http.Client{Transport: myTransport}

resp, err := myClient.Get(url)
...

Không có gì ở trên đã được thử nghiệm.


Xin bất cứ ai sửa cho tôi, nhưng có vẻ như ResponseHeaderTimeout nói về thời gian chờ đọc, đó là thời gian chờ sau khi kết nối được thiết lập. Giải pháp toàn diện nhất dường như là giải pháp của @dmichael, vì nó cho phép đặt cả thời gian chờ quay số và thời gian chờ đọc.
Daniele B

http.DefaultTransport.(*http.Transport).ResponseHeaderTimeout = time.Second * 45giúp tôi rất nhiều trong kiểm tra viết cho thời gian chờ yêu cầu. Cảm ơn rât nhiều.
lee


-1
timeout := time.Duration(5 * time.Second)
transport := &http.Transport{Proxy: http.ProxyURL(proxyUrl), ResponseHeaderTimeout:timeout}

Điều này có thể hữu ích, nhưng lưu ý rằng nó ResponseHeaderTimeoutchỉ bắt đầu sau khi kết nối được thiết lập.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.