Das Material, dessen Übersetzung wir heute veröffentlichen, ist der Geschichte über die Optimierung der neuen Version des
Slack- Desktop-Clients gewidmet, deren zentrales Merkmal die Beschleunigung des Ladens war.

Hintergrund
Zu Beginn der Arbeiten an der neuen Version des Slack-Desktop-Clients wurde ein Prototyp erstellt, der als „schnelle Stiefel“ bezeichnet wurde. Das Ziel dieses Prototyps war, wie Sie sich vorstellen können, den Download so weit wie möglich zu beschleunigen. Mithilfe der HTML-Datei aus dem CDN-Cache, der im Voraus gespeicherten Redux-Speicherdaten und des Servicemitarbeiters konnten wir die Light-Version des Clients in weniger als einer Sekunde laden (zu diesem Zeitpunkt betrug die übliche Downloadzeit für Benutzer mit 1-2 Arbeitsbereichen etwa 5 Sekunden ) Der Servicemitarbeiter war das Zentrum dieser Beschleunigung. Darüber hinaus ebnete er den Weg für Möglichkeiten, die Slack-Benutzer häufig umsetzen sollten. Wir sprechen über den Offline-Modus des Clients. Mit dem Prototyp konnten wir buchstäblich mit einem Auge sehen, wozu der neu erstellte Client in der Lage sein kann. Basierend auf den oben genannten Technologien haben wir begonnen, den Slack-Client zu verarbeiten, uns das Ergebnis grob vorzustellen und uns darauf zu konzentrieren, den Download zu beschleunigen und den Offline-Modus des Programms zu implementieren. Lassen Sie uns darüber sprechen, wie der Kern des aktualisierten Clients funktioniert.
Was ist ein Servicemitarbeiter?
Service Worker ist im Wesentlichen ein leistungsstarkes Proxy-Objekt für Netzwerkanforderungen, mit dem der Entwickler mithilfe einer kleinen Menge JavaScript-Code steuern kann, wie der Browser einzelne HTTP-Anforderungen verarbeitet. Servicemitarbeiter unterstützen eine erweiterte und flexible Caching-API, die so konzipiert ist, dass Anforderungsobjekte als Schlüssel und Antwortobjekte als Werte verwendet werden. Service Worker werden wie Web Worker in ihren eigenen Prozessen außerhalb des Hauptthreads zur Ausführung von JavaScript-Code in einem beliebigen Browserfenster ausgeführt.
Service Worker ist ein Anhänger des
Anwendungscaches , der jetzt veraltet ist. Es handelte sich um eine Reihe von APIs, die von der
AppCache
Schnittstelle dargestellt wurden und zum Erstellen von Websites verwendet wurden, die Offline-Funktionen implementieren. Bei der Arbeit mit
AppCache
wurde eine statische Manifestdatei verwendet, die die Dateien beschreibt, die der Entwickler für die Offline-Verwendung zwischenspeichern möchte. Im Großen und Ganzen waren die
AppCache
Funktionen
AppCache
beschränkt. Dieser Mechanismus war einfach, aber nicht flexibel, was dem Entwickler keine besondere Kontrolle über den Cache gab. Bei W3C wurde dies bei der Entwicklung
der Service Worker-Spezifikation berücksichtigt. Infolgedessen ermöglichen Servicemitarbeiter dem Entwickler, viele Details zu jeder von einer Webanwendung oder Website durchgeführten Netzwerkinteraktionssitzung zu verwalten.
Als wir anfingen, mit dieser Technologie zu arbeiten, war Chrome der einzige Browser, der sie unterstützte, aber wir wussten, dass nicht viel Zeit blieb, um auf eine umfassende Unterstützung für Servicemitarbeiter zu warten. Jetzt ist diese Technologie
überall und wird von allen gängigen Browsern unterstützt.
Wie wir Servicemitarbeiter einsetzen
Wenn ein Benutzer zum ersten Mal einen neuen Slack-Client startet, laden wir einen vollständigen Satz von Ressourcen (HTML, JavaScript, CSS, Schriftarten und Sounds) herunter und platzieren sie im Cache des Servicemitarbeiters. Außerdem erstellen wir eine Kopie des Redux-Speichers im Speicher und schreiben diese Kopie in die IndexedDB-Datenbank. Wenn das Programm das nächste Mal gestartet wird, prüfen wir, ob entsprechende Caches vorhanden sind. Wenn dies der Fall ist, verwenden wir sie beim Herunterladen der Anwendung. Wenn der Benutzer mit dem Internet verbunden ist, laden wir nach dem Starten der Anwendung die neuesten Daten herunter. Wenn nicht, bleibt der Client betriebsbereit.
Um zwischen den beiden oben genannten Optionen zum Laden des Clients zu unterscheiden, haben wir ihnen die Namen gegeben: heißer (warmer) und kalter (kalter) Download. Der Kaltstart eines Clients tritt am häufigsten auf, wenn der Benutzer das Programm zum ersten Mal startet. In dieser Situation sind keine zwischengespeicherten Ressourcen oder Redux-Daten gespeichert. Mit einem Hotboot haben wir alles, was Sie brauchen, um den Slack-Client auf dem Computer des Benutzers auszuführen. Bitte beachten Sie, dass die meisten binären Ressourcen (Bilder, PDFs, Videos usw.) über den Browser-Cache verarbeitet werden (diese Ressourcen werden durch reguläre Caching-Header gesteuert). Ein Servicemitarbeiter sollte sie nicht auf besondere Weise verarbeiten, damit wir offline mit ihnen arbeiten können.
Die Wahl zwischen heißer und kalter BeladungLebenszyklus von Servicemitarbeitern
Servicemitarbeiter können drei Lebenszyklusereignisse verarbeiten. Diese werden
installiert ,
abgerufen und
aktiviert . Im Folgenden werden wir darüber sprechen, wie wir auf jedes dieser Ereignisse reagieren. Zunächst müssen wir jedoch über das Herunterladen und Registrieren des Servicemitarbeiters selbst sprechen. Der Lebenszyklus hängt davon ab, wie der Browser die Aktualisierungen der Service Worker-Dateien verarbeitet. Folgendes können Sie auf
MDN darüber lesen: „Die Installation ist abgeschlossen, wenn die heruntergeladene Datei als neu erkannt wird. Dies kann entweder eine Datei sein, die sich von der vorhandenen unterscheidet (der Unterschied zwischen den Dateien wird durch einen Byte-Vergleich ermittelt), oder eine Service-Worker-Datei, die der Browser auf der verarbeiteten Seite zum ersten Mal gefunden hat. “
Jedes Mal, wenn wir die entsprechende JavaScript-, CSS- oder HTML-Datei aktualisieren, wird das benutzerdefinierte Webpack-Plugin durchlaufen, das ein Manifest mit einer Beschreibung der entsprechenden Dateien mit eindeutigen Hashes erstellt (
hier ein abgekürztes Beispiel für eine Manifestdatei). Dieses Manifest ist in den Service Worker-Code eingebettet, wodurch der Service Worker beim nächsten Start aktualisiert wird. Darüber hinaus erfolgt dies auch dann, wenn sich die Implementierung des Servicemitarbeiters nicht ändert.
▍Ereignisereignis
Jedes Mal, wenn ein Servicemitarbeiter aktualisiert wird, wird ein
install
angezeigt. Als Antwort darauf gehen wir die Dateien durch, deren Beschreibungen in dem im Service Worker integrierten Manifest enthalten sind, laden sie jeweils und platzieren sie im entsprechenden Cache-Block. Die Dateispeicherung wird mithilfe der neuen
Cache- API organisiert, die Teil der Service Worker-Spezifikation ist. Diese API speichert
Response
Verwendung von
Request
als Schlüssel. Als Ergebnis stellt sich heraus, dass die Speicherung erstaunlich einfach ist. Es passt gut dazu, wie Service-Worker-Ereignisse Anforderungen empfangen und Antworten zurückgeben.
Schlüssel zum Zwischenspeichern von Blöcken werden basierend auf der Bereitstellungszeit der Lösung zugewiesen. Der Zeitstempel ist in den HTML-Code eingebettet. Daher kann er als Teil des Dateinamens in der Anforderung zum Herunterladen jeder Ressource gesendet werden. Das separate Zwischenspeichern von Ressourcen von jeder Bereitstellung ist wichtig, um zu vermeiden, dass inkompatible Ressourcen gemeinsam genutzt werden. Dank dessen können wir sicher sein, dass die ursprünglich heruntergeladene HTML-Datei nur kompatible Ressourcen herunterlädt. Dies gilt sowohl für das Herunterladen über das Netzwerk als auch für das Herunterladen aus dem Cache.
▍Event holen
Nachdem der Servicemitarbeiter registriert wurde, beginnt er mit der Verarbeitung aller Netzwerkanforderungen, die zur selben Quelle gehören. Ein Entwickler kann es nicht schaffen, dass einige Anforderungen von einem Servicemitarbeiter verarbeitet werden, andere nicht. Der Entwickler hat jedoch die volle Kontrolle darüber, was genau mit den vom Servicemitarbeiter eingegangenen Anforderungen zu tun ist.
Bei der Bearbeitung einer Anfrage prüfen wir diese zunächst. Wenn das Angeforderte im Manifest vorhanden ist und sich im Cache befindet, geben wir die Antwort auf die Anforderung zurück, indem wir die Daten aus dem Cache entnehmen. Wenn der Cache nicht über das verfügt, was Sie benötigen, geben wir eine echte Netzwerkanforderung zurück, die auf die reale Netzwerkressource zugreift, als wäre der Servicemitarbeiter überhaupt nicht an diesem Prozess beteiligt. Hier ist eine vereinfachte Version unseres
fetch
:
self.addEventListener('fetch', (e) => { if (assetManifest.includes(e.request.url) { e.respondWith( caches .open(cacheKey) .then(cache => cache.match(e.request)) .then(response => { if (response) return response; return fetch(e.request); }); ); } else { e.respondWith(fetch(e.request)); } });
In der Realität enthält ein solcher Code viel mehr Slack-spezifische Logik, aber der Kern unseres Handlers ist so einfach wie in diesem Beispiel.
Bei der Analyse von Netzwerkinteraktionen werden die vom Service Worker zurückgegebenen Antworten an der ServiceWorker-Markierung in der Spalte erkannt, die die Datenmenge angibt▍Event aktivieren
Das
activate
wird nach der erfolgreichen Installation eines neuen oder aktualisierten Servicemitarbeiters ausgelöst. Wir verwenden es, um zwischengespeicherte Ressourcen zu analysieren und Cache-Blöcke ungültig zu machen, die älter als 7 Tage sind. Dies ist eine bewährte Methode, um das System in Ordnung zu halten. Außerdem können Sie sicherstellen, dass beim Laden des Clients keine zu alten Ressourcen verwendet werden.
Client-Code, der hinter der neuesten Version zurückbleibt
Möglicherweise haben Sie bemerkt, dass unsere Implementierung impliziert, dass jeder, der den Slack-Client nach dem ersten Start des Clients startet, nicht die neuesten, sondern zwischengespeicherten Ressourcen erhält, die während der vorherigen Registrierung des Servicemitarbeiters geladen wurden. In der ursprünglichen Client-Implementierung haben wir versucht, den Service Worker nach jedem Download zu aktualisieren. Ein typischer Slack-Benutzer kann jedoch beispielsweise ein Programm nur einmal am Tag morgens herunterladen. Dies kann dazu führen, dass er ständig mit einem Kunden zusammenarbeitet, dessen Code für den ganzen Tag hinter der neuesten Version zurückbleibt (wir veröffentlichen mehrmals täglich neue Versionen).
Im Gegensatz zu einer typischen Website, die beim Besuch schnell verlassen wird, ist der Slack-Client auf dem Computer des Benutzers stundenlang geöffnet und befindet sich im geöffneten Zustand. Infolgedessen hat unser Code eine ziemlich lange Lebensdauer, weshalb wir spezielle Ansätze verwenden müssen, um seine Relevanz zu erhalten.
Gleichzeitig bemühen wir uns sicherzustellen, dass Benutzer mit den neuesten Versionen des Codes arbeiten, damit sie die neuesten Funktionen, Fehlerbehebungen und Leistungsverbesserungen erhalten. Kurz nachdem wir einen neuen Client veröffentlicht haben, haben wir einen Mechanismus implementiert, mit dem wir die Lücke zwischen dem, mit dem Benutzer arbeiten, und dem, was wir veröffentlicht haben, schließen können. Wenn nach dem letzten Update eine neue Version des Systems bereitgestellt wurde, laden wir neue Ressourcen, die beim nächsten Start des Clients verwendet werden. Wenn nichts Neues gefunden werden kann, wird nichts geladen. Nachdem diese Änderung am Client vorgenommen wurde, wurde die durchschnittliche Lebensdauer der Ressourcen, mit denen der Client geladen wurde, halbiert.
Neue Versionen des Codes werden regelmäßig heruntergeladen, beim Herunterladen des Programms wird jedoch nur die neueste Version verwendetNew Feature Flags Sync
Mit Hilfe von Flags neuer Features (Feature Flags) markieren wir in der Codebasis die Arbeiten, an denen noch nicht abgeschlossen wurde. Dies ermöglicht es uns, neue Funktionen vor ihrer Veröffentlichung in den Code aufzunehmen. Dieser Ansatz verringert das Risiko von Produktionsfehlern, da neue Funktionen zusammen mit dem Rest der Anwendung frei getestet werden können, lange bevor die Arbeiten an ihnen abgeschlossen sind.
Neue Funktionen in Slack werden normalerweise freigegeben, wenn sie Änderungen an den entsprechenden APIs vornehmen. Bevor wir Servicemitarbeiter einsetzten, hatten wir die Garantie, dass neue Funktionen und Änderungen in der API immer synchronisiert werden. Nachdem wir jedoch begonnen hatten, den Cache zu verwenden, der möglicherweise nicht die neueste Version des Codes enthält, stellte sich heraus, dass sich der Client möglicherweise in einer Situation befindet, in der der Code nicht mit den Backend-Funktionen synchronisiert ist. Um dieses Problem zu lösen, werden nicht nur Ressourcen, sondern auch einige API-Antworten zwischengespeichert.
Die Tatsache, dass Servicemitarbeiter absolut alle Netzwerkanforderungen verarbeiten, hat die Lösung vereinfacht. Bei jedem Update des Service Workers führen wir unter anderem API-Anforderungen aus und speichern Antworten im selben Cache-Block wie die entsprechenden Ressourcen. Dies verbindet Fähigkeiten und experimentelle Funktionen mit den richtigen Ressourcen - möglicherweise veraltet, aber garantiert konsistent miteinander.
Dies ist in der Tat nur die Spitze des Eisbergs der Möglichkeiten, die dem Entwickler dank Servicemitarbeitern zur Verfügung stehen. Ein Problem, das mit dem
AppCache
Mechanismus nicht gelöst werden konnte oder bei dem sowohl Client- als auch Servermechanismen gelöst werden müssten, wird einfach und natürlich mit Service Workern und der Cache-API gelöst.
Zusammenfassung
Der Servicemitarbeiter beschleunigte das Laden des Slack-Clients, indem er die lokale Speicherung von Ressourcen organisierte, die beim nächsten Start des Clients einsatzbereit sind. Das Netzwerk - die Hauptursache für Verzögerungen und Unklarheiten, auf die unsere Benutzer stoßen könnten - hat praktisch keine Auswirkungen mehr auf die Situation. Wir haben es sozusagen aus der Gleichung entfernt. Und wenn Sie das Netzwerk aus der Gleichung entfernen können, stellt sich heraus, dass Sie Offline-Funktionen im Projekt implementieren können. Unsere Unterstützung für den Offline-Modus ist derzeit sehr einfach. Der Benutzer kann den Client herunterladen und Nachrichten aus heruntergeladenen Konversationen lesen. Gleichzeitig bereitet sich das System auf Synchronisationsmarkierungen bei gelesenen Nachrichten vor. Jetzt haben wir jedoch eine Grundlage für die künftige Implementierung fortschrittlicherer Mechanismen.
Nach vielen Monaten der Entwicklung, des Experimentierens und der Optimierung haben wir viel darüber gelernt, wie Servicemitarbeiter in der Praxis arbeiten. Darüber hinaus stellte sich heraus, dass diese Technologie für Großprojekte gut geeignet ist. In weniger als einem Monat nach der Veröffentlichung eines Kunden mit einem Servicemitarbeiter bearbeiten wir täglich erfolgreich Millionen von Anfragen von Millionen installierter Servicemitarbeiter. Dies führte zu einer Verkürzung der Ladezeit neuer Kunden um etwa 50% im Vergleich zu alten Kunden und zu der Tatsache, dass das Heißladen etwa 25% schneller ist als das Kaltladen.
Von links nach rechts: Laden eines alten Clients, Kaltladen eines neuen Clients, Heißladen eines neuen Clients (je niedriger der Indikator, desto besser)Liebe Leser! Verwenden Sie in Ihren Projekten Servicemitarbeiter?
