Reusing http connections in the Golang

I'm currently trying to find a way to reuse connections when creating HTTP messages in the Golang.

I created the transport and the client like this:

// Create a new transport and HTTP client tr := &http.Transport{} client := &http.Client{Transport: tr} 

Then I pass this client pointer to goroutine, which makes several messages at the same endpoint as follows:

 r, err := client.Post(url, "application/json", post) 

If you look at netstat, this seems to lead to a new connection for each message, resulting in a large number of concurrent connections.

What is the correct way to reuse compounds in this case?

+62
go
Jul 30 '13 at 13:45
source share
7 answers

You must make sure that you read until the response is complete before calling Close() .

eg.

 res, _ := client.Do(req) io.Copy(ioutil.Discard, res.Body) res.Body.Close() 

To ensure the reuse of http.Client reuse, be http.Client to do two things:

  • Read until response is complete (i.e. ioutil.ReadAll(resp.Body) )
  • Call Body.Close()
+81
Jul 30 '13 at 17:27
source

Change: This is more a note for people who create Transport and Client for each request.

Edit2: changed link to godoc.

Transport is a structure that contains connections for reuse; see https://godoc.org/net/http#Transport ("By default, Transport caches connections for reuse later.")

Therefore, if you create a new transport for each request, it will create new connections each time. In this case, the solution is to split the same Transport instance between clients.

+34
Oct 23 '14 at 21:07
source

If someone still finds answers to the question of how to do this, here's how I do it.

 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 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 Play: http://play.golang.org/p/oliqHLmzSX

In general, I am creating another method for creating an HTTP client and assigning it a global variable, and then using it to make requests. note

 defer response.Body.Close() 

This will close the connection and configure it to be reused again.

Hope this helps someone.

+33
Jun 12 '15 at 16:12
source

IIRC, the default client reuses connections. Do you close the answer ?

Callers should close resp.Body when finished reading from it. If resp.Body is not closed, the Client underlying RoundTripper (usually Transport) may not be able to reuse a permanent TCP connection to the server for a subsequent keep-alive request.

+9
Jul 30 '13 at 13:50
source

about the body

 // It is the caller responsibility to // close Body. The default HTTP client Transport may not // reuse HTTP/1.x "keep-alive" TCP connections if the Body is // not read to completion and closed. 

Therefore, if you want to reuse TCP connections, you need to close the Body every time after reading until completion. For this, the ReadBody function (io.ReadCloser) is proposed.

 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 } 
+3
Dec 03 '18 at 8:12
source

Another approach to init () is to use a singleton method to get the http client. Using sync.Once, you can be sure that only one instance will be used for all your requests.

 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) // ... } 
0
May 13 '19 at 13:08
source

There are two possible ways:

  1. Use a library that internally reuses and manages the file descriptors associated with each request. The Http Client does the same inside, but then you will have control over how many concurrent connections open and how to manage your resources. If you're interested, look at a netpoll implementation that internally uses epoll / kqueue to manage them.

  2. It would be easy, instead of combining network connections, to create a working pool for your goroutines. This would be an easy and better solution that would not interfere with your current codebase and would require minor changes.

Suppose you need to make an n POST request after receiving the request.

enter image description here

enter image description here

You can use channels to implement this.

Or you can just use third-party libraries.
For example: https://github.com/ivpusic/grpool

-one
Aug 07 '18 at 10:24
source



All Articles