2017-07-26 3 views
0

Le programme génère simultanément de nombreux goroutines (getStock), ce qui, selon moi, entraîne l'abandon immédiat de la connexion par le serveur distant. Je n'essaie pas de créer un DOS, mais je veux toujours obtenir des données de manière agressive sans avoir d'erreurs de 'réinitialisation de connexion'.golang: stratégies pour empêcher la réinitialisation de la connexion par des erreurs d'homologues

Quelles sont certaines stratégies pour n'avoir au plus que N (par exemple 20) connexions simultanées? Existe-t-il une file d'attente intégrée pour les requêtes GET dans le client HTTP golang? Je continue d'apprendre que ce serait génial de comprendre s'il existe de meilleurs modèles de conception pour ce type de code.

Sortie

$ go run s1w.go 
sl(size): 1280 
body: "AAPL",17.92 
body: "GOOG",32.13 
body: "FB",42.02 
body: "AMZN",195.83 
body: "GOOG",32.13 
body: "AMZN",195.83 
body: "GOOG",32.13 
body: "FB",42.02 
body: "AAPL",17.92 
2017/07/26 00:01:23 NFLX: Get http://goanuj.freeshell.org/go/NFLX.txt: read tcp 192.168.86.28:56674->205.166.94.30:80: read: connection reset by peer 
2017/07/26 00:01:23 AAPL: Get http://goanuj.freeshell.org/go/AAPL.txt: read tcp 192.168.86.28:56574->205.166.94.30:80: read: connection reset by peer 
2017/07/26 00:01:23 NFLX: Get http://goanuj.freeshell.org/go/NFLX.txt: read tcp 192.168.86.28:56760->205.166.94.30:80: read: connection reset by peer 
2017/07/26 00:01:23 FB: Get http://goanuj.freeshell.org/go/FB.txt: read tcp 192.168.86.28:56688->205.166.94.30:80: read: connection reset by peer 
2017/07/26 00:01:23 AMZN: Get http://goanuj.freeshell.org/go/AMZN.txt: read tcp 192.168.86.28:56689->205.166.94.30:80: read: connection reset by peer 
2017/07/26 00:01:23 AAPL: Get http://goanuj.freeshell.org/go/AAPL.txt: read tcp 192.168.86.28:56702->205.166.94.30:80: read: connection reset by peer 

s1.go

package main 
import (
     "fmt" 
     "io/ioutil" 
     "log" 
     "net/http" 
     "time" 
) 

// https://www.youtube.com/watch?v=f6kdp27TYZs (15m) 
// Generator: function that returns a channel 
func getStocks(sl []string) <-chan string { 
     c := make(chan string) 
     for _, s := range sl { 
       go getStock(s, c) 
     } 
     return c 
} 

func getStock(s string, c chan string) { 
     resp, err := http.Get("http://goanuj.freeshell.org/go/" + s + ".txt") 
     if err != nil { 
       log.Printf(s + ": " + err.Error()) 
       c <- err.Error() // channel send 
       return 
     } 
     body, _ := ioutil.ReadAll(resp.Body) 
     resp.Body.Close() // close ASAP to prevent too many open file desriptors 
     val := string(body) 
     //fmt.Printf("body: %s", val) 
     c <- val // channel send 
     return 
} 

func main() { 
     start := time.Now() 
     var sl = []string{"AAPL", "AMZN", "GOOG", "FB", "NFLX"} 
     // creates slice of 1280 elements 
     for i := 0; i < 8; i++ { 
       sl = append(sl, sl...) 
     } 
     fmt.Printf("sl(size): %d\n", len(sl)) 

     // get channel that returns only strings 
     c := getStocks(sl) 
     for i := 0; i < len(sl); i++ { 
       fmt.Printf("%s", <-c) // channel recv 
     } 

     fmt.Printf("main: %.2fs elapsed.\n", time.Since(start).Seconds()) 
} 

Répondre

1

Au lieu de tourner de nouveaux goroutines pour chaque demande, créez une piscine fixe au démarrage de votre programme et passer des commandes sur une canal partagé. Chaque commande serait une structure correspondant aux paramètres actuellement passés à getStock. Les choses deviennent plus compliquées si vous devez être capable de tuer le pool, mais ce n'est pas si difficile ...

Fondamentalement, votre nouveau gestionnaire serait une boucle, lisant un ordre d'un canal partagé par tous les gestionnaires, l'exécuter, puis envoyer le résultat sur le canal de réponse de la commande.

+0

Tuer la piscine est facile: fermez le canal dans lequel vous faites passer des tâches. – Adrian

+0

Avez-vous un exemple de code ou des liens qui montre comment faire cela? – AG1

+1

Jetez un oeil à [cet article sur les bassins de travailleurs] (https://gobyexample.com/worker-pools), il explique tout de beaucoup plus de détails que vous pourriez jamais avoir besoin :) –

0

Vous devez utiliser un canal mis en mémoire tampon pour limiter le nombre d'opérations parallèles. Avant de commencer un nouveau goroutine dans la boucle, vous devez envoyer dans ce canal et recevoir de celui-ci après l'appel terminé, donc il va libérer une place et une nouvelle demande pourra commencer. Découvrez votre code modifié on playground