I'm currently struggling to find a way to reuse connections when making HTTP posts in Go.
I've created a transport and client like so:
// Create a new transport and HTTP client tr := &http.Transport{} client := &http.Client{Transport: tr}
I'm then passing this client pointer into a goroutine which is making multiple posts to the same endpoint like so:
r, err := client.Post(url, "application/json", post)
Looking at netstat this appears to be resulting in a new connection for every post resulting in a large number of concurrent connections being open.
What is the correct way to reuse connections in this case?
HTTP persistent connection, also called HTTP keep-alive, or HTTP connection reuse, is the idea of using a single TCP connection to send and receive multiple HTTP requests/responses, as opposed to opening a new connection for every single request/response pair.
In HTTP/0.9 (not used anymore), each request used a separate TCP connection, and the end of a response was signalled by closing the connection. In HTTP/1.0, separate connections are still the official default.
Http clients are thread safe according to the docs (https://golang.org/src/net/http/client.go): Clients are safe for concurrent use by multiple goroutines.
The Keep-Alive general header allows the sender to hint about how the connection may be used to set a timeout and a maximum amount of requests. Note: Set the Connection header to "keep-alive" for this header to have any effect.
Ensure that you read until the response is complete AND call Close()
.
e.g.
res, _ := client.Do(req) io.Copy(ioutil.Discard, res.Body) res.Body.Close()
Again... To ensure http.Client
connection reuse be sure to:
ioutil.ReadAll(resp.Body)
)Body.Close()
If anyone is still finding answers on how to do it, this is how I am doing it.
package main import ( "bytes" "io/ioutil" "log" "net/http" "time" ) func httpClient() *http.Client { client := &http.Client{ Transport: &http.Transport{ MaxIdleConnsPerHost: 20, }, Timeout: 10 * time.Second, } return client } func sendRequest(client *http.Client, method string) []byte { endpoint := "https://httpbin.org/post" req, err := http.NewRequest(method, endpoint, bytes.NewBuffer([]byte("Post this data"))) if err != nil { log.Fatalf("Error Occured. %+v", err) } response, err := client.Do(req) if err != nil { log.Fatalf("Error sending request to API endpoint. %+v", err) } // Close the connection to reuse it defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { log.Fatalf("Couldn't parse response body. %+v", err) } return body } func main() { c := httpClient() response := sendRequest(c, http.MethodPost) log.Println("Response Body:", string(response)) }
Go Playground: https://play.golang.org/p/cYWdFu0r62e
In summary, I am creating a different method to create an HTTP client and assigning it to a variable, and then using it to make requests. Note the
defer response.Body.Close()
This will close the connection after the request is complete at the end of the function execution and you can reuse the client as many times.
If you want to send a request in a loop call the function that sends the request in a loop.
If you want to change anything in the client transport configuration, like add proxy config, make a change in the client config.
Hope this will help someone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With