문제:
Go 프로그램에서 컨텍스트 기한이 있는 경우 ioutil.ReadAll()을 사용하여 응답 본문을 읽으면 예상 기한 초과 오류가 발생합니다. 그러나 json.NewDecoder(resp.Body).Decode()를 사용하면 대신 nil이 반환됩니다.
코드 예:
<code class="go">package main import ( "context" "encoding/json" "fmt" "io/ioutil" "net/http" "time" ) var url string = "http://ip.jsontest.com/" func main() { readDoesntFail() readFails() } type IpResponse struct { Ip string } func readDoesntFail() { ctx, _ := context.WithTimeout(context.Background(), time.Second*5) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { panic(err) } resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } ipResponse := new(IpResponse) time.Sleep(time.Second * 6) fmt.Println("before reading response body, context error is:", ctx.Err()) err = json.NewDecoder(resp.Body).Decode(ipResponse) if err != nil { panic(err) } fmt.Println("Expected panic but there was none") } func readFails() { ctx, _ := context.WithTimeout(context.Background(), time.Second*5) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { panic(err) } resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } time.Sleep(time.Second * 6) fmt.Println("before reading response body, context error is:", ctx.Err()) _, err = ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("received expected error", err) } }</code>
답변:
net/http 패키지에서는 요청을 처리하는 데 버퍼를 사용할 수 있습니다. 결과적으로 수신 응답 본문을 읽으려고 시도하기 전에 부분적으로 또는 전체적으로 읽고 버퍼링될 수 있습니다. 결과적으로 만료되는 컨텍스트는 본문 읽기를 완료하는 데 방해가 되지 않을 수 있습니다. 이것이 바로 이 상황에서 발생하는 현상입니다.
더 잘 이해하기 위해 의도적으로 응답을 연기하는 테스트 HTTP 서버를 생성하도록 코드를 변경해 보겠습니다.
<code class="go">ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { s := []byte(`{"ip":"12.34.56.78"}`) w.Write(s[:10]) if f, ok := w.(http.Flusher); ok { f.Flush() } time.Sleep(time.Second * 6) w.Write(s[10:]) })) defer ts.Close() url = ts.URL readDoesntFail() readFails()</code>
수정된 예에서는 JSON을 보냅니다. ip.jsontest.com의 응답과 유사한 개체이지만 플러시하기 전에 본문의 처음 10바이트만 전송합니다. 그런 다음 6초 동안 전송을 일시 중지하여 클라이언트에게 시간 초과 기회를 제공합니다.
readDoesntFail()을 실행하면 다음 동작이 관찰됩니다.
before reading response body, context error is: context deadline exceeded panic: Get "http://127.0.0.1:38230": context deadline exceeded goroutine 1 [running]: main.readDoesntFail() /tmp/sandbox721114198/prog.go:46 +0x2b4 main.main() /tmp/sandbox721114198/prog.go:28 +0x93
이 시나리오에서는 json.Decoder.Decode()는 데이터가 아직 버퍼링되지 않았기 때문에 연결에서 읽기를 시도합니다. 컨텍스트가 만료되면 연결에서 추가로 읽으면 최종 기한 초과 오류가 발생합니다. 그러나 원래 예에서 json.Decoder.Decode()는 이미 버퍼링된 데이터를 읽고 만료된 컨텍스트를 관련성이 없게 렌더링합니다.
위 내용은 `json.NewDecoder().Decode()`는 Go HTTP 요청에서 컨텍스트 마감 기한을 무시합니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!