97 votes

Réutiliser les connexions http en Go

Je m'efforce actuellement de trouver un moyen de réutiliser les connexions lors de la création de messages HTTP en Go.

J'ai créé un transport et un client comme ceci :

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

Je passe ensuite ce pointeur client dans une goroutine qui envoie plusieurs messages au même point de terminaison, comme suit :

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

En regardant netstat, il semble qu'il y ait une nouvelle connexion pour chaque message, ce qui entraîne un grand nombre de connexions simultanées ouvertes.

Quelle est la manière correcte de réutiliser les connexions dans ce cas ?

3voto

cyaconi Points 13

Une autre approche pour init() est d'utiliser une méthode singleton pour obtenir le client http. En utilisant sync.Once, vous pouvez être sûr qu'une seule instance sera utilisée pour toutes vos requêtes.

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)
    // ...
}

1voto

Fulldump Points 81

Le point manquant ici est l'aspect "goroutine". Transport a son propre pool de connexion, par défaut chaque connexion dans ce pool est réutilisée (si le corps est entièrement lu et fermé) mais si plusieurs goroutines envoient des requêtes, de nouvelles connexions seront créées (le pool a toutes les connexions occupées et en créera de nouvelles). Pour résoudre ce problème, vous devrez limiter le nombre maximum de connexions par hôte : Transport.MaxConnsPerHost ( https://golang.org/src/net/http/transport.go#L205 ).

Vous voulez probablement aussi configurer IdleConnTimeout et/ou ResponseHeaderTimeout .

0voto

yeqown Points 21

https://golang.org/src/net/http/transport.go#L196

vous devez définir MaxConnsPerHost explicitement à votre http.Client . Transport réutilise la connexion TCP, mais vous devez limiter les MaxConnsPerHost (par défaut 0 signifie aucune limite).

func init() {
    // singleton http.Client
    httpClient = createHTTPClient()
}

// createHTTPClient for connection re-use
func createHTTPClient() *http.Client {
    client := &http.Client{
        Transport: &http.Transport{
            MaxConnsPerHost:     1,
            // other option field
        },
        Timeout: time.Duration(RequestTimeout) * time.Second,
    }

    return client
}

0voto

Chandan Kumar Points 427

C'est une fonction très utile pour l'appel GO http, vous pouvez garder la connexion en vie et la réutiliser.

    var (
        respReadLimit       = int64(4096)
    )

    // Try to read the response body so we can reuse this connection.
    func (c *Client) drainBody(body io.ReadCloser) error {
        defer body.Close()
        _, err := io.Copy(ioutil.Discard, io.LimitReader(body, respReadLimit))
        if err != nil {
            return err
        }
        return nil
    }

-3voto

Il y a deux possibilités :

  1. Utilisez une bibliothèque qui réutilise et gère en interne les descripteurs de fichiers, associés à chaque demande. Http Client fait la même chose en interne, mais vous auriez alors le contrôle sur le nombre de connexions simultanées à ouvrir, et sur la façon de gérer vos ressources. Si vous êtes intéressé, regardez l'implémentation de netpoll, qui utilise en interne epoll/kqueue pour les gérer.

  2. Le plus simple serait, au lieu de mettre en commun les connexions réseau, de créer un pool de travailleurs, pour vos goroutines. Ce serait une solution facile et meilleure solution, qui ne gênerait pas votre codebase actuel, et qui nécessiterait des changements mineurs.

Supposons que vous ayez besoin de faire une demande POST, après avoir reçu une demande.

enter image description here

enter image description here

Vous pourriez utiliser des canaux pour mettre cela en œuvre.

Ou, tout simplement, vous pouvez utiliser des bibliothèques tierces.
Comme : https://github.com/ivpusic/grpool

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X