Notre produit principal - le site - est constamment envahi par de nouvelles fonctions, mais à cause de cela, il devient progressivement plus lourd et plus difficile à manier. Par conséquent, notre service technique est occupé de temps en temps pour le rendre plus facile et plus rapide.
Les images sont l'un des principaux éléments qui affectent la vitesse de chargement de presque tous les sites, en particulier un site multimédia. Il y a beaucoup d'images sur Meduza, et c'est un moyen précieux pour les éditeurs de raconter des histoires. Les exigences de notre service photo peuvent être formulées comme suit:
- l'image doit être téléchargée sur le CMS (nous l'appelons "Monitor") le plus rapidement possible
- l'image doit rester belle et bien paraître sur toutes les plateformes
- le lecteur n'a pas à attendre le chargement de cette image
Première approche
Lorsque nous avons lancé en 2014, le processus de travail avec les images chargées dans le moniteur ressemblait à ceci: le fichier a été chargé dans une application Rails, en utilisant Paperclip et Imagemagick, il a été effacé des métadonnées, compressé avec la qualité sélectionnée (pour JPEG, il était d'environ 75) et coupé en trois tailles: petit pour les téléphones, plus grand pour les tablettes et les téléphones avec de grands écrans et très grand pour les ordinateurs. Les fichiers découpés ont été placés dans le stockage cloud AWS avec l'original. Ils ont été donnés (et sont donnés) non directement à partir d'AWS, mais via notre CDN, qui les met en cache sur ses serveurs périphériques.
L'API qui génère du JSON pour le site et les applications, puis, en plus d'attributs simples tels que les en-têtes de matériaux, a également donné un énorme morceau de code HTML, qui a été inséré dans la partie «contenu» du matériel et pondéré avec des styles CSS. Et l'API elle-même était alors la même pour tous les clients: le site, les applications et les services de support comme RSS et la recherche.
Il était immédiatement clair pour nous que cette approche ne résisterait pas à l'épreuve du temps, mais il fallait se lancer rapidement, tester un grand nombre d'hypothèses, expérimenter, survivre. Nous avons consciemment - du moins nous y croyons - choisi des «béquilles», et non la beauté du code et des solutions. Le temps a passé, notre audience et notre produit ont grandi. Et avec cela, l'appétit des éditeurs a grandi. Les éditeurs voulaient de plus en plus de techniques et de "trucs" dans leurs matériaux. En même temps, bien sûr, le design a également changé.
Le département technique a dû évoquer les styles des images, les méthodes d'affichage, les tailles - ils ont changé avec les changements de conception et les nouveaux éléments sur le site. Nous avons ajouté et soutenu des solutions de plus en plus étranges.
Voici l'un d'entre eux
Au fil du temps, le Monitor a rendu furieux tous ceux qui le rencontraient de plus en plus: la rédaction souffrait de bugs, car la correction de ces bugs prenait beaucoup de temps, les développeurs étaient furieux et les limites de l'architecture inventée ne convenaient pas aux patrons - nous ne pouvions pas développer rapidement le produit.
Nous avons commencé à repenser le moniteur. Nous avons réécrit l'essentiel du CMS chargé de travailler sur les matériaux pendant environ un an, étant régulièrement distraits par les bugs de l'ancienne version. Puis un mème interne est apparu dans "Meduza": "Ce sera dans le nouveau moniteur." C'est ainsi que nous avons répondu à la plupart des demandes des éditeurs, même si, bien sûr, nous avons fait certaines choses simultanément dans l'ancien et le nouveau CMS: une mauvaise version pour l'instant et une bonne version pour plus tard.
J'écrirai bientôt un article séparé dans notre blog en détail sur la modification de "Monitor" .
Redémarrer
Après le redémarrage et la reconstruction de l'ensemble de Meduza, l'API est restée la même en format, mais elle n'était plus formée par le CMS lui-même, mais était traitée par un service distinct. Et nous avons finalement décidé de diviser l'API en plusieurs - pour différents clients.
Nous avons décidé de commencer par les applications mobiles. À ce moment-là, ils avaient déjà des questions sur la conception, l'expérience utilisateur et la vitesse de travail.Nous avons donc rangé les applications et conçu l'API comme le souhaitaient nos développeurs iOS et Android.
Avant la séparation de l'API, nous montrions la partie contenu des matériaux via WebView, de sorte que nous ne pouvions pas afficher quelque chose exclusivement dans l'application ou exclusivement sur le site - tout était affiché partout. Nous avons maintenant la possibilité de gérer le contenu de manière plus flexible: pour ne donner que ce qui est nécessaire aux applications mobiles, pour afficher les éléments lourds d'une manière différente (comme les inserts vidéo YouTube), et enfin pour faire Lazy Load, qui vous permet de charger progressivement des éléments lourds dans le matériel - images et intégrations.
Après avoir séparé les applications, nous nous sommes concentrés sur le site. À ce stade, il était déjà décidé de ne pas modifier le code du site existant, mais d'écrire tout à partir de zéro (oui, nous nous sommes impliqués dans un projet qui a duré plus d'un an à nouveau). Dans le même temps, notre directeur technique a été remplacé, et dans mon nouveau poste, je devais auditer des projets, décider de ce qui pouvait être fermé et le coordonner avec mes supérieurs. Malgré toutes les difficultés, l'équipe a décidé de terminer le nouveau site. Mais avant cela, j'ai décidé que nous avions besoin d'un autre projet.
C'est ainsi que le kit UI de Medusa est apparu.
Pour être flexible dans la diffusion du contenu, il devait être sous la forme la plus adaptée à la transformation. Les limites sémantiques des unités de contenu - images, paragraphes de texte, titres - doivent être bien alignées. Un élément de contenu ne doit pas être trop simple ou trop complexe. S'il est complexe, il a très probablement plus d'un sens. Si c'est trop simple, très probablement, lors de son utilisation, vous devrez traîner avec une logique compliquée, et lorsque vous aurez besoin de réutiliser une telle unité, vous devrez la copier dans différentes parties du code du projet.
Prenons l' exemple des jeux de Medusa.... Ils vous permettent de raconter un grand nombre d'histoires de manière ludique. Ils peuvent être réalisés spécifiquement pour l'agenda ou à la demande de l'annonceur. Ou le jeu peut être créé sur la base de ce que l'on appelle des mécanismes - des formats qui sont utilisés à plusieurs reprises (par exemple, ce sont des tests).
Jeux sur Meduza: comment nous concevons, fabriquons et réutilisons
Le code de ces jeux ne réside pas dans le code du site. Chacun d'eux est créé en tant que microservice distinct, intégré au site via un iframe et communiquant avec le site lui-même via postMessage. Et le site ne se soucie pas vraiment de ce qu'il faut montrer à l'endroit où le jeu sera. De plus, le jeu lui-même doit être visuellement inséparable du site: la typographie et les éléments d'interface doivent être les mêmes.
Quand nous avons commencé à créer des jeux, nous avons copié des styles, des boutons et d'autres choses, et bien sûr, c'était rapide et ça avait l'air bien à l'extérieur, mais c'était terrible à l'intérieur.
L'équipe a décidé que cela devrait être changé. Nous avons interrompu la correspondance du site et commencé à créer notre propre kit d'interface utilisateur - une bibliothèque qui comprenait tous les éléments et styles répétés. Nous avons essayé de ne pas manquer la longue perspective: le site, les jeux et les mécanismes - tout devait commencer à utiliser la même bibliothèque.
Tous les projets Meduza sur le Web sont écrits en React, et le kit d'interface utilisateur est un module npm qui se connecte désormais à presque tout ce que nous développons. Et le développeur, lorsqu'il a besoin de rendre quelque chose, écrit quelque chose comme ceci: blocs de rendu.
Qu'est ce que ça fait?
- Le développeur front-end ne pense pas exactement comment rendre quelque chose.
- Tout a déjà été revu par le bureau d'études.
- : UI-kit, , , . , «», .
- — - UI-kit ( ) .
UI-kit
Il existe deux groupes de composants: le contenu et le front-end. Ces derniers sont des composants React très simples tels que des boutons et des icônes.
Les composants de contenu sont un peu plus complexes, mais restent des composants React très simples avec des styles qui représentent une seule unité de contenu. Par exemple, voici à quoi ressemble un composant de paragraphe:
Un composant qui "attrape" des blocs simples
Et c'est un composant qui rend une image: l'
API à partir de laquelle le site prend des données, à l'intérieur de chaque matériau contient un tableau de composants qui sont finalement "rendus" via l'interface utilisateur ... Avec les jeux, tout fonctionne de la même manière, ils vont simplement chercher leurs données dans leurs versions de l'API.
Hourra, nous avons redémarré!
Vous trouverez ci-dessous un graphique qui montre le temps de chargement moyen des pages. C'est un indicateur très imprécis - toutes les pages de tous les appareils sont prises en compte - mais même il donne une idée de la tendance.
Le graphique montre comment la vitesse du site a changé pendant toute l'existence de Meduza. Lorsque nous avons lancé pour la première fois avec un site très simple en 2014, c'était très rapide. Mais lorsque nous avons commencé à ajouter de nouvelles fonctionnalités, la vitesse de téléchargement a chuté.
Et voici le même calendrier, mais pour les deux dernières années. Il montre comment le temps de chargement de la page a chuté après le redémarrage du site.
Puis le temps des photos est enfin arrivé.
Images
Le schéma de travail avec des images était alors. L'image est venue via l'API, où elle avait une adresse comme /images/attachments/.../random.jpg . Le fichier lui-même a été servi à partir du stockage cloud AWS via notre CDN.
Nous avons formulé les exigences du nouveau système comme suit:
- la solution doit nous permettre de changer rapidement la taille et la qualité des images envoyées
- ça ne doit pas être cher
- il doit pouvoir gérer beaucoup de trafic
Le programme que nous recherchions s'est déroulé comme ça. Le backend générerait une URL qui serait récupérée par le client - navigateur ou application. L'URL contiendrait des informations sur l'image nécessaire, sa qualité et sa taille.
Si l'image à cette URL est déjà sur le serveur Edge, elle serait immédiatement servie au client. Sinon, le serveur Edge "frapperait" sur le serveur suivant, qui aurait déjà transmis la demande au service. Ce service, ayant reçu l'URL de l'image, la décoderait et déterminerait l'adresse de l'image d'origine et la liste des opérations sur celle-ci. Après cela, le service servirait l'image transformée afin qu'elle soit stockée dans le CDN et servie sur demande.
Meduza a déjà des solutions similaires. Par exemple, nous faisons de même des images pour des extraits de nos matériaux et jeux sur les réseaux sociaux - dans ce service, nous faisons des captures d'écran de pages HTML via Headless Chrome.
Le nouveau service était censé pouvoir travailler avec des images, appliquer des effets simples, être rapide et résilient. Comme nous aimons tout écrire nous-mêmes, il était initialement prévu d'écrire un tel service dans le langage Elixir. Mais personne dans l'équipe n'a eu assez de temps, et personne n'avait certainement le désir de plonger dans le monde merveilleux des jpg, png et gif.
Nous avions encore besoin de trouver une bibliothèque qui compresserait les images. Imagemagick, avec lequel nous avions déjà de l'expérience, n'était pas la solution la plus rapide, mais au moins nous savions comment la cuisiner. Tout le reste était neuf et il y aurait beaucoup à vérifier.
Nous avions également besoin d'un support pour le format d'image webp.
Le temps a passé, nous nous sommes préparés mentalement à nous plonger dans ce projet. Mais ensuite, l'un de nos programmeurs a lu la bibliothèque imgproxy, que les gars d' Evil Martians ont téléchargé sur Open Source . Selon la description, c'était un succès parfait: Go, Libvps, une image Docker prête à l'emploi, configuration via Env. Le même jour, nous avons déployé la bibliothèque sur nos ordinateurs portables et demandé à nos DevOps de jouer avec elle aussi. Sa tâche était de mettre en place le service et d'essayer de le tuer - afin que nous comprenions quelles charges il supportera sur nos serveurs. Pendant ce temps, l'équipe backend a continué à se familiariser avec le projet sur ses ordinateurs: nous avons écrit des scripts Ruby et maîtrisé les fonctions disponibles.
Lorsque DevOps est revenu avec le verdict selon lequel la bibliothèque pouvait être utilisée en production, nous avons collecté un grand nombre d'images qui ont été transmises via imgproxy - nous étions principalement intéressés par webp - et avons pris leurs directives photo. Les employés du service technique ne peuvent pas décider eux-mêmes si cette qualité nous convient ou non. Les éditeurs de photos nous ont envoyé leurs commentaires, nous avons peaufiné quelque chose, nous nous sommes assurés que les images ne pesaient pas beaucoup et sommes allés écrire le code backend.
Ici, tout s'est avéré très simple: puisque dans la version de l'API du site, les images étaient déjà séparées par des composants de tout le reste, nous avons simplement étendu leur JSON et ajouté des adresses supplémentaires dans différentes tailles et formats.
La mise à jour est immédiatement entrée en production, car nous n'avons rien changé, mais seulement ajouté - les gars du frontend pouvaient développer des fonctions dans la même version de l'API. Ils ont élargi la composante image en termes de fonctions, ajouté des ensembles d'images pour différentes tailles et une «béquille» spéciale pour Safari. Nous avons changé le tableau des tailles plusieurs fois et vérifié le résultat à travers les yeux de la rédaction - pour cela, nous lui avons donné la version actuelle du site et un double avec de nouvelles images pour examen.
Lorsque les commentaires ont cessé d'arriver, nous avons finalement déployé, supprimé le cache et mis en production.
Production
L'essence de nos changements peut se résumer comme suit: nous avons déplacé le lieu de décision sur quelle image donner sous quelle forme au lieu où le contexte est mieux pris en compte et la mise en œuvre et l'accompagnement sont plus faciles à mettre en œuvre.
Maintenant, presque toutes les images que vous voyez sur Meduza sont le résultat du travail d'imgproxy, et dans chaque cas, elles ont des tailles différentes et parfois une qualité différente. Cela est déterminé par le contexte - que le contenu soit ouvert sur le Web, sur l'application mobile ou sur AMP - dont le service d'API qui génère les réponses connaît.