Comment passer de 1 Ă  100 000 utilisateurs

De nombreuses startups sont passées par là: des foules de nouveaux utilisateurs sont enregistrées chaque jour et l'équipe de développement peine à prendre en charge le service.

C'est un problÚme agréable, mais il y a peu d'informations claires sur le Web pour savoir comment faire évoluer avec précision une application Web de zéro à des centaines de milliers d'utilisateurs. Habituellement, il existe des solutions d'incendie ou l'élimination des goulots d'étranglement (et souvent les deux). Par conséquent, les gens utilisent des astuces assez stéréotypées pour transformer leur projet amateur en quelque chose de vraiment sérieux.

Essayons de filtrer les informations et d'Ă©crire la formule principale. Nous allons progressivement mettre Ă  l'Ă©chelle notre nouveau site de partage de photos Graminsta de 1 Ă  100 000 utilisateurs.

Nous noterons les actions spécifiques à entreprendre pour augmenter l'audience à 10, 100, 1000, 10 000 et 100 000 personnes.

1 utilisateur: 1 voiture


Presque chaque application, qu’il s’agisse d’un site Web ou d’une application mobile, comporte trois Ă©lĂ©ments clĂ©s:

  • API
  • base de donnĂ©es
  • client (application mobile ou site web lui-mĂȘme)

La base de données stocke des données persistantes. L'API sert les demandes pour et autour de ces données. Le client transfÚre les données à l'utilisateur.

J'en suis venu à la conclusion qu'il est beaucoup plus facile de parler de mise à l'échelle d'une application si, du point de vue de l'architecture, les entités clientes et les API sont complÚtement séparées.

Lorsque nous commençons Ă  crĂ©er une application, les trois composants peuvent ĂȘtre exĂ©cutĂ©s sur le mĂȘme serveur. D'une certaine maniĂšre, cela nous rappelle notre environnement de dĂ©veloppement: un ingĂ©nieur exĂ©cute la base de donnĂ©es, l'API et le client sur le mĂȘme ordinateur.

Théoriquement, nous pourrions le déployer dans le cloud sur une instance de DigitalOcean Droplet ou AWS EC2, comme indiqué ci-dessous:

Cela dit, si le site a plus d'un utilisateur, il est presque toujours logique de mettre en évidence le niveau de la base de données.

10 utilisateurs: prendre la base de données à un niveau distinct


La division d'une base de donnĂ©es en services gĂ©rĂ©s tels qu'Amazon RDS ou la base de donnĂ©es gĂ©rĂ©e par l'ocĂ©an numĂ©rique nous sera utile pendant longtemps. C'est un peu plus cher que l'auto-hĂ©bergement sur une seule machine ou une instance EC2, mais avec ces services, vous obtenez de nombreuses extensions utiles prĂȘtes Ă  l'emploi qui vous seront utiles Ă  l'avenir: sauvegardes multi-rĂ©gions, rĂ©pliques en lecture, sauvegardes automatiques et bien plus encore.

Voici Ă  quoi ressemble maintenant le systĂšme:

100 utilisateurs: amener le client Ă  un niveau distinct


Heureusement, notre application a vraiment aimé les premiers utilisateurs. Le trafic devient plus stable, il est donc temps de déplacer le client vers un niveau distinct. Il convient de noter que la séparation d' entités est un aspect clé de la construction d'une application évolutive. Puisqu'une partie du systÚme reçoit plus de trafic, nous pouvons le diviser de maniÚre à contrÎler la mise à l'échelle du service en fonction de modÚles de trafic spécifiques.

C'est pourquoi j'aime reprĂ©senter le client sĂ©parĂ©ment de l'API. Il est donc trĂšs facile de parler de dĂ©veloppement pour plusieurs plates-formes: Web, Web mobile, iOS, Android, applications de bureau, services tiers, etc. Tous ne sont que des clients utilisant la mĂȘme API.

Par exemple, maintenant, nos utilisateurs demandent le plus souvent de publier une application mobile. La séparation des entités clientes et des API facilite la tùche.

Voici Ă  quoi ressemble le systĂšme:



1000 utilisateurs: ajouter un Ă©quilibreur de charge


Ca va bien. Les utilisateurs de Graminsta téléchargent de plus en plus de photos. Le nombre d'inscriptions augmente également. Notre seul serveur API a du mal à gérer tout le trafic. Besoin de plus de fer!

L'Ă©quilibreur de charge est un concept trĂšs puissant. L'idĂ©e clĂ© est de placer l'Ă©quilibreur devant l'API et de rĂ©partir le trafic entre les instances de service individuelles. C'est la façon de faire Ă©voluer horizontalement, c'est-Ă -dire que nous ajoutons plus de serveurs avec le mĂȘme code, augmentant le nombre de requĂȘtes que nous pouvons traiter.

Nous allons placer des équilibreurs de charge séparés devant le client Web et devant l'API. Cela signifie que vous pouvez exécuter plusieurs instances qui exécutent le code API et le code client Web. L'équilibreur de charge transmet les demandes au serveur le moins chargé.

Ici, nous obtenons un autre avantage important - la redondance. Lorsqu'une instance Ă©choue (peut-ĂȘtre des surcharges ou des plantages), nous en avons encore d'autres qui rĂ©pondent toujours aux demandes entrantes. Si une seule instance fonctionnait, en cas d'Ă©chec, tout le systĂšme tomberait.

L'équilibreur de charge fournit également une mise à l'échelle automatique. Nous pouvons le configurer pour augmenter le nombre d'instances avant la charge de pointe et réduire lorsque tous les utilisateurs dorment.

Avec un Ă©quilibreur de charge, le niveau de l'API peut ĂȘtre mis Ă  l'Ă©chelle Ă  l'infini, nous ajoutons simplement de nouvelles instances Ă  mesure que le nombre de demandes augmente.


. , PaaS, Heroku Elastic Beanstalk AWS ( ). Heroku , - API. , Heroku — .

10 000 : CDN


Cela aurait peut-ĂȘtre dĂ» ĂȘtre fait dĂšs le dĂ©but. Le traitement des demandes et la prise de nouvelles photos commencent Ă  charger trop nos serveurs.

À ce stade, vous devez utiliser un service cloud pour stocker du contenu statique - images, vidĂ©os et bien plus encore (AWS S3 ou Digital Ocean Spaces). En gĂ©nĂ©ral, notre API doit Ă©viter de traiter des choses comme le tĂ©lĂ©chargement d'images et le tĂ©lĂ©chargement d'images sur un serveur.

Un autre avantage de l'hébergement cloud est son CDN (dans AWS, ce module complémentaire est appelé Cloudfront, mais de nombreux services de stockage cloud le proposent immédiatement). CDN met automatiquement nos images en cache dans divers centres de données du monde entier.

Bien que notre centre de donnĂ©es principal puisse ĂȘtre situĂ© dans l'Ohio, mais si quelqu'un demande une image au Japon, le fournisseur de cloud fera une copie et l'enregistrera dans son centre de donnĂ©es japonais. La prochaine personne Ă  demander cette image au Japon la recevra beaucoup plus rapidement. Ceci est important lorsque nous travaillons avec des fichiers volumineux, comme des photos ou des vidĂ©os qui prennent beaucoup de temps Ă  tĂ©lĂ©charger et Ă  transmettre Ă  travers la planĂšte entiĂšre.



100 000 utilisateurs: mise à l'échelle du niveau de données


CDN a vraiment aidĂ©: le trafic augmente Ă  pleine vitesse. La cĂ©lĂšbre blogueuse vidĂ©o, Maid Mobrick, vient de s'inscrire chez nous et a postĂ© son histoire, comme on dit. GrĂące Ă  l'Ă©quilibreur de charge, le niveau d'utilisation du CPU et de la mĂ©moire sur les serveurs d'API est maintenu bas (dix instances d'API sont en cours d'exĂ©cution), mais nous commençons Ă  obtenir de nombreux dĂ©lais d'attente pour les demandes ... d'oĂč viennent ces retards?

AprÚs un peu de fouille dans les métriques, nous voyons que le CPU sur le serveur de base de données est chargé à 80-90%. Nous sommes à la limite.

La mise à l'échelle de la couche de données est probablement la partie la plus difficile de l'équation. Les serveurs d'API servent des demandes sans état, nous ajoutons donc simplement plus d'instances d'API. Mais avec la plupartles bases de données échouent. Nous discuterons des systÚmes de gestion de bases de données relationnelles populaires (PostgreSQL, MySQL, etc.).

Mise en cache


L'une des façons les plus simples d'augmenter les performances de notre base de données est d'introduire un nouveau composant: le niveau de cache. La méthode de mise en cache la plus courante consiste à stocker des enregistrements de valeurs-clés dans la RAM, tels que Redis ou Memcached. La plupart des clouds ont une version gérée de ces services: Elasticache sur AWS et Memorystore sur Google Cloud.

Le cache est utile lorsqu'un service effectue de nombreux appels rĂ©pĂ©tĂ©s Ă  la base de donnĂ©es pour obtenir les mĂȘmes informations. En fait, nous accĂ©dons Ă  la base de donnĂ©es une seule fois, enregistrons les informations dans le cache - et n'y touchons plus.

Par exemple, dans notre service Graminsta, chaque fois que quelqu'un accĂšde Ă  la page de profil de l'Ă©toile Mobric, le serveur API demande des informations Ă  son profil dans la base de donnĂ©es. Cela arrive encore et encore. Étant donnĂ© que les informations de profil de Mobrick ne changent pas Ă  chaque demande, elles sont idĂ©ales pour la mise en cache.

Nous mettrons en cache les rĂ©sultats de la base de donnĂ©es dans Redis par clĂ© user:idavec une pĂ©riode de validitĂ© de 30 secondes. Maintenant, quand quelqu'un entre dans le profil de Mobrick, nous vĂ©rifions d'abord Redis, et si les donnĂ©es sont lĂ , nous les transfĂ©rons simplement directement de Redis. Maintenant, les requĂȘtes sur le profil le plus populaire sur le site ne chargent pratiquement pas notre base de donnĂ©es.

Un autre avantage de la plupart des services de mise en cache est qu'ils sont plus faciles à faire évoluer que les serveurs de base de données. Redis dispose d'un mode de cluster Redis Cluster intégré. Comme un équilibreur de charge1, il vous permet de distribuer le cache Redis sur plusieurs machines (sur des milliers de serveurs, si nécessaire).

Presque toutes les applications à grande échelle utilisent la mise en cache; cela fait partie intégrante de l'API rapide. Traitement des demandes plus rapide et code plus productif - tout cela est important, mais sans cache, il est presque impossible d'adapter le service à des millions d'utilisateurs.

Lecture de répliques


Lorsque le nombre de requĂȘtes vers la base de donnĂ©es a considĂ©rablement augmentĂ©, nous pouvons faire une chose de plus: ajouter des rĂ©pliques en lecture dans le systĂšme de gestion de base de donnĂ©es. En utilisant les services gĂ©rĂ©s dĂ©crits ci-dessus, cela peut ĂȘtre fait en un seul clic. La rĂ©plique en lecture restera pertinente dans la base de donnĂ©es principale et est disponible pour les instructions SELECT.

Voici notre systĂšme maintenant:



Actions supplémentaires


Au fur et Ă  mesure que l'application Ă©volue, nous continuerons de sĂ©parer les services pour les adapter indĂ©pendamment. Par exemple, si nous commençons Ă  utiliser Websockets, il est logique d'extraire le code de traitement Websockets dans un service distinct. Nous pouvons le placer sur de nouvelles instances derriĂšre notre propre Ă©quilibreur de charge, qui peut Ă©voluer vers le haut et vers le bas en fonction des connexions Websockets ouvertes et quel que soit le nombre de requĂȘtes HTTP.

Nous continuons également à lutter contre les restrictions au niveau de la base de données. C'est à ce stade que le moment est venu d'étudier le partitionnement et le sharding de la base de données. Les deux approches nécessitent des frais supplémentaires, mais elles vous permettent de faire évoluer la base de données presque à l'infini.

Nous voulons Ă©galement installer un service de surveillance et d'analyse comme New Relic ou Datadog. Cela permettra d'identifier les requĂȘtes lentes et de comprendre oĂč des amĂ©liorations sont nĂ©cessaires. À mesure que nous Ă©voluons, nous voulons nous concentrer sur la recherche de goulots d'Ă©tranglement et leur rĂ©solution - en utilisant souvent certaines idĂ©es des sections prĂ©cĂ©dentes.

Sources


Ce message est inspirĂ© de l'un de mes messages prĂ©fĂ©rĂ©s Ă  haute Ă©volutivitĂ© . Je voulais concrĂ©tiser un peu l'article pour les premiĂšres Ă©tapes des projets et le dĂ©tacher d'un fournisseur. N'oubliez pas de lire si vous ĂȘtes intĂ©ressĂ© par ce sujet.

Notes de bas de page


  1. Malgré les similitudes en termes d'équilibrage de charge sur plusieurs instances, l'implémentation de base du cluster Redis est trÚs différente de l'équilibreur de charge. [rendre]



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


All Articles