Mesurer les performances avec la synchronisation du serveur

Publié: 2022-03-10
Résumé rapide ↬ L'en-tête Server Timing fournit un moyen discret et pratique de communiquer les délais de performance du serveur principal aux outils de développement dans le navigateur. L'ajout d'informations de synchronisation à votre application vous permet de surveiller les performances back-end et front-end en un seul endroit.

Lorsque nous entreprenons tout type de travail d'optimisation des performances, l'une des toutes premières choses que nous apprenons est qu'avant de pouvoir améliorer les performances, vous devez d'abord les mesurer. Sans pouvoir mesurer la vitesse à laquelle quelque chose fonctionne, nous ne pouvons pas dire si les modifications apportées améliorent les performances, n'ont aucun effet ou même aggravent les choses.

Beaucoup d'entre nous seront habitués à travailler sur un problème de performance à un certain niveau. Cela peut être quelque chose d'aussi simple que d'essayer de comprendre pourquoi JavaScript sur votre page ne démarre pas assez tôt, ou pourquoi les images mettent trop de temps à apparaître sur un mauvais wifi d'hôtel. La réponse à ce genre de questions se trouve souvent dans un endroit très familier : les outils de développement de votre navigateur.

Au fil des ans, les outils de développement ont été améliorés pour nous aider à résoudre ces types de problèmes de performances dans le front-end de nos applications. Les navigateurs intègrent désormais même des audits de performances. Cela peut aider à détecter les problèmes frontaux, mais ces audits peuvent révéler une autre source de lenteur que nous ne pouvons pas résoudre dans le navigateur. Ce problème est la lenteur des temps de réponse du serveur.

"Temps au premier octet"

Il y a très peu d'optimisations de navigateur à faire pour améliorer une page qui est tout simplement lente à se construire sur le serveur. Ce coût est encouru entre le navigateur qui fait la demande du fichier et la réception de la réponse. L'étude de votre graphique en cascade de réseau dans les outils de développement affichera ce retard dans la catégorie "Attente (TTFB)". C'est le temps d'attente du navigateur entre l'envoi de la requête et la réception de la réponse.

Plus après saut! Continuez à lire ci-dessous ↓

En termes de performances, cela s'appelle Time to First Byte - le temps qu'il faut avant que le serveur commence à envoyer quelque chose avec lequel le navigateur peut commencer à travailler. Ce temps d'attente comprend tout ce que le serveur doit faire pour créer la page. Pour un site typique, cela peut impliquer d'acheminer la demande vers la bonne partie de l'application, d'authentifier la demande, d'effectuer plusieurs appels vers des systèmes principaux tels que des bases de données, etc. Cela peut impliquer d'exécuter du contenu via des systèmes de modèles, d'appeler des API à des services tiers, et peut-être même d'envoyer des e-mails ou de redimensionner des images. Tout travail effectué par le serveur pour répondre à une demande est écrasé dans cette attente TTFB que l'utilisateur expérimente dans son navigateur.

Le panneau Réseau dans Chrome DevTools montrant l'inspection d'une demande de page unique
L'inspection d'une demande de document montre le temps que le navigateur passe à attendre la réponse du serveur.

Alors, comment pouvons-nous réduire ce temps et commencer à livrer la page plus rapidement à l'utilisateur ? Eh bien, c'est une grande question, et la réponse dépend de votre application. C'est le travail de l'optimisation des performances elle-même. Ce que nous devons faire d'abord, c'est mesurer la performance afin que les avantages de tout changement puissent être jugés.

L'en-tête de synchronisation du serveur

Le travail de Server Timing n'est pas de vous aider à chronométrer l'activité sur votre serveur. Vous devrez faire le chronométrage vous-même en utilisant l'ensemble d'outils que votre plate-forme backend met à votre disposition. Au contraire, le but de Server Timing est de spécifier comment ces mesures peuvent être communiquées au navigateur.

La façon dont cela est fait est très simple, transparente pour l'utilisateur et a un impact minimal sur le poids de votre page. Les informations sont envoyées sous la forme d'un simple ensemble d'en-têtes de réponse HTTP.

 Server-Timing: db;dur=123, tmpl;dur=56

Cet exemple communique deux points de synchronisation différents nommés db et tmpl . Ceux-ci ne font pas partie de la spécification - ce sont des noms que nous avons choisis, dans ce cas pour représenter respectivement certains timings de base de données et de modèle.

La propriété dur indique le nombre de millisecondes que l'opération a pris pour se terminer. Si nous regardons la demande dans la section Réseau des outils de développement, nous pouvons voir que les horaires ont été ajoutés au tableau.

Le panneau Timings d'une demande de page dans Chrome DevTools affichant une nouvelle section Server Timing.
Une nouvelle section Server Timing apparaît, indiquant les délais définis avec l'en-tête HTTP Server-Timing.

L'en-tête Server-Timing peut prendre plusieurs métriques séparées par des virgules :

 Server-Timing: metric, metric, metric

Chaque métrique peut spécifier trois propriétés possibles

  1. Un nom court pour la métrique (comme db dans notre exemple)
  2. Une durée en millisecondes (exprimée comme dur=123 )
  3. Une description (exprimée sous la forme desc="My Description" )

Chaque propriété est séparée par un point-virgule comme délimiteur. Nous pourrions ajouter des descriptions à notre exemple comme ceci :

 Server-Timing: db;dur=123;desc="Database", tmpl;dur=56;desc="Template processing"
Le panneau Timings d'une demande de page dans Chrome DevTools affichant les descriptions utilisées pour les métriques Server Timing.
Les noms sont remplacés par des descriptions lorsqu'elles sont fournies.

La seule propriété requise est name . dur et desc sont facultatifs et peuvent être utilisés éventuellement si nécessaire. Par exemple, si vous deviez déboguer un problème de synchronisation qui se produisait sur un serveur ou un centre de données et pas sur un autre, il peut être utile d'ajouter ces informations dans la réponse sans synchronisation associée.

 Server-Timing: datacenter;desc="East coast data center", db;dur=123;desc="Database", tmpl;dur=56;desc="Template processing”

Cela apparaîtrait alors avec les horaires.

Le panneau Timings d'une demande de page dans Chrome DevTools affichant un Server Timing sans heure définie.
La valeur "Centre de données de la côte est" est affichée, même si elle n'a pas de minutage.

Une chose que vous remarquerez peut-être est que les barres de synchronisation ne s'affichent pas en cascade. C'est simplement parce que Server Timing n'essaie pas de communiquer la séquence de minutages, mais uniquement les métriques brutes elles-mêmes.

Mise en œuvre de la synchronisation du serveur

La mise en œuvre exacte dans votre propre application dépendra de votre situation spécifique, mais les principes sont les mêmes. Les étapes seront toujours :

  1. Chronométrer certaines opérations
  2. Rassembler les résultats de chronométrage
  3. Sortir l'en-tête HTTP

En pseudocode, la génération de réponse pourrait ressembler à ceci :

 startTimer('db') getInfoFromDatabase() stopTimer('db') startTimer('geo') geolocatePostalAddressWithAPI('10 Downing Street, London, UK') endTimer('geo') outputHeader('Server-Timing', getTimerOutput())

Les bases de la mise en œuvre de quelque chose dans ce sens devraient être simples dans n'importe quelle langue. Une implémentation PHP très simple pourrait utiliser la fonction microtime() pour les opérations de synchronisation et pourrait ressembler à ce qui suit.

 class Timers { private $timers = []; public function startTimer($name, $description = null) { $this->timers[$name] = [ 'start' => microtime(true), 'desc' => $description, ]; } public function endTimer($name) { $this->timers[$name]['end'] = microtime(true); } public function getTimers() { $metrics = []; if (count($this->timers)) { foreach($this->timers as $name => $timer) { $timeTaken = ($timer['end'] - $timer['start']) * 1000; $output = sprintf('%s;dur=%f', $name, $timeTaken); if ($timer['desc'] != null) { $output .= sprintf(';desc="%s"', addslashes($timer['desc'])); } $metrics[] = $output; } } return implode($metrics, ', '); } }

Un script de test l'utiliserait comme ci-dessous, ici en utilisant la fonction usleep() pour créer artificiellement un retard dans l'exécution du script afin de simuler un processus qui prend du temps à se terminer.

 $Timers = new Timers(); $Timers->startTimer('db'); usleep('200000'); $Timers->endTimer('db'); $Timers->startTimer('tpl', 'Templating'); usleep('300000'); $Timers->endTimer('tpl'); $Timers->startTimer('geo', 'Geocoding'); usleep('400000'); $Timers->endTimer('geo'); header('Server-Timing: '.$Timers->getTimers());

L'exécution de ce code a généré un en-tête qui ressemblait à ceci :

 Server-Timing: db;dur=201.098919, tpl;dur=301.271915;desc="Templating", geo;dur=404.520988;desc="Geocoding"
Le panneau Timings d'une demande de page dans Chrome DevTools montrant les valeurs de test correctement affichées.
Les minutages du serveur définis dans l'exemple s'affichent dans le panneau Timings avec les délais configurés dans notre script de test.

Implémentations existantes

Compte tenu de la maniabilité de Server Timing, il y a relativement peu d'implémentations que j'ai pu trouver. Le package NPM de synchronisation du serveur offre un moyen pratique d'utiliser la synchronisation du serveur à partir des projets Node.

Si vous utilisez un framework PHP basé sur un middleware, tuupola/server-timing-middleware fournit également une option pratique. J'utilise cela en production sur Notist depuis quelques mois, et je laisse toujours quelques timings de base activés si vous souhaitez voir un exemple dans la nature.

Pour la prise en charge du navigateur, le meilleur que j'ai vu se trouve dans Chrome DevTools, et c'est ce que j'ai utilisé pour les captures d'écran de cet article.

Considérations

Server Timing lui-même ajoute une surcharge très minime à la réponse HTTP renvoyée sur le réseau. L'en-tête est très minimal et peut généralement être envoyé en toute sécurité sans se soucier de cibler uniquement les utilisateurs internes. Même ainsi, il vaut la peine de garder les noms et les descriptions courts afin de ne pas ajouter de surcharge inutile.

Le travail supplémentaire que vous pourriez faire sur le serveur pour chronométrer votre page ou votre application est plus préoccupant. L'ajout de temps et de journalisation supplémentaires peut lui-même avoir un impact sur les performances, il vaut donc la peine de mettre en œuvre un moyen d'activer et de désactiver cela si nécessaire.

L'utilisation d'un en-tête de synchronisation du serveur est un excellent moyen de s'assurer que toutes les informations de synchronisation du front-end et du back-end de votre application sont accessibles au même endroit. À condition que votre application ne soit pas trop complexe, elle peut être facile à mettre en œuvre et vous pouvez être opérationnelle en très peu de temps.

Si vous souhaitez en savoir plus sur la synchronisation du serveur, vous pouvez essayer ce qui suit :

  • La spécification de synchronisation du serveur W3C
  • La page MDN sur Server Timing contient des exemples et des détails à jour sur la prise en charge du navigateur
  • Un article intéressant de l'équipe BBC iPlayer sur leur utilisation de Server Timing.