lundi , 23 septembre 2019
Home » Articles » Amélioration des performances du Google App Engine

Amélioration des performances du Google App Engine

Tout au long de cet article, beaucoup d’attention est accordée à l’optimisation des performances. En améliorant les performances, vous bénéficiez de l’avantage supplémentaire de réduire les coûts d’utilisation de votre application lorsque vous dépassez le quota gratuit d’App Engine. Cet article explique les caractéristiques de performance spécifiques à l’environnement Google App Engine. En premier lieu, on va voir le processus de démarrage et d’arrêt des instances dans le cloud. Le coût de démarrage d’une instance est démontré en montrant les performances d’une servlet par l’utilisation d’une bibliothèque tierce par rapport aux performances d’une servlet de base. Cet article propose également des pointeurs pour minimiser et, si possible, pour éviter les démarrages à froid. Enfin, allons voir un aperçu de haut niveau des sujets liés à la performance.

Obtenir des performances dans le cloud

L’un des bons points et uniques de l’infonuage par rapport à l’hébergement traditionnel est l’évolutivité et la flexibilité élevées pour répondre aux changements de la demande de votre application. Le modèle de tarification de l’infonuage est particulièrement pratique si vous subissez des augmentations soudaines du nombre de visiteurs sur une base régulière.

Dans le cloud, vous payez pour ce que vous utilisez. Sur App Engine, cela signifie que si votre trafic est généralement inférieur au quota quotidien gratuit de Google et que vous n’avez que des augmentations de trafic accidentelles, vous ne payez que pour la puissance de calcul utilisée pendant les jours avec des trafics élevés. L’avantage de l’infonuage par rapport à un parc des machines physique capable de gérer les pointes de trafic élevé est que vous ne payez pas pour les machines qui restent inactives, sauf pendant un pic de trafic.

Cette flexibilité introduit également un nouveau défi qui pourrait ne pas être apparent à première vue. Répondre aux changements de la demande signifie démarrer et arrêter les instances plusieurs fois par heure. Le temps nécessaire pour répondre à un changement de demande est directement lié au temps nécessaire pour démarrer votre application web. Cela signifie que votre application web ne devient pas nécessairement flexible et évolutive simplement parce qu’elle est déployée sur App Engine. Vous devez optimiser votre application pour tirer le meilleur parti des circonstances spécifiques de l’exécution sur Google App Engine.

Comparaison entre App Engine et les applications Web traditionnelles

Alors que la durée de vie d’une instance App Engine typique est mesurée en minutes et en heures, la durée de vie d’une instance d’application Web traditionnelle est mesurée en semaines ou en mois. Une application Web traditionnelle signifie ici une application Web exécutée sur une machine physique que vous gérez vous-même plutôt qu’une application s’exécutant dans le cloud.

L’une des approches les plus courantes pour optimiser les performances d’une application Web traditionnelle consiste à obtenir une performance au démarrage de l’instance. Par exemple, si vous chargez un grand nombre de classes et de données dans la mémoire au démarrage, vous pouvez économiser du temps de chargement lors du traitement des requêtes utilisateur car le démarrage et l’arrêt d’une instance d’application ne sont pas liés à la gestion d’une requête.

Prendre un coup de performance lors du démarrage d’une nouvelle instance n’est cependant pas une bonne idée, si un visiteur du site Web attend pendant le démarrage de votre application. Vous risquez de perdre un visiteur chaque fois qu’une nouvelle instance est lancée.

En outre, les exigences d’évolutivité d’App Engine nécessitent des stratégies de stockage différentes. La plupart des applications Web traditionnelles sont basées sur des bases de données relationnelles. Les stratégies d’utilisation optimale d’une base de données relationnelle peuvent parfois être catastrophiques lorsqu’elles sont appliquées à des stockages NoSQL tels que le magasin de données Google App Engine.

Par conséquent, les frameworks des applications Web conçus à l’origine pour une utilisation avec des piles de logiciels peuvent entraîner de mauvais résultats lorsqu’ils sont utilisés sur App Engine sans tenir compte de ces composants.

Optimisation des paiements pour les ressources

Sur App Engine, vous payez pour les ressources que vous utilisez. Cela signifie que l’optimisation de votre application pour utiliser les ressources entraîne également des réductions de coûts.

Sur App Engine, certaines ressources sont plus coûteuses que d’autres. Le rapport optimal entre utilisation et coût dépend des caractéristiques de votre application. Combien de données stockez-vous ? Combien de trafic est généré par vos visiteurs ? Comment le trafic est-il réparti sur l’ensemble des données ? Combien de traitement de données est impliqué ? Comment le nombre de visiteurs est-il réparti au fil du temps ?

Lorsque vous examinez ces questions et que vous observez les tableaux de prix actuels sur le site de Google, vous constatez rapidement que vous pouvez avoir un problème d’optimisation. Jetez un coup d’œil sur http://code.google.com/appengine/docs/billing.html pour plus d’informations.

Bien qu’il n’y ait pas de solution miracle pour une réduction optimale des coûts, cet article vise à vous donner le plus de contrôle sur les performances et les coûts de votre application web.

Mesurer le coût du chargement de la classe

Chaque bibliothèque ou framework que vous introduisez apporte beaucoup de classes supplémentaires à charger au démarrage. Pour cette raison, cet article présente seulement trois JAR tiers pour aider avec les exemples de code : Commons FileUpload, StringTemplate et ANTLR. Commons FileUpload est utilisé pour traiter les soumissions de formulaires avec des fichiers en tant que contenu. StringTemplate est utilisé comme un langage de template pour générer la sortie pour les visiteurs, et il peut également être utilisé pour générer du texte pour un e-mail. ANTLR représente un autre outil pour la reconnaissance de langage et est une dépendance de StringTemplate.

Pour vous montrer le coût du chargement des classes, on va étudier l’heure du démarrage de l’instance App Engine avec StringTemplate et sans StringTemplate. En outre, il existe une comparaison du temps de démarrage entre un fichier web.xml d’environ 400 lignes et un fichier web.xml de 21 lignes.

Temporisation d’une servlet contenant une bibliothèque

La liste 2.1 montre une servlet très simple qui traite un modèle en utilisant le framework StringTemplate et affiche « Hello, World » dans la fenêtre du navigateur.

Extrait 2.1

Écrire Hello World avec StringTemplate

01 package com.appspot.template;
 02
 03 import java.io.IOException;
 04
 05 import javax.servlet.ServletException;
 06 import javax.servlet.http.HttpServlet;
 07 import javax.servlet.http.HttpServletRequest;
 08 import javax.servlet.http.HttpServletResponse;
 09
 10 import org.antlr.stringtemplate.StringTemplate;
 11 import org.antlr.stringtemplate.StringTemplateGroup;
 12
 13 public class StringTemplateServlet extends HttpServlet {
 14
 15 protected void doGet(HttpServletRequest request,
 16 HttpServletResponse response)
 17 throws ServletException, IOException {
 18 long startTime = System.currentTimeMillis();
 19
 20 StringTemplateGroup group = new StringTemplateGroup("xhtml",
 21 "WEB-INF / templates / xhtml");
 22 StringTemplate hello = group.getInstanceOf("hello-world");
 23 hello.setAttribute("nom", "Monde");
 24 response.getWriter().write(hello.toString());
 25
 26 long diff = System.currentTimeMillis() - startTime;
 27 response.getWriter ().write("time: " + diff);
 28
 29}
 30}

Les lignes 18 et 26 traitent le minuteur, tandis que le code qui charge les fichiers JAR StringTemplate et ANTLR se trouve sur les lignes 20 à 24.

Écrire l’heure résultante en bas du HTML (ligne 27) n’est pas vraiment élégant, mais cela fonctionne suffisamment pour la minuterie simple requise dans cet exemple.

La ligne 22 fait référence à un fichier externe avec un modèle HTML. Ce modèle est présenté dans l’Extrait 2.2.

Extrait 2.2

Configuration du modèle HTML pour StringTemplate

01 <html>
 02 <head>
 03 <title>Test</ title>
 04 </ head>
 05 <body>
 06 Bonjour, $nom$ d'un fichier!
 07 </ body>
 08 </ html>

La ligne 6 traite l’attribut fourni à la ligne 23 de l’Extrait 2.1. Le reste du modèle HTML ne devrait nécessiter aucune explication. L’écran résultant juste après le lancement d’une nouvelle instance est présenté à la figure 2.1.

Recharger la même servlet lorsque l’instance est déjà démarrée est beaucoup plus rapide. Le traitement de StringTemplate prend 10 à 15 millisecondes sur les demandes suivantes.

Figure 2.1 Affichage de l’heure résultante dans l’écran du navigateur avec StringTemplate.

Temporisation d’une servlet ne contenant pas de bibliothèque

Écrire Hello World sur un écran de navigateur est assez simple sans utiliser une bibliothèque comme StringTemplate. Si vous modifiez le code pour écrire Hello World directement dans le navigateur, vous obtenez une servlet comme indiqué dans l’Extrait 2.3.

Extrait 2.3 Écrire Hello World sans StringTemplate

01 package com.appspot.template;
 02
 03 import java.io.IOException;
 04
 05 import javax.servlet.ServletException;
 06 import javax.servlet.http.HttpServlet;
 07 import javax.servlet.http.HttpServletRequest;
 08 import javax.servlet.http.HttpServletResponse;
 09
 10 public class StringTemplateServlet extends HttpServlet {
 11
 12 protected void doGet(HttpServletRequest request,
 13 HttpServletResponse response)
 14 throws ServletException, IOException {
 15
 16 long startTime = System.currentTimeMillis();
 17
 18 response.getWriter().write("Bonjour le monde sans ST! ");
 19
 20 long diff = System.currentTimeMillis() - startTime;
 21 response.getWriter().write ("time: " + diff);
 22
 23}
 24}

La seule différence est dans la ligne 18. Pour éviter de gaspiller trop de code, le HTML est omis. Sept courtes lignes de HTML n’ont pas d’influence significative sur le temps de chargement : elles représentent moins d’une milliseconde.

La figure 2.2 montre la fenêtre du navigateur chargeant la servlet à partir de l’Extrait 2.3 lors du démarrage d’une nouvelle instance. La diminution du temps de chargement est importante !

Si le chargement de la bibliothèque StringTemplate augmente le temps de chargement d’une nouvelle instance App Engine de 300 millisecondes, alors pourquoi ne pas basculer vers FreeMarker, Velocity ou les Java Server Pages (JSP) ? Vous pouvez le demander. Ou peut-être connaissez-vous un autre moteur de gabarit non mentionné ici ? Vous êtes encouragé à étudier et à découvrir par vous-même quelle bibliothèque a le temps de chargement le plus efficace lors du démarrage à froid.

Pour toute autre bibliothèque ou structure que vous aimeriez introduire, vous devriez d’abord étudier l’effet sur le temps de chargement total. L’ajout d’un JAR supplémentaire est toujours un grand pas en avant.

Figure 2.2 Affichage de l’heure résultante dans l’écran du navigateur sans StringTemplate.

Réduire la taille de web.xml

Les modifications explicites telles que l’ajout des fichiers JAR sont relativement simples à gérer. Le plus difficile est de faire des changements plus progressivement au fil du temps. Par exemple, ce livre est plein de servlets. Comme les servlets ont été ajoutées, le fichier web.xml a augmenté. À la fin de l’écriture, le fichier web.xml contenait plus de 400 lignes de configuration définissant tous les exemples présentés dans le livre.

Le nombre de servlets déclarés dans le fichier web.xml a une influence significative sur le temps de chargement des classes. Pour tester la différence, le fichier web.xml a été réduit à la taille minimale, comme indiqué dans l’Extrait 2.4. Une seule servlet est incluse dans la servlet des Extraits 2.1 et 2.3.

Extrait 2.4 Réduire web.xml à un minimum absolu

01 <?x ml version="1.0" encoding="utf-8"?>
 02 <web-app xmlns: xsi="http://www.w3.org/2001/XMLSchema-instance"
 03 xmlns="http://java.sun.com/xml/ns/javaee"
 04 xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 05 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
 06 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 07 version="2.5">
 08 Mesurer le coût du chargement de la classe
 09 <!-- Template -->
 10 <servlet>
 11 <servlet-name>StringTemplateServlet</servlet-name>
 12 <servlet-class>
 13 com.appspot.template.StringTemplateServlet
 14 </servlet-class>
 15 </servlet>
 16 <servlet-mapping>
 17 <servlet-name>StringTemplateServlet</servlet-name>
 18 <url-pattern>/st</url-pattern>
 19 </servlet-mapping>
 20
 21 </web-app>

Jetez un œil sur les fichiers journaux avant et après la réduction de la taille de web.xml. La figure 2.3 montre la différence d’utilisation du processeur pour les deux scénarios.

Comme vous pouvez le constater, la différence de temps de chargement lors du démarrage à froid est importante. Cela indique que vous devez faire attention au nombre de servlets que vous déclarez dans une application Web.

Figure 2.3 Affichage des temps CPU enregistrés avant et après une réduction web.xml.

D’autre part, une très grande servlet a peu de chance de fonctionner beaucoup mieux que plusieurs servlets plus petites, donc vous devez considérer le compromis. Comment divisez-vous votre code sur un certain nombre de servlets avec la charge de chargement de la classe la moins élevée ? Encore une fois, il n’y a pas de solution miracle pour le faire. L’important est que vous pensiez à ce compromis dans votre situation spécifique.

Éviter les démarrages à froid

Au tout début de Google App Engine, toute requête pouvait entraîner le lancement d’une nouvelle instance. Pour les applications à faible trafic, il y avait un risque élevé de longs temps de réponse à la première requête d’un visiteur, en particulier si l’application n’était pas optimisée pour les démarrages à froid rapide.

Seules les applications à trafic élevé et à charge constante peuvent desservir un grand nombre d’utilisateurs sans les affronter avec des temps de réponse plus longs. Mais même celles-ci perdraient quelques visiteurs avec des départs et des arrêts par exemple.

Plus tard, Google a ajouté de nouvelles fonctionnalités pour les clients qui veulent payer afin d’éviter les temps de réponse plus longs. Il convient de noter que ces stratégies peuvent échouer lorsque l’application subit des pointes de trafic très soudaines.

Réservation des instances toujours Activées (Always On)

Les clients qui veulent payer peuvent engager des instances qui ne sont jamais désactivées. Cela résout le problème des applications à faible trafic, où presque chaque visite mène à une instance en cours de lancement.

Les instances Always On sont complétées par des instances dynamiques lorsque la requête dépasse les capacités des instances Always On disponibles. Cela signifie que le passage à Always On ne résout pas complètement le problème avec des réponses longues sur les démarrages à froid.

Always On peut être configuré dans la console d’administration, comme indiqué dans la documentation de Google à l’adresse http://code.google.com/appengine/docs/adminconsole/instances.html

Préchargement des classes à l’aide des demandes de préchauffage

Lorsqu’au moins une instance est en cours d’exécution, que ce soit Always On ou les instances dynamiques, App Engine peut parfois prévoir quand une nouvelle instance sera requise.

Tant que vous n’avez pas explicitement désactivé les demandes de préchauffage dans le fichier de configuration appengine-web.xml, App Engine peut envoyer une requête à /_ah/warmup parfois avant qu’une nouvelle instance ne soit requise. Vous pouvez configurer votre propre servlet pour écouter sur cette adresse et s’assurer que les classes et les autres données sont préchargées avant qu’un visiteur commence à accéder à cette instance.

Les demandes de préchauffage ne fonctionnent pas lorsqu’aucune instance n’est en cours d’exécution. Elles n’ajoutent pas beaucoup de valeur pour les applications à faible trafic, sauf si Always On est utilisé.

Même avec des instances en cours d’exécution, les demandes de préchauffage ne fonctionnent pas toujours. App Engine n’est pas toujours capable de prédire le trafic à l’avance.

Pour plus d’informations sur les demandes de préchauffage, consultez la page http://code.google.com/appengine/docs/adminconsole/instances.html

Gestion des demandes simultanées avec le mode Thread-Safe

Par défaut, une instance ne gère qu’une seule requête à la fois. Si une instance prend beaucoup du temps à répondre et qu’il y a d’autres demandes en même temps, App Engine lance des instances supplémentaires pour gérer le reste du trafic.

Dans certains cas, le chargement de nouvelles instances peut être évité en autorisant les demandes simultanées. Cela vous oblige à développer des servlets thread-safe. Vous trouverez plus d’informations sur le mode thread-safe sur http://code.google.com/appengine/docs/java/config/appconfig.html

Gestion des demandes intensives de mémoire avec Backends

En plus des instances Always On, vous pouvez acheter, pour un coût plus élevé, des instances spécialisées et optimisées pour le traitement des requêtes de type backend, c’est-à-dire des requêtes qui nécessitent plus de 30 secondes pour se terminer. Une autre caractéristique des applications backend est une consommation de mémoire plus élevée.

Vous trouverez plus d’informations sur les instances backend sur le site Web de Google à l’adresse http://code.google.com/appengine/docs/java/backends/

Améliorer la performance en général

Construire des applications Java hautes performances avec Google App Engine se concentre plus sur l’optimisation des performances. Voici un aperçu général des possibilités d’optimisation des performances.

Optimiser votre modèle de données pour plus de performance

Si vous modélisez vos données pour la banque de données App Engine de la même façon que vous modélisez vos données pour une base de données relationnelle, vous pouvez être certain que vous rencontrerez des problèmes de performance à un moment donné. Le cloud est fondamentalement différent de la façon dont une base de données relationnelle stocke des données sur le disque. Dans de nombreux cas, vous devez faire exactement le contraire de ce que vous avez l’habitude de faire. Par exemple, vous devez dénormaliser vos données au lieu de les normaliser.

Parce que vous pouvez stocker des tableaux de données, il y a moins besoin de relations entre les tables, bien que vous deviez être prudent si vous ressentez le besoin d’indexer le tableau, car la taille de votre index total peut exploser.

Vous devez prendre en compte le besoin de transactions avant de configurer votre modèle de données. Les transactions nécessitent des groupes d’entités, et des groupes d’entités plus importants peuvent nuire à l’évolutivité.

Éviter le traitement redondant à l’aide du cache

De nombreuses tâches fastidieuses sont effectuées à plusieurs reprises pour les demandes ultérieures, pensez aux tâches qui requièrent la collecte de données ou le traitement de calculs intensifs. Le même traitement peut être répété pour un seul visiteur ou pour plusieurs visiteurs.

La mise en cache correcte peut aider à éviter les processus répétitifs. Cet article explique à la fois la mise en cache fine à l’aide de memcache et la mise en cache au niveau de la page sur Internet.

Report de tâches à exécution longue à l’aide de la file d’attente de tâches (Task Queue)

Dans de nombreux cas, une réactivité élevée est plus importante qu’une haute performance. Parfois, répondre rapidement à une requête d’un visiteur peut être effectué en reportant le travail réel. Aussi longtemps que le visiteur peut avoir confiance que le travail sera effectué à terme, il ou elle sera satisfait(e) de la réponse rapide.

L’API Task Queue peut être utilisée de plusieurs façons. Vous pouvez planifier des tâches à intervalles réguliers ou publier des tâches dans la file d’attente à la demande. Ces deux méthodes peuvent aider à améliorer les performances et la réactivité.

Amélioration des performances de chargement de page dans le navigateur

Un serveur à haute performance est pratiquement inutile si le chargement de la page dans le navigateur ruine le temps de réponse total. Par exemple, si votre HTML est rempli d’éléments, de classes et d’identifiants inutiles, votre fichier CSS (Cascading Style Sheet) dépasse la taille d’un annuaire téléphonique moyen et vous atteignez un mégaoctet de fichiers JavaScript, tous les efforts apportés au serveur sont perdus .Vous pourriez le rendre encore pire en ajoutant un ou plusieurs fichiers Flash dans votre page. Alors dans ce cas, vous travaillez clairement dans la mauvaise direction.

Avec HTML5 et CSS3, vous n’avez plus besoin de Flash, sauf peut-être pour un lecteur vidéo occasionnel utilisé jusqu’à ce que les vidéos HTML5 soient suffisamment matures. Les nouveaux éléments ajoutés en HTML5 peuvent vous aider à réduire vos fichiers CSS. Plus votre fichier CSS est spécifique, plus il est facile de le maintenir.

La façon dont vous chargez votre JavaScript a un impact important sur le temps de chargement de la page. Le chargement discret de JavaScript au bas de la page permet de restituer le reste de la page avant l’interprétation du JavaScript. Cela améliore la réactivité du visiteur.

Travailler avec des API asynchrones

Le chargement de la page n’entraîne généralement pas de traitement de données important. Dans la plupart de cas, cela consiste à attendre que des services tels que le magasin de données répondent. Si vous savez à l’avance que vous devez effectuer plusieurs requêtes backend et que les requêtes backend sont indépendantes les unes des autres, vous pouvez utiliser des API asynchrones.

Optimisation de votre application avant le déploiement

Certaines optimisations de performances sont le résultat de la planification et de la conception. Les améliorations de performance les plus efficaces résultent généralement d’une expérimentation et de mesures précises.

Vous pouvez profiler les appels aux services backend de Google en utilisant AppStats. La majeure partie de la surcharge dans une application App Engine moyenne est dans les appels backend. Si vous faites beaucoup d’efforts dans votre propre code, vous êtes encouragé à créer ce code et à l’optimiser dans la mesure du possible.

Résumé

Les solutions cloud, et plus particulièrement Google App Engine, sont conçues pour une évolutivité et une utilisation flexible en partant de rien. Cependant, dans le cas de Google App Engine, cette conception peut signifier que certaines stratégies classiques d’optimisation de performances sont contre-productives. Cet article se concentre sur le temps de démarrage à froid et sur la raison pour laquelle vous devriez éviter les démarrages à froid lorsque cela est possible. On a vu également des surcoûts de frameworks et de bibliothèques à éviter si possible.

 

 

À lire aussi

Qu’est-ce qu’une API ? Comprendre les interfaces de programmation d’application

Les interfaces de programmation d’application cachent la complexité aux développeurs. Elles étendent les systèmes aux …