BLOG | NGINX

Présentation du serveur HTTP/2 Push avec NGINX 1.13.9

NGINX-Partie-de-F5-horiz-black-type-RGB
Vignette d'Owen Garrett
Owen Garrett
Publié le 20 février 2018

La prise en charge du push serveur HTTP/2 est également incluse dans NGINX Plus R15 .

Nous sommes ravis d'annoncer que NGINX 1.13.9 , publié le 20 février 2018 , inclut la prise en charge du push serveur HTTP/2. Pour les utilisateurs de NGINX Plus, la prise en charge du push serveur HTTP/2 sera incluse dans la prochaine version NGINX Plus R15 , prévue pour avril 2018.

Le push serveur, tel que défini dans la spécification HTTP/2, permet à un serveur d’envoyer de façon anticipée des ressources à un client distant, en prévoyant que vous pourriez bientôt en faire la demande. Ainsi, vous pouvez réduire le nombre d’aller-retours réseau (RTT – le temps nécessaire pour une requête et sa réponse) lors du chargement d’une page d’un RTT ou plus, et bénéficier d’une réponse plus rapide.

Le push serveur sert à préparer un client avec les feuilles de style, images et autres ressources dont il aura besoin pour afficher une page web. Veillez à ne pousser que les ressources indispensables ; évitez d’envoyer celles que le client a probablement déjà en cache.

Dans cet article de blog, je décris :

Configuration du push du serveur HTTP/2

Pour envoyer des ressources avec le chargement d'une page, utilisez la directive http2_push de la manière suivante :

server { # Assurez-vous que HTTP/2 est activé pour le serveur listen 443 ssl http2 ; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; # chaque fois qu'un client demande demo.html, envoyez également # /style.css, /image1.jpg et /image2.jpg location = /demo.html { http2_push /style.css; http2_push /image1.jpg; http2_push /image2.jpg; } }

Vérification du push du serveur HTTP/2

Vous pouvez facilement vérifier que le push du serveur est en vigueur en utilisant l'une des deux méthodes suivantes :

  • Les outils de développement dans votre navigateur Web
  • Un client HTTP/2 en ligne de commande tel que nghttp

Vérification avec les outils de développement (Google Chrome)

Voici comment utiliser les outils de développement de votre navigateur pour confirmer que le serveur push est activé, en prenant Google Chrome comme exemple. Sur la figure, la colonne Initiateur de l’onglet Réseau des outils de développement de Chrome montre que plusieurs ressources ont été envoyées au client lors d’une requête sur /demo.html.

La colonne Initiateur indique que le push serveur a servi à envoyer des ressources

Vérification avec un client de ligne de commande ( nghttp )

En plus des outils de navigateur Web, vous pouvez utiliser le client de ligne de commande nghttp du projet nghttp2.org pour vérifier que la transmission du serveur est effective. Vous pouvez télécharger le client de ligne de commande nghttp depuis GitHub ou installer le package de système d’exploitation approprié lorsqu’il est disponible. Pour Ubuntu, utilisez le package nghttp2-client .

Dans le résultat, l’astérisque (*) indique les ressources envoyées par le serveur.

$ nghttp -ans https://example.com/demo.html id responseEnd requestStart processus code taille requête chemin 13 +84.25ms +136us 84.11ms 200 492 /demo.html 2 +84.33ms * +84.09ms 246us 200 266 /style.css 4 +261.94ms * +84.12ms 177.83ms 200 40K /image2.jpg 6 +685.95ms * +84.12ms 601.82ms 200 173K /image1.jpg

Distribution automatique des ressources aux clients

Dans bien des cas, il est peu pratique, voire impossible, d’énumérer les ressources à pousser dans le fichier de configuration NGINX. C’est pourquoi NGINX prend aussi en charge la convention d’interception des en-têtes Link preload, puis pousse les ressources indiquées dans ces en-têtes. Pour activer le préchargement, ajoutez la directive http2_push_preload à la configuration :

server { # Assurez-vous que HTTP/2 est activé pour le serveur listen 443 ssl http2 ; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; # Intercepter l'en-tête du lien et lancer les push demandés location = /myapp { proxy_pass http://upstream; http2_push_preload on; } }

Par exemple, lorsque NGINX fonctionne comme un proxy (pour HTTP, FastCGI ou d’autres types de trafic), le serveur en amont peut ajouter un en-tête de lien comme celui-ci à sa réponse :

Lien : </style.css> ; as=style ; rel=preload

NGINX intercepte cet en-tête et lance un push serveur de /style.css . Le chemin dans l'en-tête du lien doit être absolu – les chemins relatifs comme ./style.css ne sont pas pris en charge. Le chemin peut éventuellement inclure une chaîne de requête.

Pour envoyer plusieurs objets, vous pouvez fournir plusieurs en-têtes de lien ou, mieux encore, inclure tous les objets dans une liste séparée par des virgules :

Lien : </style.css>; as=style; rel=preload, </favicon.ico>; as=image; rel=preload

Si vous ne voulez pas que NGINX envoie automatiquement une ressource préchargée, ajoutez le paramètre nopush à l'en-tête :

# La ressource n'est pas poussée Lien : </nginx.png>; as=image; rel=preload; nopush

Lorsque http2_push_preload est activé, vous pouvez également lancer le push du serveur de préchargement en définissant l'en-tête de réponse dans votre configuration NGINX :

add_header Lien "</style.css>; as=style; rel=preload";

Diffuser sélectivement les ressources aux clients

La spécification HTTP/2 ne traite pas la question de savoir quand lancer la poussée de ressources. Vous devez pousser des ressources aux clients uniquement si vous savez qu’ils en auront probablement besoin et qu’ils ne les ont pas déjà en cache.

Vous pouvez choisir de n'envoyer des ressources aux clients que lors de leur première visite sur le site. Par exemple, testez la présence d'un cookie de session et configurez l’en-tête Link de façon conditionnelle pour ne précharger les ressources que si ce cookie est absent.

Si les clients se comportent correctement en ajoutant le cookie dans les requêtes suivantes, NGINX transmet les ressources aux clients une seule fois par session de navigation avec cette configuration :

server {
    listen 443 ssl http2 default_server;

    ssl_certificate ssl/certificate.pem;
    ssl_certificate_key ssl/key.pem;

    root /var/www/html;
    http2_push_preload on;

    location = /demo.html {
        add_header Set-Cookie "session=1";
        add_header Link $resources;
    }
}

map $http_cookie $resources {
    "~*session=1" "";
    default "</style.css>; as=style; rel=preload, </image1.jpg>; as=image; rel=preload, </image2.jpg>; as=image; rel=preload";
}

Mesurer l'effet du push du serveur HTTP/2

Pour mesurer l'effet du push serveur, nous avons créé une page de test simple, /demo.html , qui fait référence à une feuille de style distincte, /style.css . La feuille de style fait également référence à deux images. Nous avons testé les temps de chargement des pages en utilisant trois configurations différentes :

  • GET séquentiels (pas d'optimisation) – Le navigateur a chargé les ressources lorsqu'il a découvert qu'elles étaient nécessaires
  • Conseils de préchargement – Des conseils de préchargement (en-têtes de lien ) ont été inclus dans la première réponse pour indiquer au navigateur de charger les dépendances
  • Server Push (HTTP/2 uniquement) – Les dépendances ont été poussées de manière préventive vers le navigateur
Trois configurations ont été testées pour mesurer l'impact de HTTP/2 avec le push serveur

Nous avons effectué plusieurs tests de chaque configuration en utilisant HTTP, HTTPS ou HTTP/2. Les deux premières configurations s'appliquent aux trois protocoles et le serveur envoie uniquement des données vers HTTP/2.

Le comportement a été mesuré à l’aide des outils de développement Chrome. Le comportement le plus courant de chaque configuration a été évalué et moyenné, et les temps ont été corrélés avec le RTT du lien (mesuré à l'aide de ping ) pour illustrer l'effet mécanique de chaque méthode.

Les résultats des tests montrent que de nombreux allers-retours ont été effectués avec chaque configuration

Quelques observations de base

  • Le chargement du DOM marque le moment où vous pouvez établir une nouvelle connexion pour récupérer la page demo.html. La feuille de style correspond à la récupération de la ressource CSS.
  • L'établissement d'une connexion via HTTP nécessite 1 RTT ; pour HTTPS et HTTP/2, il faut 2 RTT.
  • La charge utile des ressources HTML et CSS est inférieure à la taille de l'unité de transmission maximale (MTU), donc une opération GET s'achève en environ 1 RTT.

Interprétation des résultats : Conseils de préchargement

  • Les indices de préchargement ont peu d’impact sur la ressource CSS, car elle est directement liée dans la ressource HTML, qui elle-même est rapidement fournie. Le navigateur démarre la requête CSS dès la livraison de la page HTML.
  • Les conseils de préchargement lancent rapidement le téléchargement des ressources déclarées dans la ressource CSS (ici, les deux images). Vous pouvez démarrer les téléchargements 1 RTT plus tôt grâce aux conseils de préchargement.
  • Les économies nettes obtenues grâce aux indications de préchargement peuvent atteindre 1 RTT ou plus. Pour télécharger des ressources en parallèle, le navigateur doit ouvrir une ou plusieurs connexions supplémentaires. Les performances dépendent de l'ordre d'exécution : si les requêtes lentes (avec réponses volumineuses) sont traitées en priorité ou si elles attendent l'ouverture des nouvelles connexions. Cette imprévisibilité dans l'ordre des requêtes explique l'accélération d'1 RTT pour HTTP et HTTP/2, et de 2 RTT pour HTTPS.

Interprétation des résultats : Poussée du serveur

  • Le serveur push a amélioré le temps de préchargement des indications d'un RTT supplémentaire. Les « réponses » push ont été initiées en même temps que la réponse à la première requête, tandis que les réponses preload-hints ont subi un délai RTT de 1 – 0,5 RTT pour la réponse à la première requête plus 0,5 RTT pour la requête GET de préchargement.

Notes de test

  • Plusieurs tests ont été effectués pour chaque configuration. Chaque exécution a commencé avec un cache de navigateur vide et sans connexions keepalive établies avec le serveur NGINX. Les directives NGINX keepalive_timeout et http2_idle_timeout ont été utilisées pour fermer rapidement les connexions keepalive.
  • Vous ne pouvez pas encore envoyer directement les ressources de polices à Chrome, sans doute à cause d’une complication connue. Chrome demande explicitement une ressource de police, même si vous l'avez déjà envoyée.
  • Des précautions ont été prises pour effacer explicitement les caches du navigateur avant chaque test, et tout le contenu a été diffusé avec des en-têtes de contrôle de cache expirés.
  • Chrome met en cache les ressources préchargées. Ces ressources mises en cache ne sont pas toujours ignorées quand vous désactivez la mise en cache, ni systématiquement supprimées lors d’une opération de « vidage du cache du navigateur ». (Dans Chrome, vous pouvez désactiver la mise en cache en cochant la case Désactiver le cache dans l’onglet Réseau des outils de développement. Pour vider le cache, faites un clic droit sur le bouton d’actualisation du navigateur lorsque les outils de développement sont ouverts, puis choisissez Vider le cache et recharger à froid).
  • Certaines tentatives de préchargement de contenu ont amené Chrome à réviser sans succès les copies en cache, puis à télécharger normalement la ressource. Nous n’avons pas inclus ces tentatives dans les mesures.
  • Chrome ajoute un délai 2-RTT inutile à toutes les nouvelles connexions SSL qui utilisent des certificats auto-signés précédemment acceptés. Le test a été réalisé à l’aide de certificats Let’s Encrypt signés par une autorité de certification pour éviter ce délai de 2 RTT.
  • Les retards DNS ont été résolus en modifiant le fichier local /etc/hosts .
  • Ces tests simples ne mesuraient pas l'impact de la poussée serveur lorsque le client possède déjà une copie en cache de la ressource. Pendant les tests, nous avons vidé tous les caches avant chaque essai, et la plupart des poussées étaient trop rapides pour être annulées.

Conclusion

Nous avons délibérément choisi un test simple pour illustrer le fonctionnement des indices de préchargement et du serveur push. Le serveur push améliore la latence d'une RTT par rapport aux indices de préchargement dans des cas simples, et offre un gain encore plus important face aux requêtes GET séquentielles non optimisées et à la découverte des ressources dépendantes.

Les cas d’usage plus réalistes comprennent de nombreuses variables supplémentaires : plusieurs ressources dépendantes, plusieurs sources, et même le risque de bandwith gaspillé en envoyant des ressources déjà en cache ou non immédiatement nécessaires. Les différences entre navigateurs impactent aussi les performances. Vos résultats différeront sûrement de ce test simple.

Par exemple, l'équipe Chrome a publié des recommandations détaillées sur le moment de déployer le push serveur et a effectué des mesures sur des sites plus complexes pour comparer les effets de l'absence d'optimisation, des conseils de préchargement et du push serveur sur HTTP/2. Leur rapport Rules of Thumb for HTTP/2 Push mérite d'être lu par quiconque envisage de déployer le serveur HTTP/2 Push en production.

Si vous pouvez déterminer à l'avance quelles ressources sont nécessaires, vous tirez un réel avantage à ce que les serveurs en amont envoient un indice de préchargement. L’envoi supplémentaire de ces ressources offre un bénéfice modeste mais mesurable, et peut parfois gaspiller de la bande passante ou retarder des ressources essentielles. Testez et surveillez avec soin toute configuration de poussée serveur.

Annexe : Comment fonctionne HTTP/2 Push ?

Les informations ci-dessous sont basées en partie sur les recherches effectuées dans l'article de blog très détaillé de Jake Archibald intitulé HTTP/2 push is tougher than I thought .

Le serveur HTTP/2 pousse généralement en avance les ressources dont dépend une requête lorsque vous demandez un contenu. Par exemple, quand vous demandez une page web, le serveur vous envoie aussi les feuilles de style, polices et images associées.

Lorsqu’un client établit une connexion HTTP/2, le serveur peut choisir d’envoyer une ou plusieurs réponses push serveur sur cette connexion. Ces push délivrent des ressources que vous n’avez pas demandées explicitement.

Le client peut soit rejeter un push (en envoyant une trame RST_STREAM ) soit l'accepter. Le client stocke le contenu poussé dans un « cache push » local associé à la connexion HTTP/2.

Plus tard, quand le client demande une ressource via une connexion HTTP/2 déjà établie, il consulte le cache push de la connexion pour trouver une réponse terminée ou en cours. Il privilégie la ressource en cache plutôt que d’envoyer une nouvelle requête HTTP/2.

Une ressource poussée reste dans le cache push par connexion jusqu’à ce que vous l’utilisiez ou que la connexion HTTP/2 se ferme :

  1. Lorsque vous utilisez la ressource, vous en copiez une version et nous supprimons l’entrée du cache push. Si la ressource est mise en cache, vous pouvez ensuite conserver votre copie dans le cache de page HTTP.
  2. Si la connexion HTTP/2 est fermée pour une raison quelconque, son cache push local est supprimé.

Cela a plusieurs implications :

  • Le contenu du cache de page HTTP dans le navigateur est utilisé de préférence au contenu du cache push, même si le contenu poussé est plus récent.
  • Vous pouvez partager les connexions HTTP/2 entre plusieurs chargements de page. Une ressource que nous poussons lors d’un chargement peut servir à une autre demande dans un chargement différent.
  • Les requêtes avec identifiants utilisent des connexions HTTP/2 distinctes de celles sans identifiants ; par exemple, une ressource poussée via une requête cross-origin avec identifiants peut ne pas être trouvée si le navigateur effectue une requête sans identifiants pour cette ressource.

Vous pouvez consulter une liste beaucoup plus détaillée des problèmes dans l'article de blog de Jake Archibald, HTTP/2 push is tougher than I thought .

Le push du serveur HTTP/2 est une fonctionnalité intéressante. Assurez-vous de tester minutieusement la configuration push de votre serveur HTTP/2 et soyez prêt à recourir aux indications de préchargement dans les cas où cela donne un comportement plus prévisible et plus sensible au cache.


« Cet article de blog peut faire référence à des produits qui ne sont plus disponibles et/ou qui ne sont plus pris en charge. Pour obtenir les informations les plus récentes sur les produits et solutions F5 NGINX disponibles, explorez notre famille de produits NGINX . NGINX fait désormais partie de F5. Tous les liens NGINX.com précédents redirigeront vers un contenu NGINX similaire sur F5.com."