Maison > développement back-end > Golang > le corps du texte

Requêtes multithread en cours et n'obtenant pas de RPS élevé

WBOY
Libérer: 2024-02-09 16:48:10
avant
953 Les gens l'ont consulté

go 中的多线程请求并且没有获得高 RPS

L'éditeur PHP Xigua a remarqué que lors de l'utilisation du langage Go pour effectuer des requêtes multithread, il n'est parfois pas possible d'obtenir une vitesse de requête par seconde (RPS) élevée. Bien que le langage Go soit excellent dans le traitement simultané, dans certains cas, les requêtes multithread ne sont pas très efficaces. Cela peut être dû à la latence du réseau, à la concurrence entre les ressources, etc. Dans cet article, nous allons explorer ce problème et proposer quelques solutions possibles pour améliorer le RPS des requêtes multithread en langage Go.

Contenu de la question

J'essaie d'écrire un client multithread pour tester mon serveur. Lorsque j'utilise 2 goroutines, tout va bien, j'obtiens 50 000 rps et ma charge CPU est normale, mais lorsque je crée plus de 2 goroutines, le rps tombe à 3 000 mais ma charge CPU dépasse. Bien que lorsque j'exécute le code client plusieurs fois (comme exécuter le même code sur 3 consoles en même temps), j'obtiens plus de rps, comme 80 000 rps.

Voici mon code client

package main

import (
    "fmt"
    "net/http"
    "os"
    "sync"
    "time"
)

func main() {
    requesturl := fmt.sprintf("http://localhost:%d/home", 3333)
    var wg sync.waitgroup
    wg.add(4)
    req, err := http.newrequest(http.methodget, requesturl, nil)
    if err != nil {
        fmt.printf("client: could not create request: %s\n", err)
        os.exit(1)
    }
    for i := 0; i < 4; i++ {
        go func() {
            defer wg.done()
            client := http.client{
                timeout: 30 * time.second,
            }
            for {
                client.do(req)
            }
        }()
    }
    wg.wait()
}
Copier après la connexion

Voici mon code côté serveur

package main

import (
    "errors"
    "fmt"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "log"
    "net/http"
    "os"
    "sync"
)

// log handling
func openlogfile(path string) (*os.file, error) {
    logfile, err := os.openfile(path, os.o_wronly|os.o_append|os.o_create, 0644)
    if err != nil {
        return nil, err
    }
    return logfile, nil
}

// variables of counter in metric
var okstatuscounter = prometheus.newcounter(
    prometheus.counteropts{
        name: "ok_request_count",
        help: "number of 200",
    },
)

func listener(serverlog *log.logger) http.handlerfunc {
    return func(w http.responsewriter, r *http.request) {
        //metric 
        okstatuscounter.inc()
        
        w.writeheader(http.statusok)
    }
}

func main() {
    //metric
    prometheus.mustregister(okstatuscounter)

    //log handling
    filesimpleserverlog, err := openlogfile("simpleserver/simpleserverlog.log")
    if err != nil {
        log.fatal(err)
    }
    serverlog := log.new(filesimpleserverlog, "[simple server]", log.lstdflags|log.lshortfile|log.lmicroseconds)

    var wg sync.waitgroup
    wg.add(1)

    //server:
    go func() {
        defer wg.done()
        mux := http.newservemux()
        mux.handlefunc("/home", listener(serverlog))
        mux.handle("/metrics", promhttp.handler())
        server := http.server{
            addr:    fmt.sprintf(":%d", 3333),
            handler: mux,
        }
        if err := server.listenandserve(); err != nil {
            if !errors.is(err, http.errserverclosed) {
                serverlog.printf("error running http server: %s\n", err)
            }
        }
    }()
    wg.wait()
}
Copier après la connexion

Au début, je pensais que Go pourrait utiliser un seul port pour toutes les connexions client, mais lorsque je l'ai vérifié avec netstat, il utilisait plusieurs ports. J'ai essayé de chercher mais je n'ai pas trouvé de réponse appropriée

J'ai essayé sync.mutex :

var mu sync.Mutex
...
for i := 0; i < 1000; i++ {
        go func() {
            defer wg.Done()
            client := http.Client{
                //Timeout: 30 * time.Second,
            }
            for {
                mu.Lock()
                _, err := client.Do(req)
                if err != nil {
                    clientLog.Printf("client: error making http request: %s\n", err)
                    os.Exit(1)
                }
                mu.Unlock()
            }
        }()
    }
    wg.Wait()
...
Copier après la connexion

Avec les changements ci-dessus, j'ai obtenu 13 000 rps et la charge de mon processeur était correcte, mais ce n'était tout simplement pas suffisant

Solution de contournement

Puisque vous n'envoyez des requêtes qu'à un seul hôte, la valeur par défaut du transport http ne fonctionnera pas pour toi. Il est préférable de régler les paramètres manuellement en fonction de votre situation :

t := http.DefaultTransport.(*http.Transport).Clone()
t.MaxIdleConns = 100
t.MaxConnsPerHost = 100
t.MaxIdleConnsPerHost = 100
    
httpClient = &http.Client{
  Timeout:   10 * time.Second,
  Transport: t,
}
Copier après la connexion

Pour plus d'informations, vous pouvez lire ici.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:stackoverflow.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!