Votre ordinateur portable peut-il gérer un million de requêtes HTTP simultanées ?
Imaginez envoyer 1 000 000 de requêtes HTTP simultanées à un service API REST, repoussant ainsi les limites de votre ordinateur. pour maximiser la concurrence. Bien que des outils existent pour cette tâche, voyons comment le faire dans Go à l'aide de goroutines.
package main import ( "fmt" "net/http" "runtime" "time" ) func main() { runtime.GOMAXPROCS(runtime.NumCPU()) // Use all available CPU cores transport := &http.Transport{} // Create an HTTP transport for i := 0; i < 1000000; i++ { go func() { // Start a goroutine for each HTTP request req, _ := http.NewRequest("GET", "http://myapi.com", nil) req.Header.Set("User-Agent", "custom-agent") req.SetBasicAuth("xxx", "xxx") resp, err := transport.RoundTrip(req) if err != nil { panic("HTTP request failed.") } defer resp.Body.Close() if resp.StatusCode != 302 { panic("Unexpected response returned.") } location := resp.Header.Get("Location") if location == "" { panic("No location header returned.") } fmt.Println("Location Header Value:", location) }() } time.Sleep(60 * time.Second) // Sleep for 60 seconds }
Mais attendez, il y a un problème !
Exécuter ce script entraîne des erreurs en raison des limites du descripteur de fichier. Le système ne peut tout simplement pas gérer autant de connexions simultanées.
Une solution améliorée
Pour surmonter ces limitations, nous devons utiliser une approche plus sophistiquée.
Utilisation d'un pool de répartiteurs et de travailleurs
Cette solution consiste à créer une goroutine de répartiteur qui pousse les requêtes sur un canal. Un pool de tâches de goroutines extrait les requêtes du canal, les traite et les envoie à un canal de réponse. Une goroutine de consommateur traite ensuite les réponses.
// Dispatcher function func dispatcher(reqChan chan *http.Request, reqs int) { defer close(reqChan) for i := 0; i < reqs; i++ { req, err := http.NewRequest("GET", "http://localhost/", nil) if err != nil { log.Println(err) } reqChan <- req } } // Worker function func worker(t *http.Transport, reqChan chan *http.Request, respChan chan Response) { for req := range reqChan { resp, err := t.RoundTrip(req) r := Response{resp, err} respChan <- r } } // Consumer function func consumer(respChan chan Response, reqs int) (int64, int64) { var ( conns int64 size int64 ) for conns < int64(reqs) { select { case r, ok := <-respChan: if ok { if r.err != nil { log.Println(r.err) } else { size += r.ContentLength if err := r.Body.Close(); err != nil { log.Println(r.err) } } conns++ } } } return conns, size }
Résultats
L'exécution de ce script amélioré génère des résultats impressionnants, tels que :
Connexions : 1000000
Concurrent : 200
Taille totale : 15000000 octets
Durée totale : 38 m20,3012317s
Durée moyenne : 2.280131ms
Optimisation des performances
Cependant, ajuster le nombre de requêtes simultanées et le nombre total de requêtes vous aidera à pousser les limites de votre système et testez ses capacités. N'oubliez pas qu'il s'agit d'un test extrême et qu'il peut rapidement consommer des ressources système.
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!