Maison >développement back-end >Golang >Requêtes multithread en cours et n'obtenant pas de RPS élevé
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.
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() }
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() }
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() ...
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
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, }
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!