SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

L'analyse de performance et l'optimisation sont des outils puissants pour vérifier la conformité des performances pour les clients.

L'analyse de performance peut ĂȘtre utilisĂ©e pour identifier les goulets d'Ă©tranglement dans le programme, en appliquant une approche scientifique lors de l'Ă©valuation des expĂ©riences d'optimisation. Cet article dĂ©finit une approche gĂ©nĂ©rale pour l'analyse de performance et l'optimisation, en utilisant un serveur web en Go comme exemple.

Go convient particuliĂšrement bien Ă  cette fonction, car il dispose d'outils de profilage. pprof dans la bibliothĂšque standard.

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Stratégie

Créons une liste récapitulative pour notre analyse structurelle. Nous essaierons d'utiliser des données pour prendre des décisions plutÎt que d'apporter des modifications basées sur l'intuition ou des suppositions. Pour ce faire, procédons comme suit :

  • DĂ©finissons les limites de l'optimisation (exigences) ;
  • Calculons la charge transactionnelle du systĂšme ;
  • Effectuons un test (crĂ©ons des donnĂ©es) ;
  • Observons ;
  • Analysons — toutes les exigences sont-elles respectĂ©es ?
  • Optimisons de maniĂšre scientifique, formulons une hypothĂšse ;
  • RĂ©aliser une expĂ©rience pour vĂ©rifier cette hypothĂšse.

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Architecture d'un simple serveur HTTP

Pour cet article, nous allons utiliser un petit serveur HTTP en Golang. Tout le code de cet article est disponible. ici.

L'application analysĂ©e est un serveur HTTP qui interroge PostgreSQL pour chaque requĂȘte. En complĂ©ment, nous avons Prometheus, node_exporter et Grafana pour collecter et afficher les mĂ©triques de l'application et du systĂšme.

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Pour simplifier, considérons que pour le scaling horizontal (et pour simplifier les calculs), chaque service et base de données est déployé ensemble :

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Définissons les objectifs

À cette Ă©tape, nous clarifions notre objectif. Que cherchons-nous Ă  analyser ? Comment saurons-nous qu'il est temps de conclure ? Dans cet article, nous supposerons que nous avons des clients et que notre service traitera 10 000 requĂȘtes par seconde.

Dans Google SRE Book Nous examinerons en dĂ©tail les mĂ©thodes de sĂ©lection et de modĂ©lisation. ProcĂ©dons de mĂȘme, en construisant des modĂšles :

  • Latence : 99 % des requĂȘtes doivent ĂȘtre exĂ©cutĂ©es en moins de 60 ms ;
  • CoĂ»t : le service doit consommer le montant le plus faible, que nous jugeons raisonnablement possible. Pour cela, nous maximisons le dĂ©bit ;
  • Planification des capacitĂ©s : il est nĂ©cessaire de comprendre et de documenter combien d'instances d'application doivent ĂȘtre lancĂ©es, y compris la fonction globale de mise Ă  l'Ă©chelle, ainsi que combien d'instances sont nĂ©cessaires pour rĂ©pondre aux exigences initiales de charge et garantir la redondance n+1.

La latence peut nĂ©cessiter des optimisations en plus de l'analyse, mais la bande passante doit clairement ĂȘtre analysĂ©e. Dans le cadre du processus SRE, l'exigence en matiĂšre de latence provient du client et/ou de l'entreprise, reprĂ©sentĂ©e par le propriĂ©taire du produit. Et notre service rĂ©pondra Ă  cet engagement dĂšs le dĂ©part, sans aucune configuration !

Configuration d'un environnement de test

Grùce à l'environnement de test, nous pourrons imposer une charge mesurée à notre systÚme. Des données sur les performances du service web seront créées pour l'analyse.

Charge transactionnelle

Cet environnement utilise Vegeta pour crĂ©er une frĂ©quence de requĂȘtes HTTP personnalisĂ©e jusqu'Ă  ce qu'il soit arrĂȘté :

$ make load-test LOAD_TEST_RATE=50
echo "POST http://localhost:8080" | vegeta attack -body tests/fixtures/age_no_match.json -rate=50 -duration=0 | tee results.bin | vegeta report

Surveillance

Pendant l'exĂ©cution, une charge transactionnelle sera appliquĂ©e. En plus des mĂ©triques de l'application (nombre de requĂȘtes, latences de rĂ©ponse) et du systĂšme d'exploitation (mĂ©moire, CPU, IOPS), un profilage de l'application sera lancĂ© pour comprendre oĂč se trouvent les problĂšmes et comment le temps processeur est consommĂ©.

Profilage

Le profilage est un type de mesure permettant de voir oĂč va le temps processeur lors de l'exĂ©cution de l'application. Il permet de dĂ©terminer oĂč et combien le temps processeur est dĂ©pensé :

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Ces donnĂ©es peuvent ĂȘtre utilisĂ©es lors de l'analyse pour obtenir un aperçu du temps processeur dĂ©pensĂ© inutilement et des tĂąches exĂ©cutĂ©es sans nĂ©cessitĂ©. Go (pprof) peut gĂ©nĂ©rer des profils et les visualiser sous forme de flame graph, en utilisant un ensemble d'outils standard. Je parlerai de leur utilisation et de la configuration dans un moment dans l'article.

Exécution, surveillance, analyse.

Nous allons mener une expĂ©rience. Nous exĂ©cuterons, observerons et analyserons jusqu'Ă  ce que la performance nous satisfasse. Nous choisirons une faible charge de maniĂšre alĂ©atoire pour l'utiliser afin d'obtenir les rĂ©sultats des premiĂšres observations. À chaque Ă©tape suivante, nous augmenterons la charge avec un certain facteur d'Ă©chelle, choisi avec une certaine dispersion. Chaque exĂ©cution de test de charge se fait avec un ajustement du nombre de requĂȘtes : make load-test LOAD_TEST_RATE=X.

50 requĂȘtes par seconde

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Notez les deux graphiques en haut. Le supĂ©rieur gauche montre que notre application traite 50 requĂȘtes par seconde (Ă  son avis), tandis que le supĂ©rieur droit indique la durĂ©e de chaque requĂȘte. Ces deux paramĂštres nous aident Ă  observer et Ă  analyser : sommes-nous dans nos limites de performance ou non. La ligne rouge sur le graphique Latence des requĂȘtes HTTP indique un SLO de 60 ms. On voit sur la ligne que nous sommes bien en dessous de notre temps de rĂ©ponse maximal.

Voyons du cÎté des coûts :

10000 requĂȘtes par seconde / 50 requĂȘtes par serveur = 200 serveurs + 1

Nous pouvons encore améliorer ce chiffre.

500 requĂȘtes par seconde

Des choses plus intĂ©ressantes commencent Ă  se produire lorsque la charge atteint 500 requĂȘtes par seconde :

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Encore une fois, sur le graphique supĂ©rieur gauche, on voit que l'application enregistre une charge normale. Si ce n'est pas le cas, il y a un problĂšme avec le serveur sur lequel l'application est exĂ©cutĂ©e. Le graphique de latence de rĂ©ponse en haut Ă  droite montre que 500 requĂȘtes par seconde ont conduit Ă  une latence de rĂ©ponse de 25-40 ms. Le 99Ăšme percentile reste largement dans le SLO de 60 ms choisi prĂ©cĂ©demment.

Du point de vue des coûts :

10000 requĂȘtes par seconde / 500 requĂȘtes par serveur = 20 serveurs + 1

Il est encore possible d'améliorer.

1000 requĂȘtes par seconde

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Excellente exĂ©cution ! L'application montre qu'elle a traitĂ© 1000 requĂȘtes par seconde, cependant la limite de latence a Ă©tĂ© dĂ©passĂ©e du cĂŽtĂ© du SLO. Cela se voit sur la ligne p99 du graphique supĂ©rieur droit. Bien que la ligne p100 soit bien plus haute, les latences rĂ©elles dĂ©passent le maximum de 60 ms. Plongeons dans le profilage pour voir ce que fait rĂ©ellement l'application.

Profilage

Pour le profilage, nous plaçons la charge Ă  1000 requĂȘtes par seconde, puis utilisons pprof pour collecter des donnĂ©es afin de dĂ©terminer oĂč l'application consomme du temps processeur. Cela peut ĂȘtre fait en activant le point de terminaison HTTP. pprof, puis en chargeant les rĂ©sultats avec curl :

$ curl http://localhost:8080/debug/pprof/profile?seconds=29 > cpu.1000_reqs_sec_no_optimizations.prof

Les rĂ©sultats peuvent ĂȘtre affichĂ©s comme suit :

$ go tool pprof -http=:12345 cpu.1000_reqs_sec_no_optimizations.prof

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Le graphique montre oĂč et combien l'application dĂ©pense de temps CPU. D'aprĂšs la description de Brendan Gregg:

L'axe X représente le remplissage du profil de la pile, trié par ordre alphabétique (ce n'est pas le temps), l'axe Y montre la profondeur de la pile, commençant à zéro dans [top]. Chaque rectangle représente un cadre de la pile. Plus le cadre est large, plus il apparaßt souvent dans les piles. Ce qui est en haut fonctionne sur le CPU, et en bas se trouvent les éléments enfants. Les couleurs, en général, ne signifient rien, elles sont simplement choisies au hasard pour distinguer les cadres.

Analyse — hypothùse

Pour la configuration, nous nous concentrerons sur la recherche des gaspillages inutiles de temps CPU. Nous allons chercher les plus grandes sources de dĂ©penses inutiles et les supprimer. Et Ă©tant donnĂ© que le profilage rĂ©vĂšle trĂšs prĂ©cisĂ©ment oĂč l'application dĂ©pense son temps CPU, il faudra peut-ĂȘtre le faire plusieurs fois, et il sera Ă©galement nĂ©cessaire de modifier le code source de l'application, de redĂ©marrer les tests et d'observer que les performances se rapprochent de ce qui est prĂ©vu.

En suivant les recommandations de Brendan Gregg, nous allons lire le graphique de haut en bas. Chaque ligne représente un cadre de la pile (appel de fonction). La premiÚre ligne est le point d'entrée de la programme, parent de tous les autres appels (en d'autres termes, tous les autres appels auront cette ligne dans leur pile). La ligne suivante est déjà différente :

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Si l'on survole le nom de la fonction sur le graphique, le temps total qu'elle a passĂ© dans la pile pendant le dĂ©bogage s'affiche. La fonction HTTPServe y Ă©tait prĂ©sente 65 % du temps, d'autres fonctions runtime, runtime.mcall, mstart et gc, ont pris le reste du temps. Fait intĂ©ressant : 5 % du temps total a Ă©tĂ© consacrĂ© aux requĂȘtes DNS :

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Les adresses que le programme recherche appartiennent Ă  Postgresql. Cliquez sur FindByAge:

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Il est intĂ©ressant de noter que le programme montre qu'il y a essentiellement trois sources principales qui ajoutent des dĂ©lais : l'ouverture et la fermeture des connexions, la requĂȘte de donnĂ©es et la connexion Ă  la base. Sur le graphique, on voit que les requĂȘtes DNS, l'ouverture et la fermeture des connexions reprĂ©sentent environ 13 % du temps d'exĂ©cution total.

HypothĂšse : La rĂ©utilisation des connexions via un pool devrait rĂ©duire le temps d'une requĂȘte HTTP, permettant une plus grande bande passante et des dĂ©lais plus courts..

Configuration de l'application — expĂ©rience.

Nous mettons Ă  jour le code source, essayons de supprimer la connexion Ă  Postgresql pour chaque requĂȘte. PremiĂšre option — utilisation. du pool de connexions. au niveau de l'application. Dans cette expĂ©rience, nous allons configurer un pool de connexions Ă  l'aide du pilote SQL pour Go :

db, err := sql.Open("postgres", dbConnectionString)
db.SetMaxOpenConns(8)

if err != nil {
   return nil, err
}

Exécution, observation, analyse.

AprĂšs le redĂ©marrage du test avec 1000 requĂȘtes par seconde, il est clair que le p99 des dĂ©lais est revenu Ă  la normale avec un SLO de 60 ms !

Qu'en est-il du coût ?

10000 requĂȘtes par seconde / Ă  1000 requĂȘtes par serveur = 10 serveurs + 1.

Faisons encore mieux !

2000 requĂȘtes par seconde.

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Le doublement de la charge montre la mĂȘme chose, le graphique en haut Ă  gauche montre que l'application parvient Ă  traiter 2000 requĂȘtes par seconde, p100 infĂ©rieur Ă  60 ms, p99 respecte le SLO.

Du point de vue des coûts :

10000 requĂȘtes par seconde / Ă  2000 requĂȘtes par serveur = 5 serveurs + 1.

3000 requĂȘtes par seconde.

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Ici, l'application peut traiter 3000 requĂȘtes avec un retard p99 infĂ©rieur Ă  60 ms. Le SLO n'est pas enfreint, et le coĂ»t est pris comme suit :

10000 requĂȘtes par seconde / Ă  3000 requĂȘtes par serveur = 4 serveurs + 1. (l'auteur a arrondi au supĂ©rieur, note du traducteur)

Essayons un autre round d'analyse.

Analyse — hypothùse

Nous collectons et affichons les rĂ©sultats du dĂ©bogage de l'application Ă  3000 requĂȘtes par seconde :

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

6 % du temps est encore consacré à l'établissement des connexions. La configuration du pool a amélioré la performance, mais il est clair que l'application continue de créer de nouvelles connexions avec la base.

HypothÚse : Les connexions, malgré la présence du pool, sont toujours rejetées et nettoyées, donc l'application doit les rétablir. Fixer le nombre de connexions en attente à la taille du pool devrait aider avec le délai en minimisant le temps que l'application consacre à la création de connexions..

Configuration de l'application — expĂ©rience.

Essayons de fixer MaxIdleConns à la taille du pool (décrit également ici):

db, err := sql.Open("postgres", dbConnectionString)
db.SetMaxOpenConns(8)
db.SetMaxIdleConns(8)
if err != nil {
   return nil, err
}

Exécution, observation, analyse.

3000 requĂȘtes par seconde.

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

p99 inférieur à 60 ms avec un p100 nettement inférieur !

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

La vĂ©rification du flame graph montre que l'Ă©tablissement de la connexion n'est plus perceptible ! VĂ©rifions en dĂ©tail. pg(*conn).query — nous ne remarquons Ă©galement pas d'Ă©tablissement de connexion ici.

SRE : Analyse de performance. Méthode de configuration utilisant un serveur web simple en Go

Conclusion

L'analyse de la performance est essentielle pour comprendre si les attentes des clients et les exigences non fonctionnelles sont satisfaites. L'analyse par la mise en correspondance des observations avec les attentes des clients peut aider à déterminer ce qui est acceptable et ce qui ne l'est pas. Go fournit des outils efficaces, intégrés dans la bibliothÚque standard, qui rendent l'analyse simple et accessible.

Source : habr.com

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster