Hallo! Ich bin Vanya, die Leiterin des Plattformteams bei Tinkoff Business.
Mein Lieblingsbeschäftigung ist es, die Registerkarte DevTools zu öffnen und zu überprüfen, wie viel Website-Artefakte wiegen. In diesem Artikel erkläre ich Ihnen, wie wir mithilfe des Plattform-Front-End-Teams das Gewicht der Anwendung an einem Tag um 30% reduziert haben, ohne den Site-Code zu ändern. Keine Tricks und Registrierungen - nur nginx, docker und node.js (optional).

Warum?
Jetzt wiegen Front-End-Anwendungen viel. Gesammelte Artefakte können 2-3 MB oder mehr wiegen. Komprimierungsalgorithmen helfen den Benutzern jedoch.
Bis vor kurzem haben wir nur Gzip verwendet, das 1992 in die Welt eingeführt wurde. Dies ist wahrscheinlich der beliebteste Komprimierungsalgorithmus im Web. Er wird von
allen Browsern über IE 6 unterstützt.Ich möchte Sie daran erinnern, dass die Komprimierungsstufe von Gzip im Bereich von 1 bis 9 variiert (mehr ist effizienter) und Sie sie entweder im laufenden Betrieb oder statisch komprimieren können.
- "On the fly" (dynamisch) - Artefakte werden in der Form gespeichert, die nach der Montage empfangen wurde. Ihre Komprimierung erfolgt während der Lieferung an den Client. In unserem Fall auf Nginx-Ebene.
- Statisch - Artefakte werden nach dem Zusammenbau komprimiert und vom HTTP-Server unverändert an den Client gesendet.
Offensichtlich erfordert die erste Option mehr Serverressourcen für jede Anforderung. Die zweite befindet sich in der Phase der Montage und Vorbereitung des Antrags.
Unser Frontend wurde von der vierten Ebene dynamisch komprimiert. Ich werde den Unterschied zwischen einem komprimierten Artefakt und dem Original demonstrieren:
Sie werden vielleicht bemerken, dass sogar die vierte Stufe die Größe des Artefakts um das Vierfache verringert! Der Unterschied zwischen der vierten und der neunten Ebene beträgt 35 KB, das sind 1,3% des Originals, aber die Komprimierungszeit ist zweimal länger.
Und vor kurzem dachten wir: Warum nicht zu Brotli wechseln? Ja und mit der stärksten Komprimierungsstufe!
Dieser Algorithmus wurde übrigens 2015 von Google eingeführt und verfügt über 11 Komprimierungsstufen. Gleichzeitig ist die vierte Stufe von Brotli effektiver als die neunte in Gzip. Ich wurde motiviert und warf schnell Code, um Artefakte mit dem Brotli-Algorithmus zu komprimieren. Die Ergebnisse sind unten dargestellt:
Die Tabelle zeigt jedoch, dass selbst die erste Brotli-Komprimierungsstufe länger dauert als die neunte in Gzip. Und das letzte Level - bis zu 8,3 Sekunden! Es hat mich alarmiert.
Auf der anderen Seite ist das Ergebnis eindeutig beeindruckend. Als nächstes habe ich versucht, die Komprimierung auf nginx zu übertragen -
googeln Sie die Dokumentation . Es stellte sich heraus, dass alles sehr einfach war:
brotli on; brotli_comp_level 11; brotli_types text/plain text/css application/javascript;
Er stellte das Docker-Image zusammen, startete den Container und war furchtbar überrascht:

Die Downloadzeit meiner Datei hat sich verzehnfacht - von 100 ms auf 5 Sekunden! Die Anwendung ist nicht mehr verwendbar.
Nachdem ich die Dokumentation genauer studiert hatte, wurde mir klar, dass Sie statisch verteilen können. Ich habe ein zuvor geschriebenes Skript verwendet, dieselben Artefakte komprimiert, in einen Container gestellt und gestartet. Download mal wieder normal - Sieg! Es ist jedoch zu früh, sich darüber zu freuen, da der Anteil der Browser, die diese Art der Komprimierung unterstützen, bei
etwa 80% liegt .
Dies bedeutet, dass Sie die Abwärtskompatibilität aufrechterhalten müssen und zusätzlich die effektivste Stufe von Gzip verwenden möchten. So entstand die Idee, ein Hilfsprogramm zur Dateikomprimierung zu entwickeln, das später den Namen "Jackal" erhielt.

Was brauchen wir
Nginx, Docker und Node.js, obwohl Sie Bash auch verwenden können, wenn Sie möchten.
Mit Nginx ist fast alles klar:
brotli off; brotli_static on; gzip_static on;
Was tun mit Anwendungen, die das Docker-Image noch nicht aktualisiert haben? Richtig, Abwärtskompatibilität hinzufügen:
gzip on; gzip_level 4; gzip_types text/plain text/css application/javascript;
Ich werde das Funktionsprinzip erklären:
Bei jeder Anforderung sendet der Client einen Accept-Encoding-Header, in dem die unterstützten Komprimierungsalgorithmen durch Kommas getrennt aufgeführt sind. Normalerweise ist es entleert, gzip, br.
Wenn der Client br in der Zeile hat, sucht nginx nach Dateien mit der Erweiterung .br. Wenn es keine solchen Dateien gibt und der Client Gzip unterstützt, sucht er nach .gz. Wenn es keine solchen Dateien gibt, wird es "on the fly" geschüttelt und mit der vierten Komprimierungsstufe zurückgegeben.
Wenn der Client keine Komprimierung unterstützt, gibt der Server Artefakte in ihrer ursprünglichen Form aus.
Es ist jedoch ein Problem aufgetreten: Unser Nginx-Docker-Image unterstützt das Brotli-Modul nicht. Als Basis habe ich das
fertige Docker-Image genommen .
Dockerfile zum "Packen" von Nginx in ein Projekt 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
Wir haben den Verkehrsausgleich herausgefunden, aber woher bekommen wir Artefakte? Hier kommt der Schakal zur Rettung.
Der Schakal
Dies ist ein Dienstprogramm zum Komprimieren der Statik Ihrer Anwendung.
Dies sind nun drei node.js-Skripte, die in ein Docker-Image mit node: alpine eingebunden sind. Lassen Sie uns die Skripte durchgehen.
Basiskompressor - Ein Skript, das die grundlegende Komprimierungslogik implementiert.
Eingabeargumente:
- Komprimierungsfunktion - Mit jeder JavaScript-Funktion können Sie Ihren eigenen Komprimierungsalgorithmus implementieren.
- Komprimierungsparameter - Ein Objekt mit Parametern, die für die übertragene Funktion erforderlich sind.
- Erweiterung - Erweiterung von Kompressionsartefakten. Muss mit einem Punkt beginnen.
gzip.js - Eine Datei mit einem Basis-Kompressor-Aufruf mit der Gzip-Funktion, die vom
zlib- Paket übergeben wurde und die neunte Komprimierungsstufe angibt.
brotli.js - Datei mit einem Basis-Kompressor-Aufruf mit der Brotli-Funktion, die aus
demselben npm-Paket übergeben wurde und die 11. Komprimierungsstufe angibt.
Dockerfile, das das Schakalbild erstellt 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
Wir haben herausgefunden, wie es funktioniert, jetzt können Sie sicher laufen:
docker run \ -v $(pwd)/dist:/scripts/dist \ -e 'dirs=["dist/"]' \ -i mngame/shakal
- -v $ (pwd) / dist: / scripts / dist - Geben Sie an, welches lokale Verzeichnis das Verzeichnis im Container berücksichtigen soll (Link zum Mount). Die Angabe des Skriptverzeichnisses ist erforderlich, da es im Container funktioniert.
- -e 'dirs = ["dist /"]' - gibt den Umgebungsparameter dirs an - ein Array von Zeilen, die die Verzeichnisse in Skripten beschreiben / die komprimiert werden.
- -i mngame / shakal - Angabe eines Bildes mit docker.io.
In den angegebenen Verzeichnissen komprimiert das Skript rekursiv alle Dateien mit den angegebenen Erweiterungen .js, .json, .html, .css und speichert Dateien mit den Erweiterungen .br und .gz daneben. Bei unserem Projekt dauert dieser Vorgang ungefähr zwei Minuten, wobei das Gewicht aller Artefakte ungefähr 6 MB beträgt.
Zu diesem Zeitpunkt und vielleicht noch früher haben Sie vielleicht gedacht: „Welcher Hafenarbeiter? Welcher Knoten? Warum fügen Sie sich nicht einfach zwei Pakete in der package.json des Projekts hinzu und rufen direkt beim Postbuild auf? “
Es ist sehr schmerzhaft für mich zu sehen, wann das Projekt zum Ausführen von Lintern in CI mehr als 100 Pakete für sich selbst installiert, von denen es in der Flusenphase maximal 10 benötigt. Dies ist die Agentenzeit, schließlich Ihre Zeit bis zur Markteinführung.
Im Fall des Dockers erhalten wir ein vormontiertes Image, in dem alles installiert ist, was für die Komprimierung erforderlich ist. Wenn Sie jetzt nichts komprimieren müssen, komprimieren Sie nicht. Benötigen Sie einen Fussel - führen Sie ihn nur aus, benötigen Sie Tests - führen Sie nur sie aus. Außerdem erhalten wir eine gute Version von Jackal: Wir müssen die Abhängigkeiten nicht in jedem Projekt aktualisieren - veröffentlichen Sie einfach eine neue Version und verwenden Sie das neueste Tag für das Projekt.
Ergebnis:
- Die Größe der Artefakte hat sich von 636 Kb auf 446 Kb geändert.
- Die prozentuale Größe nahm um 30% ab.
- Die Downloadzeit wurde um 10-12% verringert.
- Die Dekompressionszeit, basierend auf dem Artikel , bleibt gleich.
Total
Sie können Ihren Benutzern jetzt, gleich beim nächsten PR, helfen: Fügen Sie nach dem Zusammenbau einen Schritt hinzu - "Jackal" -Komprimierung, und liefern Sie dann Artefakte an Ihren Container. Nach einer halben Stunde fühlen sich Ihre Benutzer etwas besser.
Wir haben es geschafft, das Gewicht des Frontends um 30% zu reduzieren - Sie werden Erfolg haben! Alle einfachen Seiten.
Referenzen: