Le pilier performance se concentre sur la conception, le développement, la validation et l’exploitation des solutions qui répondent à leurs exigences non fonctionnelles en matière de performance (généralement associée aux temps de réponse), de capacité (magnitudes de charge supportées, base d’utilisateurs et débits atteints) et d’évolutivité (capacité à répondre de manière organique à la demande variable et aux charges croissantes). Contrairement aux environnements informatiques « traditionnels », composés d’une infrastructure à capacité fixe, les environnements de cloud hybride permettent aux solutions d’augmenter et de diminuer dynamiquement leur consommation de capacité et de ressources selon les fluctuations de la demande, à condition que les solutions soient conçues pour tirer parti de ces capacités.
L’analyse de la performance permet également d’améliorer l’expérience utilisateur via des améliorations de la conception de produit fondées sur des preuves, et d’atteindre les objectifs métier grâce à l’évolutivité et à la capacité intégrées.
Recueillez les attentes des utilisateurs lors de la phase de définition du produit, quantifiez les exigences métier et utilisez-les comme base pour l’architecture et la conception ultérieures du produit.
Les composants d’une solution bien conçue peuvent être mis à l’échelle de façon indépendante (par exemple, ajouter une autre instance d’un service), mais l’ajout ou la suppression de composants peut avoir des répercussions sur d’autres parties de la solution. Par exemple, ajouter un autre serveur Web pour gérer un pic de trafic peut nécessiter plus de files d’attente de messages pour communiquer avec les services back-en. Connaître à l’avance les dépendances de la mise à l’échelle permet de comprendre le comportement des solutions et d’éviter l’épuisement des ressources en surdimensionnant une seule ressource.
Les solutions cloud hybrides bien architecturées profitent des architectures multi-plateformes pour mettre en œuvre des stratégies de mise à l’échelle et de bursting, afin d’optimiser la performance ainsi que la sécurité, les coûts d’exploitation et les attentes des utilisateurs finaux. Par exemple, exécuter des workloads sur une infrastructure sur site avec de fortes garanties de performance et des coûts d’exploitation fixes, et procéder au bursting/mettre à l’échelle vers un service cloud public lors des pics de workload.
Déplacer des données coûte cher. Les solutions bien architecturées profitent de la portabilité et de la mobilité des workloads conteneurisées pour placer les services au plus près des données qu’ils consomment.
Les solutions doivent choisir la bonne plateforme et les bonnes ressources pour maximiser la valeur de leur architecture. Une solution cloud hybride peut couvrir plusieurs clouds, y compris une infrastructure sur site, ce qui donne aux architectes la liberté de sélectionner la combinaison de ressources optimale pour répondre aux besoins de performance de leur solution.
La performance doit être « intégrée » à la solution dès sa conception. En laissant les considérations de performance pour la fin de la conception, ou pire, de sa mise en œuvre, on obtient souvent des résultats sous-optimaux qui ne peuvent pas être corrigés sans revoir de grandes parties de l’architecture de la solution. Les pratiques de conception aident les architectes à créer des solutions hautement performantes et à éviter les approches susceptibles de limiter la performance.
Les solutions doivent être conçues pour augmenter ou réduire leur capacité de traitement en ajoutant ou en supprimant des unités discrètes (serveurs, services, interfaces réseau, etc.), au lieu de modifier la capacité des unités existantes en ajoutant, par exemple, des processeurs à un serveur. Pour ce faire, les solutions doivent adopter les principes d’architecture suivants :
Les composants sans état sont des composants qui ne conservent ni l’état du client ni celui de la session (par exemple, l’identité d’un utilisateur ou les entrées de données fournies lors de l’appel précédent) entre les interactions, éliminant ainsi les dépendances entre les clients et toute instance spécifique d’un composant. Cette absence de dépendance entre les composants et leurs consommateurs signifie que la solution peut se mettre à l’échelle en ajoutant ou en supprimant des instances de composants, sans aucun impact sur les consommateurs de ces services. Un bon exemple de composant sans état est celui d’un caissier en magasin : tant qu’il y a au moins un caissier disponible, les acheteurs peuvent passer à la caisse et payer leurs courses, et des caissiers peuvent être ajoutés et retirés selon le volume d’acheteurs. L’inverse serait que les acheteurs se voient attribuer un caissier au début de leur parcours d’achat. Si le caissier désigné est bloqué ou, pire encore, indisponible, les clients doivent attendre ou recommencer, ce qui nuit à la performance globale du magasin (mesurée en nombre de clients par heure).
Éviter les tâches de longue durée. Si une solution doit prendre en charge des tâches de longue durée (par exemple, effectuer un calcul scientifique complexe), la tâche doit être conçue pour supporter le dimensionnement grâce à une installation d’interruption et de point de contrôle qui permet à la solution d’arrêter et de redémarrer la tâche au fur et à mesure que des ressources sont ajoutées et retirées de la solution.
Les données aux extrémités. Les composants sans état peuvent, en théorie, évoluer à l’infini et peuvent être réutilisés d’un client à l’autre. Les solutions hautement performantes transmettent l’état, c’est-à-dire les données utilisateur et application, vers l’application client et les bases de données situées aux extrémités de l’architecture de la solution, sans conserver d’état dans les couches architecturales intermédiaires.
Ressources :
Avec ou sans état
Concevoir les solutions comme un ensemble de composants hautement cohérents et faiblement couplés permet de mettre à l’échelle les composants de façon indépendante, selon la demande du service qu’ils fournissent. Les approches architecturales telles que l’architecture orientée services et les microservices intègrent cette pratique comme principe fondamental de conception, à savoir un ensemble de services hautement cohérents communiquant via des API de haut niveau faiblement couplées.
Le transfert de données entre les composants d’une solution est souvent l’élément qui prend le plus de temps lors d’une transaction. Les composants doivent être conçus pour optimiser la fréquence et le volume des communications selon la bande passante disponible. Par exemple, une application qui effectue des appels répétés pour récupérer des valeurs individuelles à partir d’une base de données peut être « suffisamment » performante lorsqu’elle est déployée sur un réseau local, mais peut prendre du retard lorsque le composant de base de données est déplacé vers un fournisseur de services cloud.
Le style architectural REST (Representational State Transfer), couramment utilisé dans les applications Web, est un bon exemple du type d’équilibre qu’offre une solution bien conçue ; l’état représentatif complet d’une ressource est transféré sous la forme d’un document JSON, XML ou autre, ce qui permet de concilier la quantité d’informations transférées et la latence élevée d’une interaction Web.
La mise en cache permet de limiter la demande de ressources et de services qui produisent des données. Il est intéressant d’utiliser la mise en cache pour les données relativement statiques de longue durée et/ou les données « chères » à produire. Les solutions bien architecturées mettent en œuvre un mécanisme de mise en cache pour chaque couche ; elles placent les caches aussi près que possible du consommateur, afin de limiter les communications entre le consommateur et le cache et d’améliorer le temps de réponse global.
Les architectes doivent garder à l’esprit que la mise en cache peut être excessive. Un mécanisme de mise en cache mal conçu ou un cache trop grand peuvent nuire à la performance globale de la solution. Les architectes doivent évaluer le type et la stratégie de mise en cache, puis mesurer l’efficacité du cache lors des tests et des analyses de performance.
La messagerie asynchrone utilisant des files d’attente de messages, des modèles de rappel ou d’autres moyens permet aux solutions de s’adapter efficacement et de se dégrader sous charge lorsque les ressources sont épuisées. Les solutions bien architecturées tirent parti de la communication asynchrone, en particulier des files d’attente de messages, pour offrir aux utilisateurs finaux une expérience adaptative et éviter de « perdre » les requêtes utilisateur en cas de défaillance d’un composant. Ce même mécanisme peut également être utilisé pour interconnecter des systèmes ayant des niveaux de service ou des heures de fonctionnement différents. Par exemple, une application Web disponible 24 h/24 et 7 j/7 qui est connectée à un système d’enregistrement fonctionnant de 9h à 17h avec une file d’attente de messages, pourra accepter les requêtes des utilisateurs finaux même lorsque le système d’enregistrement est indisponible.
Les solutions évoluent avec le temps, et leur performance peut changer en conséquence. Intégrer des instruments de performance pour que les équipes de développement, de test et d’exploitation puissent collecter de manière non intrusive les indicateurs de performance de l’application, permet de développer et de tester un produit robuste utilisant des méthodes fondées sur des preuves. L’instrumentation contribue également au functional testing, à l’analyse des défauts et constitue une aide précieuse pour maintenir la performance des solutions et identifier les sources des problèmes de performance en production. L’instrumentation configurable et non intrusive prend en charge la surveillance des produits, garantit l’observabilité de la solution en exploitation et soutient ainsi les équipes DevOps et SRE.
La planification, le test et l’analyse de la performance sont un ensemble de pratiques et d’approches appliquées aux solutions informatiques dans le but de garantir leur qualité et leur capacité à atteindre les résultats métier escomptés.
Habituellement, l’analyse s’applique à des attributs de qualité tels que la performance, la capacité et l’évolutivité de la solution, ainsi que certains aspects de la disponibilité, de la continuité d’activité et de la durabilité en général. L’analyse comprend l’identification et la quantification des exigences métier liées à la qualité, la conception et l’exécution des tests pour récupérer les indicateurs qui reflètent la performance de la solution par rapport à un ensemble d’attentes telles que les temps de réponse, les débits ou les charges supportées.
En outre, dans un sens plus large, la portée de la performance comprend l’analyse de la capacité de la solution, le nombre total d’unités de travail qu’elle peut servir, son évolutivité (sa capacité à répondre aux variations de la demande). L’analyse de performance sert également à prouver que les produits restent fonctionnels et stables dans des conditions d’exploitation extrêmes. L’analyse de la performance ne vise pas seulement à dresser un tableau des performances de la solution, mais aussi à identifier les goulets d’étranglement et à favoriser la collaboration avec les parties prenantes pour améliorer la qualité et la facilité d’utilisation.
Compte tenu de la nature complexe et holistique de la gestion de la performance et des capacités des produits, elle doit couvrir les différentes phases du SDLC, de la conception du produit à la SRE en passant par l’exploitation. Cela garantit une gestion adéquate des besoins clients, une détection précoce des problèmes et une réponse rapide aux incidents de production.
Du point de vue commercial, la performance de la solution dans son ensemble est importante, ce qui doit se refléter dans des exigences non fonctionnelles holistiques. Cependant, pour les tests de performance au niveau unitaire, les tests de performance précoces mettant en œuvre le paradigme shift left et pour l’analyse des causes racines des problèmes de performance, il peut s’avérer nécessaire de spécifier des exigences de bas niveau limitant la durée des appels individuels, la latence réseau, etc.
Ainsi, les exigences de performance de haut niveau sont généralement fournies au niveau du processus ou de la transaction, par exemple « Le processus d’origine du prêt doit se terminer en moins de 2 minutes », sans tenir compte de la manière dont la performance des étapes et sous-processus individuels contribue au résultat final. La création d’un budget de performance qui attribue des objectifs à chaque étape du processus de développement fournit des cibles mesurables pour les équipes de développement de fonctionnalités, aide à identifier les zones problématiques potentielles et permet de concentrer l’optimisation des performances et les efforts de résolution là où ils sont les plus bénéfiques.
Les budgets de performance doivent prendre en compte toutes les couches de la solution, du matériel jusqu’au code de l’application. Si vous omettez l’un de ces éléments, la solution risque de ne pas répondre aux attentes des utilisateurs.
Lors des tests de performance, il n’y a qu'en testant la solution de bout en bout qu’il est possible de s'assurer qu’elle répond à ses exigences. Cela reflète la nature holistique de l’analyse des performances. Le test des composants individuels (par exemple la base de données, le middleware, etc.) fournit des informations précieuses pour l’analyse des performances d’une solution et aide à identifier les goulets d’étranglement en matière de performances. Mais le test des composants ne suffit pas, car les interactions entre les composants peuvent entraîner des goulets d’étranglement inattendus ou d’autres obstacles susceptibles d’aboutir à des résultats médiocres.
Se concentrer sur la perception de l’utilisateur signifie se concentrer principalement sur les temps de réponse perçus et la réactivité globale de l’interface utilisateur. La dégradation de la capacité est généralement invisible pour les utilisateurs jusqu’à ce que les performances du produit soient affectées. Une étude réalisée en 1968 a révélé qu’il existe trois ordres de grandeur différents dans les interactions entre l’homme et la machine :
Il en ressort qu’un temps de réponse de 2 secondes serait idéal, et donc que 2 secondes ou un peu plus constituent un bon objectif de temps de réponse pour les solutions de cloud hybride, dans la mesure du possible. Bien sûr, les attentes des utilisateurs dépendent de ce qu’ils font. Par exemple, personne ne s’attend à ce que l’animation après l’appui sur un bouton dure 2 secondes, mais 2 à 3 secondes constituent une bonne cible générale pour les applications destinées aux utilisateurs.
Dans le prolongement de ce principe, les solutions doivent intégrer des tests de temps de réponse de l’utilisateur dans leur cycle d’assurance qualité en utilisant des outils de test de l’interface utilisateur. Même si les latences des appels d’API sont importantes pour les performances globales du produit, la performance perçue par l’utilisateur est essentielle pour attirer et fidéliser la base d’utilisateurs.
Les utilisateurs ont souvent des attentes différentes quant à ce qui constitue une « bonne » performance. Par exemple, un « utilisateur expérimenté » qui utilise une application plusieurs fois par jour et quotidiennement a des attentes de performance très différentes de quelqu’un qui utilise la même application peut-être une fois par mois. Les utilisateurs sont également souvent confrontés à la difficulté de quantifier ce que signifie pour eux une « bonne » performance, et se retrouvent souvent bloqués sur des exigences telles que « suffisamment rapide » (un objectif difficile à atteindre). De plus, la perception individuelle des performances des produits destinés aux utilisateurs varie d’une personne à l’autre. Par exemple, pour certains, un temps de connexion de 10 secondes est acceptable (surtout s’il s’agit d’un événement unique), pour d’autres, c’est peut-être bien trop lent (surtout si la connexion fait souvent partie du workflow).
Pour quantifier et gérer les attentes des utilisateurs, il est recommandé de :
Surveillez et communiquez les limites de performance aux clients.
Une mauvaise utilisation ou configuration d’un produit ou d’un composant de la solution peut être à l’origine de mauvaises performances et d’une expérience utilisateur négative. Pour éviter cette situation, les architectes doivent :
Être conscients des contraintes de performance de la solution et les communiquer aux utilisateurs de manière proactive. Par exemple, si une solution utilise un canal de communication lent ou à faible bande passante, l’architecte doit informer les utilisateurs finaux que le téléchargement d’images à haute résolution en pâtira.
Permettre aux systèmes de détecter et de communiquer lorsque les requêtes ne sont pas conformes aux paramètres de conception de la solution, dans la mesure du possible. Par exemple, le système doit avertir activement les utilisateurs lorsqu’ils tentent de télécharger des fichiers volumineux sur un canal lent.
Une approche courante des tests de performance consiste à tester que la solution atteint ses objectifs de temps de réponse à la charge maximale prévue ; l’hypothèse étant que si la solution fonctionne bien à la charge maximale attendue, elle fonctionnera également correctement pour les charges inférieures à celle-ci. Le problème de cette approche est qu’elle ne fournit qu’un seul point de données par pic de charge testé, comme s’il s’agissait d’un exercice réussite/échec.
Une solution bien conçue est testée à l’aide de l’approche exploratoire pour vérifier sa réactivité dans une gamme de charges de taille variable, de divers types d’utilisateurs et de différentes fonctions testées. L’équipe chargée de la solution dispose ainsi d’informations précieuses et variées sur la manière dont les composants de la solution interagissent pour influer sur les performances, sur les goulets d’étranglement potentiels et sur la manière de dimensionner la solution pour faire face à des workloads plus ou moins importants.
Cette approche permet d’éviter les tests supplémentaires en cas d’évolution des cibles de charge attendues et de nécessité de recueillir des indicateurs de performance sous différentes conditions de charge. L’interpolation/extrapolation des dépendances existantes de la performance aux magnitudes de charge permet de calculer des indicateurs de performance pour toute charge dans le spectre des charges initialement testées (de zéro aux magnitudes du point de rupture, et au-delà).
L’approche typique des tests de performance suit le schéma simplifié « Mesurer les temps de réponse à des charges/débits donnés ». Elle permet de savoir si le temps de réponse des transactions clés lors des pics d’activité satisfait aux accords de niveau de service (SLA) existants. En général, les magnitudes testées sont limitées à « faible », « pic » et « contrainte ». Cette approche peut répondre aux questions sur les temps de réponse à des charges typiques, mais elle ne donne pas une image complète des performances du système dans toutes les situations possibles.
L’approche plus avancée des *tests de performance exploratoires* vise à créer un *aperçu de performance* de la solution testée. L’instantané comprend un ensemble complet d’indicateurs de performance, rassemblés sur le plus large éventail de charges pris en charge, des mesures pour un seul utilisateur aux charges postérieures au point de rupture (si possible, sauf en cas de panne du système). Il s’agit notamment de recueillir les temps de réponse des transactions, les débits des transactions et des données, ainsi que les données relatives à la consommation des ressources recueillies dans des conditions de charge croissantes. Par « transaction », nous entendons une tâche finie effectuée par le système, qu’il s’agisse de macro-transactions comme la connexion ou la mise à jour du compte, de sous-transactions individuelles (comme l’appel d’authentification au sein de la transaction de connexion) ou de simples résultats HTTP.
L’ensemble complet de données de performance, l’instantané de performance, inclut les indicateurs de performance ci-dessus pour la plage de charge « linéaire » à faible charge (où les fils traités individuellement ne ressentent pas la présence les uns des autres et les temps de réponse n’augmentent pas avec l’augmentation de la charge), la plage « non linéaire » où les temps de réponse augmentent à mesure que la charge augmente, les points de saturation, où les débits cessent d’augmenter avec l’accroissement de la charge et l’atteinte des niveaux de saturation, et une plage postérieure au point de rupture où les performances diminuent une fois que les débits ont atteint leur maximum et que les temps de réponse dépassent les niveaux SLA.
Couvrir toute la gamme des charges ne demande généralement pas plus d’efforts de la part des équipes de test que les simples tests aux niveaux de « pic » et de « contrainte » et les tests d’endurance, car les mêmes scripts de test sont utilisés (et l’effort principal consiste généralement à créer ces scripts). Mais les avantages de la création d’un tel instantané de performance sont les suivants :
Il n’est pas nécessaire de consacrer du temps et des efforts à essayer de deviner la bonne configuration de test qui produit le « bon » débit de transaction (« pic » ou « contrainte »), il vous suffit d’incrémenter votre charge et de couvrir *tous* les volumes de charge et les débits pris en charge.
Il est possible de déterminer à partir de quelles charges les temps de réponse commencent à augmenter, où ils dépassent les niveaux SLA et où les débits atteignent les niveaux maximaux.
Cela permet de mesurer directement la capacité du système, par exemple l’ampleur de la charge à laquelle la condition du point de rupture est atteinte (temps de réponse dépasse le SLA, ou les débits atteignent des niveaux maximaux, ou l’utilisation des ressources du système entre dans la « zone rouge » spécifiée, par exemple l’utilisation de l’unité centrale atteint 90 %, ou le système tombe en panne, selon la première éventualité). Cela signifie qu’il n’est pas nécessaire d’utiliser les approches habituelles basées sur des conjectures pour l’analyse et la planification des capacités.
En cas d’évolution des conditions attendues de l’opération de production (par exemple, les charges moyennes et maximales attendues sont redéfinies par l’entreprise), il n’est pas nécessaire de refaire les tests de performance : les indicateurs de performance recherchés pour les différentes ampleurs de charge peuvent être simplement obtenus par interpolation ou extrapolation des résultats de test existants.
Couvrir tous les spectres de charges au lieu de quelques magnitudes prédéfinies nous permet d’avoir une vue d’ensemble des performances du système et de ne passer à côté d’aucun problème de performance éventuel en ne testant pas le produit dans des conditions extrêmes.
Veiller à atteindre les points de rupture des performances du système lors des tests signifie que nous savons où se trouvent les goulots d’étranglement des performances, quel lien, quel composant ou quelle couche échouera en premier à mesure que les charges augmentent. Cela permet de fournir des commentaires utiles et factuels aux équipes d’architecture et de conception afin d’améliorer la robustesse et la performance du produit.
Plusieurs types de tests de performance peuvent être exécutés sur une solution. Une solution bien conçue les exploite tous.
Tâchez de tirer le meilleur parti de la variété des données de performance obtenues lors des tests :