2017-10-14 6 views
1

J'ai écrit le programme ci-dessous GO pour un test de but. Ce serveur http reçoit une requête get et effectue un appel http vers un autre service de repos. Ce programme fonctionne bien, mais quand je lance un test de charge dans la boîte de 2 Go de 2vCPUs. Il a commencé à donner Http 503 après environ 500 TPS.go lang serveur http 503 erreur avec charge

func retrievedata(w http.ResponseWriter, r *http.Request){ 

    client := &http.Client{ 
     Timeout: time.Second * 5, 
     Transport: &http.Transport{ 
      TLSClientConfig: &tls.Config{ 
       InsecureSkipVerify: true, 
      }, 
     }, 
    } 

    w.Header().Set("Content-Type", "application/json") 
    urlstring, _ := url.Parse("https://service.dot.com/backendservice/") 

    req, _ := http.NewRequest("GET", endpointurl, nil) 
    req.Header.Set("Accept", "application/json") 
    req.Header.Set("Content-Type", "application/json") 
    resp, err := client.Do(req) 
    if err != nil { 
     fmt.Println(err) 
    } 

    defer resp.Body.Close() 


    switch resp.StatusCode { 
    case 200: 

     data, _ := ioutil.ReadAll(resp.Body) 
     w.Write(data) 
    case 404: 
     w.WriteHeader(http.StatusNotFound) 
     data, _ := ioutil.ReadAll(resp.Body) 
     w.Write(data) 
    case 500: 
     w.WriteHeader(http.StatusInternalServerError) 
     data, _ := ioutil.ReadAll(resp.Body) 
     w.Write(data) 
    default: 
     w.WriteHeader(http.StatusNoContent) 
    } 

} 


func main() { 
    fmt.Println("this is a main function") 
    http.HandleFunc("/getdata", retrievedata) 
    err := http.ListenAndServe(":8191", nil) 
    if err != nil { 
     fmt.Println(err) 
    } 
    fmt.Println("Service is Running at port 8191") 
} 

J'ai ensuite ajouté routine aller pour frayer la fonction de gestionnaire

go http.HandleFunc("/getdata", retrievedata) 

cette fois que je vois une légère augmentation du TPS mais je reçois 503 erreurs après environ 600 TPS. S'il vous plaît noter que l'autre fonction de repos a été testé pour 2000TPS, donc je suis sûr qu'il n'y a pas de problème avec cela. Dois-je faire quelque chose de différent pour obtenir plus de TPS?

+0

Je recommande d'afficher toutes les erreurs et de les écrire. Pourriez-vous vérifier les ressources: ouvrir les fichiers, les sockets, etc. Ensuite, essayez d'utiliser https://golang.org/pkg/net/http/pprof/ pour rechercher le processeur et le profil de la mémoire. –

+0

'go http.HandleFunc' n'est pas une bonne idée. Le serveur http gère déjà chaque requête dans un goroutine séparé, et appeler 'go http.HandleFunc' ne le fait même pas - tout ce qu'il fait est de démarrer un goroutine pendant la phase de démarrage, qui se terminera immédiatement. En bref: que goroutine n'ajoute rien exactement (sauf peut-être une condition de course au démarrage) – Flimzy

Répondre

0

Si vous regardez transport.go vous verrez:

var DefaultTransport RoundTripper = &Transport{ 
    //... 
    MaxIdleConns:   100, 
    IdleConnTimeout:  90 * time.Second, 
    //... 
} 

// DefaultMaxIdleConnsPerHost is the default value of Transport's 
// MaxIdleConnsPerHost. 
const DefaultMaxIdleConnsPerHost = 2 

Quand il fait MaxIdleConns: 100 il met la taille du pool de connexion à 100 connexions, mais il DefaultMaxIdleConnsPerHost ensembles à 2 par hôte.

Donc, essentiellement, votre pool de connexions ne peut contenir que 2 sockets. Donc, si vous deviez faire 100 requêtes simultanées, une fois terminées, 2 des sockets resteront ouvertes dans le pool, et les 98 autres seront fermées et finiront dans l'état TIME_WAIT.

Comme cela se produit dans un goroutine dans un outil de test de charge, vous accumulez simplement des milliers de connexions dans l'état TIME_WAIT. Finalement, vous manquerez de ports éphémères et ne serez pas en mesure d'ouvrir de nouvelles connexions client.

defaultRoundTripper := http.DefaultTransport 
defaultTransportPtr, ok := defaultRoundTripper.(*http.Transport) 
if !ok { 
    panic(fmt.Sprintf("defaultRoundTripper not an *http.Transport")) 
} 
defaultTransport := *defaultTransportPtr 
defaultTransport.MaxIdleConns = 1000 
defaultTransport.MaxIdleConnsPerHost = 1000 

client = &http.Client{Transport: &defaultTransport} 

En plus de cela, une grande partie du travail que vous faites, vous n'avez pas besoin de le faire à chaque demande. Vous pouvez le faire plus comme ceci:

var client *http.Client 
var endpointurl string 
var req http.Request 

func init() { 
    defaultRoundTripper := http.DefaultTransport 
    defaultTransportPtr, ok := defaultRoundTripper.(*http.Transport) 
    if !ok { 
     panic(fmt.Sprintf("defaultRoundTripper not an *http.Transport")) 
    } 
    defaultTransport := *defaultTransportPtr 
    defaultTransport.MaxIdleConns = 1000 
    defaultTransport.MaxIdleConnsPerHost = 1000 
    defaultTransport.TLSClientConfig = &tls.Config{ 
     InsecureSkipVerify: true, 
    } 
    client = &http.Client{Transport: } 
    client = &http.Client{ 
     Timeout: time.Second * 5, 
     Transport: &defaultTransport 
    } 
    endpointurl, _ = url.Parse("https://service.dot.com/backendservice/") 
    req, _ := http.NewRequest("GET", endpointurl, nil) 
    req.Header.Set("Accept", "application/json") 
    req.Header.Set("Content-Type", "application/json") 
} 

func retrievedata(w http.ResponseWriter, r *http.Request){ 
    w.Header().Set("Content-Type", "application/json") 
    resp, err := client.Do(req) 
    if err != nil { 
     fmt.Println(err) 
    } 
    defer resp.Body.Close() 
    switch resp.StatusCode { 
    case 200: 
     data, _ := ioutil.ReadAll(resp.Body) 
     w.Write(data) 
    case 404: 
     w.WriteHeader(http.StatusNotFound) 
     data, _ := ioutil.ReadAll(resp.Body) 
     w.Write(data) 
    case 500: 
     w.WriteHeader(http.StatusInternalServerError) 
     data, _ := ioutil.ReadAll(resp.Body) 
     w.Write(data) 
    default: 
     w.WriteHeader(http.StatusNoContent) 
    } 

} 


func main() { 
    fmt.Println("this is a main function") 
    http.HandleFunc("/getdata", retrievedata) 
    err := http.ListenAndServe(":8191", nil) 
    if err != nil { 
     fmt.Println(err) 
    } 
    fmt.Println("Service is Running at port 8191") 
}