"Jackal": serrez le frontend

Salut Je suis Vanya, le chef de l'équipe de la plateforme chez Tinkoff Business.

Mon passe-temps préféré est d'ouvrir l'onglet DevTools et de vérifier le poids des artefacts de site Web. Dans cet article, je vais vous expliquer comment nous avons réduit le poids de l'application de 30% avec l'aide de l'équipe front-end de la plateforme en une journée sans changer le code du site. Aucune astuce et inscription - uniquement nginx, docker et node.js (facultatif).




Pourquoi


Les applications frontales pèsent désormais beaucoup. Les artefacts collectés peuvent peser 2 à 3 Mo, voire plus. Cependant, les algorithmes de compression viennent en aide aux utilisateurs.

Jusqu'à récemment, nous utilisions uniquement Gzip, qui a été introduit dans le monde en 1992. Il s'agit probablement de l'algorithme de compression le plus populaire sur le Web, il est pris en charge par tous les navigateurs au-dessus d'IE 6.

Permettez-moi de vous rappeler que le niveau de compression de Gzip varie de 1 à 9 (plus est plus efficace), et vous pouvez compresser à la volée ou de manière statique.

  • «À la volée» (dynamiquement) - les artefacts sont stockés sous la forme reçue après l'assemblage, leur compression se produit lors de la livraison au client. Dans notre cas, au niveau nginx.
  • Statique - les artefacts sont compressés après l'assemblage et le serveur HTTP les envoie au client «tels quels».

De toute évidence, la première option nécessite plus de ressources de serveur pour chaque demande. La seconde est au stade de l'assemblage et de la préparation de la demande.

Notre frontend a été compressé dynamiquement par le quatrième niveau. Je vais démontrer la différence entre un artefact compressé et l'original:
Niveau de compression
Poids de l'artefact, Kb
Temps de compression, ms
0
2522
-
1
732
42
2
702
44
3
683
48
4
636
55
5
612
65
6
604
77
7
604
80
8
603
104
9
601
102

Vous remarquerez peut-être que même le quatrième niveau réduit la taille de l'artefact de 4 fois! Et la différence entre le quatrième niveau et le neuvième est de 35 Ko, soit 1,3% de l'original, mais le temps de compression est 2 fois plus long.

Et récemment, nous avons pensé: pourquoi ne pas passer à Brotli? Oui, et au niveau de compression le plus puissant!

Soit dit en passant, cet algorithme a été introduit par Google en 2015 et possède 11 niveaux de compression. Dans le même temps, le quatrième niveau de Brotli est plus efficace que le neuvième à Gzip. J'ai été motivé et j'ai rapidement lancé du code pour compresser les artefacts avec l'algorithme Brotli. Les résultats sont présentés ci-dessous:
Niveau de compression
Poids de l'artefact, Kb
Temps de compression, ms
0
2522
-
1
662
128
2
612
155
3
601
156
4
574
202
5
526
227
6
512
249
7
501
303
8
496
359
9
492
420
10
452
3708
11
446
8257

Cependant, le tableau montre que même le premier niveau de compression Brotli prend plus de temps que le neuvième niveau dans Gzip. Et le dernier niveau - autant que 8,3 secondes! Cela m'a alerté.

En revanche, le résultat est clairement impressionnant. Ensuite, j'ai essayé de transférer la compression vers nginx - google la documentation . Tout s'est avéré extrêmement simple:

brotli on; brotli_comp_level 11; brotli_types text/plain text/css application/javascript; 

Il a assemblé l'image du docker, a lancé le conteneur et a été terriblement surpris:



Le temps de téléchargement de mon fichier a décuplé - de 100 ms à 5 secondes! L'application est devenue impossible à utiliser.

Après avoir étudié la documentation plus en profondeur, j'ai réalisé que vous pouvez distribuer de façon statique. J'ai utilisé un script précédemment écrit, compressé les mêmes artefacts, l'ai mis dans un conteneur et l'ai lancé. Les temps de téléchargement sont revenus à la normale - victoire! Cependant, il est trop tôt pour se réjouir, car la proportion de navigateurs prenant en charge ce type de compression est d' environ 80% .

Cela signifie que vous devez conserver la compatibilité descendante, tout en souhaitant utiliser le niveau de Gzip le plus efficace. L'idée est alors venue de créer un utilitaire de compression de fichiers, qui a plus tard reçu le nom de «Jackal».



De quoi avons-nous besoin?


Nginx, Docker et Node.js, bien que vous puissiez également utiliser bash si vous le souhaitez.
Avec Nginx, presque tout est clair:

 brotli off; brotli_static on; gzip_static on; 

Mais que faire des applications qui n'ont pas encore réussi à mettre à jour l'image docker? À droite, ajoutez la compatibilité descendante:

 gzip on; gzip_level 4; gzip_types text/plain text/css application/javascript; 

Je vais vous expliquer le principe de fonctionnement:


À chaque demande, le client envoie un en-tête Accept-Encoding, qui répertorie les algorithmes de compression pris en charge, séparés par des virgules. Habituellement, c'est dégonfler, gzip, br.

Si le client a br dans la ligne, alors nginx recherche les fichiers avec l'extension .br, s'il n'y a pas de tels fichiers et que le client prend en charge Gzip, alors il recherche .gz. S'il n'y a pas de tels fichiers, il tremblera "à la volée" et le rendra avec le quatrième niveau de compression.

Si le client ne prend en charge aucun type de compression, le serveur émettra des artefacts dans leur forme d'origine.

Cependant, un problème est survenu: notre image docker nginx ne prend pas en charge le module Brotli. Comme base, j'ai pris l' image docker terminée .

Dockerfile pour "empaqueter" nginx dans un projet
 FROM fholzer/nginx-brotli #      RUN rm -rf /usr/share/nginx/html/ #      COPY app/nginx /etc/nginx/conf.d/ #      COPY dist/ /usr/share/nginx/html/ #  CMD nginx -c /etc/nginx/conf.d/nginx.conf 


Nous avons compris l'équilibrage du trafic, mais d'où obtenir les artefacts? C'est là que le chacal vient à la rescousse.

Le chacal


Il s'agit d'un utilitaire pour compresser la statique de votre application.

Maintenant, ce sont trois scripts node.js enveloppés dans une image docker avec node: alpine. Passons en revue les scripts.

base-compresseur - un script qui implémente la logique de compression de base.

Arguments d'entrée:

  1. Fonction de compression - n'importe quelle fonction javascript, vous pouvez implémenter votre propre algorithme de compression.
  2. Paramètres de compression - un objet avec les paramètres nécessaires à la fonction transférée.
  3. Extension - expansion des artefacts de compression. Doit être spécifié en commençant par un caractère point.

gzip.js - un fichier avec un appel de compresseur de base avec la fonction Gzip transmis depuis le package zlib et indiquant le neuvième niveau de compression.

brotli.js - fichier avec un appel de compresseur de base avec la fonction Brotli transmis à partir du même package npm et indiquant le 11e niveau de compression.

Dockerfile créant l'image de chacal
 FROM node:8.12.0-alpine #     COPY scripts scripts #  package.json  package-lock.json   COPY package*.json scripts/ #      WORKDIR scripts #    #    node_modules/   #  ,     RUN npm ci #     CMD node gzip.js | node brotli.js 


Nous avons compris comment cela fonctionne, vous pouvez maintenant exécuter en toute sécurité:

 docker run \ -v $(pwd)/dist:/scripts/dist \ -e 'dirs=["dist/"]' \ -i mngame/shakal 

  • -v $ (pwd) / dist: / scripts / dist - spécifie le répertoire local à considérer dans le conteneur (lien vers le montage). La spécification du répertoire des scripts est requise, car il fonctionne à l'intérieur du conteneur.
  • -e 'dirs = ["dist /"]' - spécifiez le paramètre d'environnement dirs - un tableau de lignes qui décrivent les répertoires à l'intérieur des scripts / qui seront compressés.
  • -i mngame / shakal - spécifiant une image avec docker.io.

Dans les répertoires spécifiés, le script compresse récursivement tous les fichiers avec les extensions spécifiées .js, .json, .html, .css et enregistre les fichiers avec les extensions .br et .gz à côté. Sur notre projet, ce processus prend environ deux minutes avec le poids de tous les artefacts environ 6 Mo.

À ce stade, et peut-être même plus tôt, vous auriez pu penser: «Quel docker? Quel nœud? Pourquoi ne pas simplement vous ajouter deux packages dans le package.json du projet et appeler directement la post-construction? »

C'est très douloureux pour moi de voir quand, pour exécuter des linters dans CI, le projet installe plus de 100 packages pour lui-même, dont il a besoin d'un maximum de 10 au stade du linting. C'est le temps de l'agent, votre temps, après tout, le temps de mise sur le marché.

Dans le cas du docker, nous obtenons une image pré-assemblée dans laquelle tout le nécessaire pour la compression est installé. Si vous n'avez pas besoin de compresser quoi que ce soit maintenant - ne compressez pas. Besoin d'une peluche - exécutez-le uniquement, besoin de tests - exécutez uniquement eux. De plus, nous obtenons une bonne version de Jackal: nous n'avons pas besoin de mettre à jour ses dépendances dans chaque projet - il suffit de publier une nouvelle version et d'utiliser la dernière balise du projet.

Résultat:


  • La taille des artefacts est passée de 636 Ko à 446 Ko.
  • La taille en pourcentage a diminué de 30%.
  • Le temps de téléchargement a diminué de 10 à 12%.
  • Le temps de décompression, basé sur l'article , reste le même.

Total


Vous pouvez aider vos utilisateurs dès maintenant, juste à côté du PR suivant: ajoutez une étape après l'assemblage - la compression «Jackal», puis livrez des artefacts à votre conteneur. Après une demi-heure, vos utilisateurs se sentent un peu mieux.

Nous avons réussi à réduire le poids du frontal de 30% - vous réussirez! Tous les sites faciles.

Références:


Source: https://habr.com/ru/post/fr474632/


All Articles