go

Reutilizando conexiones http en Golang



(4)

Actualmente estoy luchando por encontrar una forma de reutilizar las conexiones al hacer publicaciones HTTP en Golang.

Creé un transporte y un cliente así:

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

Luego estoy pasando este puntero de cliente a una rutina que está haciendo múltiples publicaciones en el mismo punto final así:

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

En cuanto a netstat, parece que esto da como resultado una nueva conexión para cada publicación, lo que da como resultado que se abra una gran cantidad de conexiones simultáneas.

¿Cuál es la forma correcta de reutilizar las conexiones en este caso?


Debe asegurarse de leer hasta que la respuesta esté completa antes de llamar a Close() .

p.ej

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

Para garantizar la reutilización de la conexión http.Client , asegúrese de hacer dos cosas:

  • Lea hasta que la respuesta esté completa (es decir, ioutil.ReadAll(resp.Body) )
  • Llamar a Body.Close()

Editar: esta es una nota más para las personas que construyen un Transporte y Cliente para cada solicitud.

Transport es la estructura que contiene conexiones para su reutilización:

http://golang.org/src/pkg/net/http/transport.go#L46

Por lo tanto, si crea un nuevo Transporte para cada solicitud, creará nuevas conexiones cada vez. En este caso, la solución es compartir la única instancia de transporte entre clientes.


IIRC, el cliente predeterminado reutiliza las conexiones. ¿Estás cerrando la response ?

Las personas que llaman deben cerrar resp. Cuerpo cuando termine de leer de él. Si resp.Body no está cerrado, el RoundTripper subyacente del Cliente (normalmente Transport) puede no ser capaz de reutilizar una conexión TCP persistente al servidor para una subsiguiente solicitud "keep-alive".


Si alguien todavía está encontrando respuestas sobre cómo hacerlo, así es como lo estoy haciendo.

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

En resumen, estoy creando un método diferente para crear un cliente HTTP y asignarlo a la variable global y luego usarlo para realizar solicitudes. Nota la

defer response.Body.Close()

Esto cerrará la conexión y la preparará para su reutilización nuevamente.

Espero que esto ayude a alguien.