Golang에서 http 연결 재사용
저는 현재 Golang에서 HTTP 게시물을 만들 때 연결을 재사용하는 방법을 찾기 위해 고군분투하고 있습니다.
다음과 같이 전송 및 클라이언트를 만들었습니다.
// Create a new transport and HTTP client
tr := &http.Transport{}
client := &http.Client{Transport: tr}
그런 다음이 클라이언트 포인터를 같은 엔드 포인트에 여러 게시물을 만드는 고 루틴에 전달합니다.
r, err := client.Post(url, "application/json", post)
netstat를 살펴보면 모든 게시물에 대해 새로운 연결이 발생하여 많은 수의 동시 연결이 열리는 것으로 보입니다.
이 경우 연결을 재사용하는 올바른 방법은 무엇입니까?
당신은 당신이 있는지 확인해야합니다 응답이 완료 될 때까지 읽어 호출하기 전에 Close()
.
예 :
res, _ := client.Do(req)
io.Copy(ioutil.Discard, res.Body)
res.Body.Close()
http.Client
연결 재사용 을 보장하려면 다음 두 가지를 수행해야합니다.
- 응답이 완료 될 때까지 (즉, 읽기
ioutil.ReadAll(resp.Body)
) - 요구
Body.Close()
누군가가 그것을 수행하는 방법에 대한 답을 여전히 찾고 있다면 이것이 내가하는 방법입니다.
package main
import (
"bytes"
"io/ioutil"
"log"
"net/http"
"time"
)
var httpClient *http.Client
const (
MaxIdleConnections int = 20
RequestTimeout int = 5
)
func init() {
httpClient = createHTTPClient()
}
// createHTTPClient for connection re-use
func createHTTPClient() *http.Client {
client := &http.Client{
Transport: &http.Transport{
MaxIdleConnsPerHost: MaxIdleConnections,
},
Timeout: time.Duration(RequestTimeout) * time.Second,
}
return client
}
func main() {
endPoint := "https://localhost:8080/doSomething"
req, err := http.NewRequest("POST", endPoint, bytes.NewBuffer([]byte("Post this data")))
if err != nil {
log.Fatalf("Error Occured. %+v", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
response, err := httpClient.Do(req)
if err != nil && response == nil {
log.Fatalf("Error sending request to API endpoint. %+v", err)
}
// Close the connection to reuse it
defer response.Body.Close()
// Let's check if the work actually is done
// We have seen inconsistencies even when we get 200 OK response
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatalf("Couldn't parse response body. %+v", err)
}
log.Println("Response Body:", string(body))
}
Go Playground : http://play.golang.org/p/oliqHLmzSX
요약하면 HTTP 클라이언트를 만들고이를 전역 변수에 할당 한 다음이를 사용하여 요청하는 다른 방법을 만들고 있습니다. 참고
defer response.Body.Close()
이렇게하면 연결이 닫히고 다시 사용할 준비가됩니다.
이것이 누군가를 도울 수 있기를 바랍니다.
편집 : 이것은 모든 요청에 대해 전송 및 클라이언트를 구성하는 사람들을위한 메모입니다.
Edit2 : 링크를 godoc로 변경했습니다.
Transport
재사용을 위해 연결을 보유하는 구조체입니다. 참조 https://godoc.org/net/http#Transport ( "기본적으로 운송 미래의 재사용에 대한 연결을 캐시합니다.")
따라서 각 요청에 대해 새 전송을 생성하면 매번 새로운 연결이 생성됩니다. 이 경우 해결 방법은 클라이언트간에 하나의 전송 인스턴스를 공유하는 것입니다.
IIRC, 기본 클라이언트 는 연결을 재사용합니다. 응답 을 닫으 십니까?
발신자는 읽기가 끝나면 resp.Body를 닫아야합니다. resp.Body가 닫히지 않으면 클라이언트의 기본 RoundTripper (일반적으로 전송)가 후속 "연결 유지"요청을 위해 서버에 대한 영구 TCP 연결을 재사용하지 못할 수 있습니다.
몸에 관하여
// It is the caller's responsibility to
// close Body. The default HTTP client's Transport may not
// reuse HTTP/1.x "keep-alive" TCP connections if the Body is
// not read to completion and closed.
따라서 TCP 연결을 재사용하려면 읽기 완료 후 매번 Body를 닫아야합니다. 이와 같이 ReadBody (io.ReadCloser) 함수를 제안합니다.
package main
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"time"
)
func main() {
req, err := http.NewRequest(http.MethodGet, "https://github.com", nil)
if err != nil {
fmt.Println(err.Error())
return
}
client := &http.Client{}
i := 0
for {
resp, err := client.Do(req)
if err != nil {
fmt.Println(err.Error())
return
}
_, _ = readBody(resp.Body)
fmt.Println("done ", i)
time.Sleep(5 * time.Second)
}
}
func readBody(readCloser io.ReadCloser) ([]byte, error) {
defer readCloser.Close()
body, err := ioutil.ReadAll(readCloser)
if err != nil {
return nil, err
}
return body, nil
}
에 대한 또 다른 접근 방식 init()
은 단일 메서드를 사용하여 http 클라이언트를 가져 오는 것입니다. sync.Once를 사용하면 모든 요청에 하나의 인스턴스 만 사용되도록 할 수 있습니다.
var (
once sync.Once
netClient *http.Client
)
func newNetClient() *http.Client {
once.Do(func() {
var netTransport = &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
}).Dial,
TLSHandshakeTimeout: 2 * time.Second,
}
netClient = &http.Client{
Timeout: time.Second * 2,
Transport: netTransport,
}
})
return netClient
}
func yourFunc(){
URL := "local.dev"
req, err := http.NewRequest("POST", URL, nil)
response, err := newNetClient().Do(req)
// ...
}
가능한 두 가지 방법이 있습니다.
Use a library that internally reuses and manages the file descriptors, associated with each requests. Http Client does the same thing internally, but then you would have the control over how many concurrent connections to open, and how to manage your resources. If you are interested, look at the netpoll implementation, which internally uses epoll/kqueue to manage them.
The easy one would be, instead of pooling network connections, create a worker pool, for your goroutines. This would be easy, and better solution, that would not hinder with your current codebase, and would require minor changes.
Let's assume you need to make n POST request, after you recieve a request.
You could use channels, to implement this.
Or, simply you could use third party libraries.
Like: https://github.com/ivpusic/grpool
참고URL : https://stackoverflow.com/questions/17948827/reusing-http-connections-in-golang
'Nice programing' 카테고리의 다른 글
Django : FloatField 또는 DecimalField for Currency? (0) | 2020.11.16 |
---|---|
숫자 키패드의 keyCode 값? (0) | 2020.11.16 |
mongodb 컬렉션에서 최대 가치를 얻는 방법 (0) | 2020.11.16 |
정규식의 유효성을 어떻게 확인할 수 있습니까? (0) | 2020.11.16 |
구성 변환을 사용하여 ConnectionString을 제거하는 방법 (0) | 2020.11.16 |