2016-08-31 1 views
0

Je connecte mon programme go à redis en utilisant la bibliothèque redigo. Quand je cours une demande j'obtiens des résultats corrects. Mais sur les tests de charge, en utilisant l'outil de référence apache, il fonctionne quand:Redigo: obtenir des erreurs sur les tests de charge Apache

ab -n 1000 -k -c 10 -p post.txt -T application/x-www-form-urlencoded http://localhost:8084/abcd 

Toutefois, lorsque la demande est:

ab -n 1000 -k -c 15 -p post.txt -T application/x-www-form-urlencoded http://localhost:8084/abcd 

Je reçois l'erreur:

panic: dial tcp :6379: too many open files 

Ceci est mon code:

func newPool() *redis.Pool { 
    return &redis.Pool{ 
     MaxIdle: 80, // max number of connections 
     Dial: func() (redis.Conn, error) { 
      c, err := redis.Dial("tcp", ":6379") 
      if err != nil { 
       panic(err.Error()) 
      } 
      return c, err 
     }, 
     // If Wait is true and the pool is at the MaxActive limit, then Get() waits 
     // for a connection to be returned to the pool before returning 
     Wait: true, 
    } 
} 

var pool = newPool() 

func checkError(err error) { 
    if err != nil { 
     log.Fatal(err) 
    } 
} 

func func1(pool *redis.Pool, id int) string { 
    c := pool.Get() 
    defer c.Close() 
    m, err := redis.String(c.Do("HGET", "key", id)) 
    checkError(err) 
    return m 
} 

func func2(pool *redis.Pool, string_ids []string) chan string { 
    c := make(chan string) 
    var ids []int 
    var temp int 
    for _, v := range string_ids { 
     temp, _ = strconv.Atoi(v) 
     ids = append(ids, temp) 
    } 
    go func() { 
     var wg sync.WaitGroup 
     wg.Add(len(ids)) 
     for _, v := range ids { 
      go func(id int) { 
       defer wg.Done() 
       c <- func1(pool, id) 
      }(v) 
     } 
     wg.Wait() 
     close(c) 
    }() 
    return c 
} 
func getReq(w http.ResponseWriter, req *http.Request) { 
    err := req.ParseForm() 
    checkError(err) 
    ids := req.Form["ids[]"] 
    for v := range func2(pool, ids) { 
     fmt.Println(v) 
    } 
} 

func main() { 
    http.HandleFunc("/abcd", getReq) 

    log.Fatal(http.ListenAndServe(":8084", nil)) 
} 

Comment gérer atleast 40 demande simultanée utilisant l'outil de comparaison apache.

Remarque: Je n'ai rien changé dans mon fichier Redis conf

Je reçois la réponse suivante lors de l'exécution de l'outil de référence apache. Seulement 15 demandes sont complétées.

$ ab -n 1000 -k -c 15 -p post.txt -T application/x-www-form-urlencoded http://localhost:8084/abcd 
This is ApacheBench, Version 2.3 <$Revision: 1528965 $> 
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 
Licensed to The Apache Software Foundation, http://www.apache.org/ 

Benchmarking localhost (be patient) 
apr_socket_recv: Connection refused (111) 
Total of 15 requests completed 
+0

Ce n'est pas le cas et vous montrez une erreur, c'est juste une partie de la trace de la pile. S'il vous plaît montrer la sortie d'erreur, ou l'ensemble de la trace de la pile. – JimB

+0

@JimB J'ai mis à jour la question. De plus, la trace de la pile est trop grande. J'ai ajouté les dernières lignes. – Jagrati

+1

Ce ne sont que des piles de goroutine, et n'ont rien à voir avec l'erreur. La cause de la pile trace d'abord imprimé, avec le goroutine paniquer. – JimB

Répondre

2

Pour résoudre le problème immédiatement, mis MaxActive sur votre redis.Pool, que vous mentionnez dans les commentaires mais ne vous définissez pas.

Fondamentalement, vous ne devriez pas distribuer de goroutines pour chaque recherche id. Votre simultanéité maximum possible serait alors (number of client connections) x (number of ids in the request), chacun pouvant ouvrir une nouvelle connexion redis. Il serait beaucoup plus rapide et plus efficace d'avoir une seule connexion redis lue en série sur chacune des ids. Il n'y a aucun besoin de la concurrence supplémentaire que vous avez ici, faites-le tous en série depuis le gestionnaire, et ne convertissez pas les chaînes en ints quand redis fonctionne uniquement sur les clés de chaîne pour commencer.

func getReq(w http.ResponseWriter, req *http.Request) { 
    err := req.ParseForm() 
    checkError(err) 

    c := pool.Get() 
    defer c.Close() 

    for _, id := range req.Form["ids[]"] { 
     m, err := redis.String(c.Do("HGET", "key", id)) 
     checkError(err) 
     fmt.Println(id) 
    } 
} 

Si vous souhaitez optimiser davantage, vous pouvez utiliser pipelines pour réduire les allers-retours au serveur Redis.

for _, id := range req.Form["ids[]"] { 
    c.Send("HGET", "key", id)) 
} 
c.Flush() 
values, err := redis.Strings(c.Receive()) 
checkError(err) 
... 
+0

ids que je reçois par demande de poste sont d'environ 80-100. Devrais-je toujours me connecter à redis en série? Je pense que cela va diminuer la performance. Vous avez également dit à propos de l'utilisation de pipeline, allez-vous s'il vous plaît donner un exemple de comment puis-je l'utiliser dans mon code? – Jagrati

+0

@Jagrati: oui, interroger tous les identifiants sur une connexion, l'ouverture de 1500 connexions simultanées ne fait qu'accroître les conflits sur la base de données et dégrader considérablement les performances. – JimB