Unser Hauptprodukt - die Website - erhält ständig neue Funktionen, wird jedoch allmählich schwieriger und unhandlicher. Daher ist unsere technische Abteilung von Zeit zu Zeit damit beschäftigt, dies einfacher und schneller zu machen.
Eines der Hauptelemente, die die Ladegeschwindigkeit fast aller Websites, insbesondere einer Medienwebsite, beeinflussen, sind Bilder. Es gibt viele Bilder auf Meduza, und dies ist eine wertvolle Möglichkeit für die Redakteure, Geschichten zu erzählen. Die Anforderungen unseres Fotoservices können wie folgt formuliert werden:
- Das Bild muss so schnell wie möglich auf das CMS hochgeladen werden (wir nennen es "Monitor")
- Das Bild sollte schön bleiben und auf allen Plattformen gut aussehen
- Der Leser sollte nicht warten, bis dieses Bild geladen ist
Erste Ansatz
Als wir 2014 starteten, sah der Prozess der Arbeit mit Bildern, die in den "Monitor" geladen wurden, folgendermaßen aus: Die Datei wurde mit Paperclip und Imagemagick in eine Rails-Anwendung geladen, von Metadaten befreit, mit der ausgewählten Qualität komprimiert (für JPEG waren es etwa 75) und In drei Größen schneiden: klein für Telefone, größer für Tablets und Telefone mit großen Bildschirmen und sehr groß für Computer. Die in Scheiben geschnittenen Dateien wurden zusammen mit dem Original im AWS-Cloud-Speicher abgelegt. Sie wurden (und werden) nicht direkt von AWS gegeben, sondern über unser CDN, das es auf seinen Edgeservern zwischenspeichert.
Die API, die JSON für die Site und die Anwendungen generiert, lieferte dann neben einfachen Attributen wie Überschriften von Materialien auch einen großen Teil des HTML-Codes, der in den "Inhalt" -Teil des Materials eingefügt und mit CSS-Stilen aufgehängt wurde. Und die API selbst war dann für alle Clients gleich: die Site, Anwendungen und unterstützende Dienste wie RSS und Suche.
Uns war sofort klar, dass ein solcher Ansatz den Test der Zeit nicht bestehen würde, aber wir mussten schnell starten, eine große Anzahl von Hypothesen testen, experimentieren und überleben. Wir haben uns bewusst - zumindest glauben wir daran - für "Krücken" entschieden und nicht für die Schönheit des Codes und der Lösungen. Die Zeit verging, unser Publikum und unser Produkt wuchsen. Gleichzeitig wuchs der Appetit der Redakteure. Die Redakteure wollten immer mehr Techniken und "Tricks" in ihren Materialien. Gleichzeitig hat sich natürlich auch das Design geändert.
Die technische Abteilung musste die Stile der Bilder, die Darstellungsmethoden und die Größen heraufbeschwören - sie änderten sich zusammen mit Änderungen im Design und neuen Elementen auf der Website. Wir haben immer seltsamere Lösungen hinzugefügt und unterstützt.
Hier ist einer von ihnen
Im Laufe der Zeit machte der Monitor alle wütend, die auf ihn stießen: Die Redaktion litt unter Fehlern, da die Behebung dieser Fehler viel Zeit in Anspruch nahm, die Entwickler wütend waren und die Einschränkungen der erfundenen Architektur nicht den Vorgesetzten entsprachen - wir konnten das Produkt nicht schnell entwickeln.
Wir haben mit der Neugestaltung des Monitors begonnen. Wir haben den Hauptteil des CMS neu geschrieben, der für etwa ein Jahr für die Arbeit an Materialien verantwortlich ist, und werden regelmäßig durch Fehler in der alten Version abgelenkt. Dann erschien ein internes Mem in "Meduza": "Es wird im neuen Monitor sein." Auf diese Weise haben wir die meisten Anfragen der Redakteure beantwortet, obwohl wir im alten und im neuen CMS natürlich einige Dinge gleichzeitig erledigt haben: eine schlechte Version für jetzt und eine gute Version für später.
Ich werde in Kürze einen separaten Beitrag in unserem Blog über die Änderung von "Monitor" schreiben .
Neu starten
Nach dem Neustart und der Neuerstellung des gesamten Meduza blieb die API im Format gleich, wurde jedoch nicht mehr vom CMS selbst erstellt, sondern von einem separaten Dienst verarbeitet. Und wir haben uns schließlich entschlossen, die API in mehrere zu unterteilen - für verschiedene Clients.
Wir haben uns entschlossen, mit mobilen Anwendungen zu beginnen. Zu diesem Zeitpunkt hatten sie bereits Fragen zu Design, UX und Arbeitsgeschwindigkeit. Deshalb haben wir die Anwendungen aufgeräumt und die API so gestaltet, wie es unsere iOS- und Android-Entwickler wollten.
Vor der Trennung der API haben wir den Inhaltsteil der Materialien über eine WebView angezeigt, sodass wir nicht ausschließlich in der Anwendung oder ausschließlich auf der Website etwas anzeigen konnten - alles wurde überall angezeigt. Jetzt haben wir die Möglichkeit, Inhalte flexibler zu verwalten: nur das zu senden, was für mobile Anwendungen erforderlich ist, schwere Elemente auf andere Weise anzuzeigen (z. B. YouTube-Videoeinfügungen) und schließlich Lazy Load zu erstellen, mit dem Sie nach und nach schwere Elemente in das Material laden können - Bilder und Einbettungen.
Nachdem wir die Anwendungen getrennt hatten, konzentrierten wir uns auf die Site. Zu diesem Zeitpunkt wurde bereits beschlossen, den Code der vorhandenen Site nicht zu ändern, sondern alles von Grund auf neu zu schreiben (ja, wir haben uns an einem Projekt beteiligt, das wieder mehr als ein Jahr dauerte). Gleichzeitig wurde unser technischer Direktor ersetzt, und in meiner neuen Position musste ich Projekte prüfen, entscheiden, was geschlossen werden könnte, und es mit meinen Vorgesetzten koordinieren. Trotz aller Schwierigkeiten entschied sich das Team, die neue Website fertigzustellen. Aber vorher habe ich beschlossen, dass wir ein anderes Projekt brauchen.
So erschien das UI-Kit von Medusa.
Um Inhalte flexibel bereitstellen zu können, musste sie in einer Form vorliegen, die für die Transformation am besten geeignet war. Die semantischen Grenzen von Inhaltseinheiten - Bilder, Textabschnitte, Überschriften - sollten gut aufeinander abgestimmt sein. Ein Inhalt sollte nicht zu einfach oder zu komplex sein. Wenn es komplex ist, hat es höchstwahrscheinlich mehr als eine Bedeutung. Wenn es zu einfach ist, muss es höchstwahrscheinlich mit komplizierter Logik gewichtet werden, und wenn Sie eine solche Einheit wiederverwenden müssen, müssen Sie sie in verschiedene Teile des Projektcodes kopieren.
Nehmen wir zum Beispiel Medusas Spiele.... Sie ermöglichen es Ihnen, eine große Anzahl von Geschichten auf spielerische Weise zu erzählen. Sie können speziell für die Tagesordnung oder auf Anfrage des Werbetreibenden erstellt werden. Oder das Spiel kann auf der Basis sogenannter Mechanik erstellt werden - Formate, die wiederholt verwendet werden (dies sind beispielsweise Tests).
Spiele auf Meduza: Wie wir entwerfen, herstellen und wiederverwenden
Der Code dieser Spiele liegt nicht im Code der Website. Jeder von ihnen wird als separater Microservice erstellt, über einen Iframe in die Site eingebettet und über postMessage mit der Site selbst kommuniziert. Und der Seite ist es egal, was an dem Ort gezeigt werden soll, an dem das Spiel stattfinden wird. Darüber hinaus sollte das Spiel selbst visuell untrennbar mit der Site verbunden sein: Typografie und Oberflächenelemente sollten identisch sein.
Als wir anfingen, Spiele zu machen, haben wir Stile, Schaltflächen und andere Dinge kopiert, und natürlich war es schnell und es sah von außen in Ordnung aus, aber von innen war es schrecklich.
Das Team entschied, dass dies geändert werden sollte. Wir haben die Korrespondenz der Site angehalten und begonnen, unser eigenes UI-Kit zu erstellen - eine Bibliothek, die alle sich wiederholenden Elemente und Stile enthält. Wir haben versucht, eine lange Perspektive nicht zu verpassen: die Site, die Spiele und die Mechanik - alles musste dieselbe Bibliothek verwenden.
Alle Meduza-Projekte im Web sind in React geschrieben, und das UI-Kit ist ein npm-Modul, das sich jetzt in fast alles einfügt, was wir entwickeln. Und wenn der Entwickler etwas rendern muss, schreibt er so etwas: Renderblöcke.
Was tut es?
- Der Front-End-Entwickler überlegt nicht genau, wie er etwas rendern soll.
- Alles wurde bereits von der Designabteilung überprüft.
- : UI-kit, , , . , «», .
- — - UI-kit ( ) .
UI-kit
Es gibt zwei Gruppen von Komponenten: Inhalt und Frontend. Letztere sind sehr einfach. Reagieren Sie auf Komponenten wie Schaltflächen und Symbole.
Inhaltskomponenten sind etwas komplexer, aber immer noch sehr einfach. Reagieren Sie auf Komponenten mit Stilen, die eine Inhaltseinheit darstellen. So sieht beispielsweise eine Absatzkomponente aus:
Eine Komponente, die einfache Blöcke "abfängt"
Und dies ist eine Komponente, die ein Bild rendert: Die
API, von der die Site Daten entnimmt, enthält in jedem Material ein Array von Komponenten, die schließlich über das UI-Kit "gerendert" werden ... Bei Spielen funktioniert alles gleich, sie gehen einfach für ihre Daten zu ihren Versionen der API.
Hurra, wir haben neu gestartet!
Unten sehen Sie eine Grafik, die die durchschnittliche Ladezeit der Seite zeigt. Dies ist ein sehr ungenauer Indikator - alle Seiten auf allen Geräten werden berücksichtigt -, aber selbst er gibt eine Vorstellung vom Trend.
Die Grafik zeigt, wie sich die Geschwindigkeit der Website während der gesamten Existenz von Meduza geändert hat. Als wir 2014 zum ersten Mal mit einer sehr einfachen Website starteten, war dies sehr schnell. Als wir jedoch neue Funktionen hinzufügten, sank die Download-Geschwindigkeit.
Und hier ist der gleiche Zeitplan, aber für die letzten zwei Jahre. Es zeigt, wie die Ladezeiten der Seite nach dem Neustart der Site gesunken sind.
Dann kam endlich die Zeit für Bilder.
Bilder
Das Schema der Arbeit mit Bildern war dann. Das Bild kam über die API, wo es eine Adresse wie /images/attachments/.../random.jpg hatte . Die Datei selbst wurde aus dem AWS-Cloud-Speicher über unser CDN bereitgestellt.
Wir haben die Anforderungen für das neue System wie folgt formuliert:
- Die Lösung sollte es uns ermöglichen, die Größe und Qualität der gesendeten Bilder schnell zu ändern
- es muss nicht teuer sein
- Es muss in der Lage sein, viel Verkehr zu bewältigen
Das Schema, das wir anstrebten, stellte sich so heraus. Das Backend würde eine URL generieren, die vom Client - Browser oder Anwendung - abgerufen wird. Die URL würde Informationen darüber enthalten, welches Bild in welcher Qualität und in welcher Größe benötigt wird.
Wenn sich das Bild unter dieser URL bereits auf dem Edgeserver befindet, wird es sofort an den Client gesendet. Wenn nicht, würde der Edgeserver auf den nächsten Server "klopfen", der die Anforderung bereits an den Dienst weitergeleitet hätte. Dieser Dienst, der die URL des Bildes erhalten hat, würde es dekodieren und die Adresse des Originalbilds und die Liste der Operationen darauf bestimmen. Danach würde der Dienst das transformierte Bild bereitstellen, so dass es im CDN gespeichert und auf Anfrage bereitgestellt wird.
Meduza hat bereits ähnliche Lösungen. Zum Beispiel machen wir in ähnlicher Weise Bilder für Ausschnitte unserer Materialien und Spiele in sozialen Netzwerken - in diesem Service machen wir Screenshots von HTML-Seiten über Headless Chrome.
Der neue Dienst sollte in der Lage sein, mit Bildern zu arbeiten, einfache Effekte anzuwenden, schnell und belastbar zu sein. Da wir gerne alles selbst schreiben, war ursprünglich geplant, einen solchen Dienst in der Elixiersprache zu schreiben. Aber niemand im Team hatte genug Zeit und sicherlich hatte niemand den Wunsch, in die wunderbare Welt von JPG, PNG und GIF einzutauchen.
Wir mussten noch eine Bibliothek finden, die Bilder komprimiert. Imagemagick, mit dem wir bereits Erfahrung hatten, war nicht die schnellste Lösung, aber zumindest wussten wir, wie man es kocht. Alles andere war neu und es würde viel zu überprüfen geben.
Wir brauchten auch Unterstützung für das Webp-Bildformat.
Mit der Zeit haben wir uns mental darauf vorbereitet, in dieses Projekt einzutauchen. Aber dann las einer unserer Programmierer über die Imgproxy-Bibliothek, die die Jungs von Evil Martians auf Open Source hochgeladen hatten . Laut Beschreibung war es ein perfekter Hit: Go, Libvps, ein fertiges Docker-Image, Konfiguration über Env. Am selben Tag stellten wir die Bibliothek auf unseren Laptops bereit und baten unsere DevOps, auch damit zu spielen. Seine Aufgabe war es, den Dienst aufzurufen und zu versuchen, ihn zu beenden - damit wir verstehen, welchen Belastungen er auf unseren Servern standhält. Während dieser Zeit spielte das Backend-Team weiterhin mit dem Projekt auf seinen Computern: Wir haben Ruby-Skripte geschrieben und die verfügbaren Funktionen beherrscht.
Als DevOps mit dem Urteil zurückkam, dass die Bibliothek für die Produktion verwendet werden könnte, sammelten wir eine große Anzahl von Bildern, die über imgproxy weitergeleitet wurden - wir waren hauptsächlich an webp interessiert - und nahmen ihre Fotoanweisungen entgegen. Die Mitarbeiter der technischen Abteilung können nicht selbst entscheiden, ob diese Qualität zu uns passt oder nicht. Die Bildbearbeiter haben uns ihre Kommentare geschickt, wir haben etwas optimiert, dafür gesorgt, dass die Bilder nicht viel wiegen, und haben den Backend-Code geschrieben.
Hier stellte sich alles als sehr einfach heraus: Da in der Version der API für die Site die Bilder bereits durch Komponenten von allem anderen getrennt waren, haben wir einfach ihren JSON erweitert und zusätzliche Adressen in verschiedenen Größen und Formaten hinzugefügt.
Das aktualisierte ging sofort in Produktion, da wir nichts geändert, sondern nur hinzugefügt haben - die Leute im Frontend konnten Funktionen alle in derselben Version der API entwickeln. Sie erweiterten die Bildkomponente in Bezug auf Funktionen, fügten Bildsätze für verschiedene Größen und eine spezielle "Krücke" für Safari hinzu. Wir haben die Größentabelle ein paar Mal geändert und das Ergebnis mit den Augen der Redaktion überprüft. Dazu haben wir ihr die aktuelle Version der Website und ein Duplikat mit neuen Bildern zur Überprüfung gegeben.
Als die Kommentare nicht mehr eingingen, haben wir sie endlich bereitgestellt, den Cache gelöscht und in die Produktion eingeführt.
Ausgabe
Das Wesentliche unserer Änderungen lässt sich wie folgt zusammenfassen: Wir haben den Ort der Entscheidungsfindung, welches Bild in welcher Form zu geben ist, an den Ort verlagert, an dem der Kontext besser berücksichtigt wird und Implementierung und Unterstützung einfacher zu implementieren sind.
Jetzt sind fast alle Bilder, die Sie auf Meduza sehen, das Ergebnis der Arbeit von imgproxy und haben jeweils unterschiedliche Größen und manchmal unterschiedliche Qualität. Dies wird durch den Kontext bestimmt - ob der Inhalt im Web, in der mobilen App oder in AMP geöffnet ist -, den der API-Dienst, der die Antworten generiert, kennt.