Golang에서 http.Get () 요청에 대한 시간 제한을 설정하는 방법은 무엇입니까?


106

Go에서 URL 가져 오기 프로그램을 만들고 있으며 가져올 URL 목록이 있습니다. http.Get()각 URL에 요청을 보내고 응답을받습니다.

resp,fetch_err := http.Get(url)

각 Get 요청에 대해 사용자 지정 시간 제한을 설정하려면 어떻게해야합니까? (기본 시간은 매우 길기 때문에 가져 오기 프로그램이 매우 느려집니다.) 내 가져 오기 프로그램이 약 40-45 초의 시간 제한을 갖기를 원합니다. 그 후 "요청 시간 초과"를 반환하고 다음 URL로 이동해야합니다.

이것을 어떻게 할 수 있습니까?


1
이 방법이 더 편리하다는 것을 여러분에게 알리기 만하면됩니다 (적어도 네트워크 문제가있는 경우 다이얼 시간 제한이 제대로 작동하지 않음). blog.golang.org/context
Audrius

@Audrius 네트워크 문제가있을 때 다이얼 시간 초과가 작동하지 않는 이유를 아십니까? 나는 같은 것을보고 있다고 생각합니다. 나는 그것이 DialTimeout의 목적이라고 생각했다?!?!
Jordan

@Jordan 나는 라이브러리 코드에 깊이 들어 가지 않았기 때문에 말하기가 어렵습니다. 아래에 내 솔루션을 게시했습니다. 나는 꽤 오랫동안 프로덕션에서 사용하고 있으며 지금까지는 "그냥 작동"(tm)합니다.
Audrius

답변:


255

분명히 Go 1.3 http.Client 에는 Timeout 필드가 있습니다.

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

그것은 나를 위해 트릭을 수행했습니다.


10
글쎄, 그것으로 충분합니다. 나는 조금 아래로 스크롤 기뻐요 :)
James Adam

5
요청마다 다른 시간 제한을 갖는 방법이 있습니까?
Arnaud Rinquin 2015 년

11
시간 초과가되면 어떻게됩니까? Get오류를 반환 합니까 ? Godoc for가 Client말하기 때문에 약간 혼란 스럽습니다 . 타이머는 Get, Head, Post 또는 Do가 돌아온 후에도 계속 실행되고 Response.Body 읽기를 중단합니다. 그래서 것을 의미하지 않습니다 중 하나 Get 또는 독서하는 것은 Response.Body오류에 의해 중단 될 수 있을까?
Avi Flax

1
질문, http.Client.Timeoutvs. 의 차이점은 무엇 http.Transport.ResponseHeaderTimeout입니까?
Roy Lee

2
@Roylee 문서에 따른 주요 차이점 중 하나 : http.Client.Timeout응답 본문을 읽는 시간을 http.Transport.ResponseHeaderTimeout포함하고 포함하지 않습니다.
imwill

53

DialTimeout 을 감싸는 사용자 지정 Dial 기능을 사용하는 자체 Transport로 자체 클라이언트 를 설정해야합니다 .

(완전히 테스트되지 않은 ) 다음 과 같은 것 :

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")
}

감사합니다! 이것이 바로 제가 찾던 것입니다.
pymd 2013-06-03

zzzz의 답변에 설명 된 Transport.ResponseHeaderTimeout보다 net.DialTimeout을 사용하는 이점은 무엇입니까?
Daniele B

4
@ 다니엘 B : 당신은 잘못된 질문을하고 있습니다. 장점이 아니라 각 코드가하는 일에 관한 것입니다. DialTimeouts는 서버를 연결할 수없는 경우 시작되고, 설정된 연결에 대한 특정 작업이 너무 오래 걸리면 다른 시간 제한이 시작됩니다. 대상 서버가 빠르게 연결을 설정 한 다음 느린 금지를 시작하면 다이얼 시간 제한이 도움이되지 않습니다.
Volker

1
@Volker, 답변 주셔서 감사합니다. 실제로 나는 그것을 깨달았다 : 그것은 Transport.ResponseHeaderTimeout이 읽기 타임 아웃을 설정하는 것처럼 보인다. 즉, 연결이 설정된 후 타임 아웃이고, 당신은 다이얼 타임 아웃이다. dmichael의 솔루션은 다이얼 시간 초과와 읽기 시간 초과를 모두 처리합니다.
Daniele B

1
@Jonno : Go에는 캐스트가 없습니다. 이것은 유형 변환입니다.
Volker 2013

31

Volker의 답변에 추가하려면 연결 제한 시간 외에도 읽기 / 쓰기 제한 시간을 설정하려면 다음과 같이 할 수 있습니다.

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),
        },
    }
}

이 코드는 테스트되었으며 프로덕션에서 작동 중입니다. 테스트가 포함 된 전체 요지는 여기 https://gist.github.com/dmichael/5710968 에서 확인할 수 있습니다.

conn.SetDeadline향후 지점을 참조하는 각 요청에 대해 새 클라이언트를 만들어야합니다.time.Now()


conn.SetDeadline의 반환 값을 확인해야하지 않습니까?
Eric Urban

3
이 시간 제한은 Keepalive 연결에서 작동하지 않습니다. 이것이 기본값이며 대부분의 사람들이 제가 생각하는 것을 사용해야합니다. 이 문제를 해결하기 위해 제가 생각 해낸 내용
xitrium 2014

추가 입력에 대해 @xitrium과 Eric에게 감사드립니다.
dmichael

각 요청에 대해 새 클라이언트를 만들어야한다고 말씀하신 것과는 다른 것 같습니다. Dial은 매번 다시 전화를받는 기능이기 때문에 동일한 클라이언트에서 각 요청을 보냅니다.
A-letubby

매번 새로운 고객이 필요하십니까? 전화를 걸 때마다 net.Dial을 사용하는 대신 TimeoutDialer가 빌드하는 함수를 사용합니다. 그것은 새로운 time.Now () 호출에서 매번 기한이 평가되는 새로운 연결입니다.
Blake Caldwell

16

요청별로 수행하려면 간결성을 위해 오류 처리가 무시됩니다.

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
추가 정보 : 문서 당 Context에 의해 부과 된 기한은 http.Client.Timeout.
kubanczyk

1
Go 1.7 이상에 대해 허용되는 답변 이어야합니다 . 이동 1.13+ 약간 사용이 단축 될 수 있습니다 들어 NewRequestWithContext
kubanczyk

9

빠르고 더러운 방법 :

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

이것은 조정없이 전역 상태를 변경하는 것입니다. 그러나 URL 가져 오기 프로그램에는 괜찮을 수 있습니다. 그렇지 않으면의 비공개 인스턴스를 만듭니다 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)
...

위의 내용은 테스트되지 않았습니다.


누구든지 나를 정정하십시오. 그러나 ResponseHeaderTimeout은 읽기 시간 초과, 즉 연결이 설정된 후 시간 초과에 관한 것 같습니다. 가장 포괄적 인 솔루션은 다이얼 타임 아웃과 읽기 타임 아웃을 모두 설정할 수있는 @dmichael의 솔루션 인 것 같습니다.
Daniele B

http.DefaultTransport.(*http.Transport).ResponseHeaderTimeout = time.Second * 45요청 시간 초과에 대한 쓰기 테스트를 많이 도와주세요. 대단히 감사합니다.
lee


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

이것은 도움이 될 수 있지만 ResponseHeaderTimeout연결이 설정된 후에 만 ​​시작됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.