Comment passer de 1 à 100 000 utilisateurs

De nombreuses startups ont vécu cela : des foules de nouveaux utilisateurs s'inscrivent chaque jour et l'équipe de développement a du mal à faire fonctionner le service.

C'est un problème intéressant, mais il existe peu d'informations claires sur le Web sur la façon de faire évoluer soigneusement une application Web de rien à des centaines de milliers d'utilisateurs. Il existe généralement soit des solutions anti-incendie, soit des solutions aux goulots d'étranglement (et souvent les deux). Par conséquent, les gens utilisent des techniques plutôt clichées pour transformer leur projet amateur en quelque chose de vraiment sérieux.

Essayons de filtrer les informations et d'écrire la formule de base. Nous allons faire évoluer notre nouveau site de partage de photos Graminsta étape par étape de 1 à 100 000 utilisateurs.

Écrivons quelles actions spécifiques doivent être entreprises lorsque l'audience passe à 10, 100, 1000 10, 000 100 et 000 XNUMX personnes.

1 utilisateur : 1 machine

Presque toutes les applications, qu'il s'agisse d'un site Web ou d'une application mobile, comportent trois éléments clés :

  • API
  • base de données
  • client (application mobile elle-même ou site Web)

La base de données stocke les données persistantes. L'API répond aux requêtes vers et autour de ces données. Le client transmet des données à l'utilisateur.

Je suis arrivé à la conclusion qu'il est beaucoup plus facile de parler de mise à l'échelle d'une application si, d'un point de vue architectural, les entités client et 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 ressemble à notre environnement de développement : un ingénieur exécute la base de données, l'API et le client sur la même machine.

En théorie, nous pourrions le déployer dans le cloud sur une seule instance DigitalOcean Droplet ou AWS EC2, comme indiqué ci-dessous :
Comment passer de 1 à 100 000 utilisateurs
Cela dit, s’il y a plus d’un utilisateur sur un site, il est presque toujours logique de dédier une couche de base de données.

10 utilisateurs : déplacer la base de données vers un niveau distinct

Diviser la base de données en services gérés comme Amazon RDS ou Digital Ocean Managed Database nous servira 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 : sauvegarde multi-régions, réplicas en lecture, sauvegarde automatique. sauvegardes, et plus encore.

Voici à quoi ressemble le système maintenant :
Comment passer de 1 à 100 000 utilisateurs

100 utilisateurs : déplacer le client vers un niveau distinct

Heureusement, nos premiers utilisateurs ont beaucoup aimé notre application. Le trafic devient plus stable, il est donc temps de déplacer le client vers un niveau distinct. Il convient de noter que séparation Les entités sont un aspect clé de la création d’une application évolutive. Lorsqu'une partie du système reçoit davantage de trafic, nous pouvons la partitionner pour contrôler la façon dont le service évolue en fonction de modèles de trafic spécifiques.

C'est pourquoi j'aime considérer le client comme distinct de l'API. Cela permet de penser très facilement au développement pour plusieurs plateformes : web, web mobile, iOS, Android, applications de bureau, services tiers, etc. Ce ne sont que des clients utilisant la même API.

Par exemple, nos utilisateurs demandent désormais le plus souvent de publier une application mobile. Si vous séparez les entités client et API, cela devient plus facile.

Voici à quoi ressemble un tel système :

Comment passer de 1 à 100 000 utilisateurs

1000 utilisateurs : ajouter un équilibreur de charge

Les choses s'améliorent. Les utilisateurs de Graminsta téléchargent de plus en plus de photos. Le nombre d'inscriptions est également en augmentation. Notre seul serveur API a du mal à suivre tout le trafic. Il me faut plus de fer !

L'équilibreur de charge est un concept très puissant. L'idée clé est que nous plaçons un équilibreur de charge devant l'API et qu'il distribue le trafic aux instances de service individuelles. C'est ainsi que nous évoluons horizontalement, ce qui signifie que nous ajoutons plus de serveurs avec le même code, augmentant ainsi le nombre de requêtes que nous pouvons traiter.

Nous allons placer des équilibreurs de charge distincts devant le client web et devant l'API. Cela signifie que vous pouvez exécuter plusieurs instances exécutant le code API et le code client Web. L'équilibreur de charge dirigera les requêtes vers le serveur le moins chargé.

Ici, nous obtenons un autre avantage important : la redondance. Lorsqu'une instance échoue (peut-être surchargée ou en panne), nous nous retrouvons avec d'autres qui continuent de répondre aux demandes entrantes. S’il n’y avait qu’une seule instance qui fonctionnait, en cas de panne, tout le système tomberait en panne.

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

Avec un équilibreur de charge, le niveau de l'API peut être adapté presque indéfiniment, en ajoutant simplement de nouvelles instances à mesure que le nombre de requêtes augmente.

Comment passer de 1 à 100 000 utilisateurs

Note. À l'heure actuelle, notre système est très similaire à ce que proposent des sociétés PaaS comme Heroku ou Elastic Beanstalk sur AWS (c'est pourquoi elles sont si populaires). Heroku place la base de données sur un hôte distinct, gère un équilibreur de charge à mise à l'échelle automatique et vous permet d'héberger le client Web séparément de l'API. C'est une excellente raison d'utiliser Heroku pour des projets en phase de démarrage ou des startups : vous obtenez tous les services de base prêts à l'emploi.

10 000 utilisateurs : CDN

Peut-être aurions-nous dû le faire dès le début. Le traitement des demandes et l'acceptation de nouvelles photos commencent à mettre trop de pression sur 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 devrait éviter de gérer des choses comme la diffusion d'images et le téléchargement d'images sur le serveur.

Un autre avantage de l'hébergement cloud est le CDN (AWS appelle ce module complémentaire Cloudfront, mais de nombreux fournisseurs de stockage cloud le proposent directement). Le CDN met automatiquement en cache nos images dans divers centres de données à travers le monde.

Bien que notre centre de données principal soit situé dans l'Ohio, si quelqu'un demande une image au Japon, le fournisseur de cloud en fera une copie et la stockera dans son centre de données japonais. La prochaine personne qui demandera 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.

Comment passer de 1 à 100 000 utilisateurs

100 000 utilisateurs : faire évoluer la couche de données

CDN a beaucoup aidé : le trafic croît à toute vitesse. Le célèbre blogueur vidéo Mavid Mobrick vient de s'inscrire chez nous et de publier son « histoire », comme on dit. Grâce à l'équilibreur de charge, l'utilisation du CPU et de la mémoire sur les serveurs API reste faible (dix instances API en cours d'exécution), mais nous commençons à avoir beaucoup de timeouts sur les requêtes... d'où viennent ces retards ?

En creusant un peu dans les métriques, nous constatons que le processeur du 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 API traitent les requêtes sans état, nous ajoutons donc simplement plus d'instances API. Nez à la majorité les bases de données ne peuvent pas faire cela. Nous parlerons des systèmes de gestion de bases de données relationnelles populaires (PostgreSQL, MySQL, etc.).

mise en cache

L'un des moyens les plus simples d'augmenter les performances de notre base de données est d'introduire un nouveau composant : la couche de cache. La méthode de mise en cache la plus courante est un magasin d'enregistrements clé-valeur en mémoire, tel que Redis ou Memcached. La plupart des cloud disposent d'une version gérée de ces services : Elasticache sur AWS et Memorystore sur Google Cloud.

Un cache est utile lorsqu'un service effectue de nombreux appels répétés à la base de données pour récupérer les mêmes informations. Essentiellement, nous accédons à la base de données une seule fois, stockons 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 la star Mobrik, le serveur API interroge la base de données pour obtenir des informations sur son profil. Cela arrive encore et encore. Étant donné que les informations de profil de Mobrik ne changent pas à chaque demande, elles sont excellentes pour la mise en cache.

Nous mettrons en cache les résultats de la base de données dans Redis par clé user:id avec une durée de validité de 30 secondes. Désormais, lorsque quelqu'un accède au profil de Mobrik, nous vérifions d'abord Redis, et si les données sont là, nous les transférons simplement directement depuis Redis. Désormais, les demandes adressées au profil le plus populaire du 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 à mettre à l’échelle que les serveurs de bases de données. Redis dispose d'un mode Redis Cluster intégré. Semblable à un équilibreur de charge1, il vous permet de distribuer votre cache Redis sur plusieurs machines (sur des milliers de serveurs si nécessaire).

Presque toutes les applications à grande échelle utilisent la mise en cache ; elle fait absolument partie intégrante d'une API rapide. Un traitement des requêtes plus rapide et un code plus productif sont tous importants, mais sans cache, il est presque impossible de faire évoluer un service vers des millions d'utilisateurs.

Lire les répliques

Lorsque le nombre de requêtes sur la base de données a considérablement augmenté, une autre chose que nous pouvons faire est d'ajouter des réplicas en lecture dans le système de gestion de base de données. Avec les services gérés décrits ci-dessus, cela peut se faire en un clic. Le réplica en lecture restera à jour dans la base de données principale et est disponible pour les instructions SELECT.

Voici notre système maintenant :

Comment passer de 1 à 100 000 utilisateurs

Prochaines étapes

À mesure que l’application continue d’évoluer, nous continuerons à séparer les services pour les faire évoluer indépendamment. Par exemple, si nous commençons à utiliser Websockets, il est alors logique d'extraire le code de traitement des Websockets dans un service distinct. Nous pouvons le placer sur de nouvelles instances derrière notre propre équilibreur de charge, qui peut évoluer en fonction des connexions Websockets ouvertes et quel que soit le nombre de requêtes HTTP.

Nous continuerons également à lutter contre les restrictions au niveau des bases de données. C’est à ce stade qu’il est temps d’étudier le partitionnement et le partitionnement des bases de données. Les deux approches nécessitent une surcharge supplémentaire, mais vous permettent de faire évoluer la base de données presque indéfiniment.

Nous souhaitons également installer un service de surveillance et d'analyse comme New Relic ou Datadog. Cela vous aidera à identifier les requêtes lentes et à comprendre où des améliorations sont nécessaires. À mesure que nous progressons, nous souhaitons nous concentrer sur la recherche des goulots d’étranglement et sur leur élimination, en utilisant souvent certaines des idées des sections précédentes.

sources

Cet article est inspiré par l'un des mes articles préférés sur la haute évolutivité. Je voulais rendre l'article un peu plus spécifique pour les étapes initiales des projets et le détacher d'un seul fournisseur. Assurez-vous de lire si ce sujet vous intéresse.

Notes de bas de page

  1. Bien que similaire en termes de répartition de charge sur plusieurs instances, l'implémentation sous-jacente d'un cluster Redis est très différente de celle d'un équilibreur de charge. [retour]

Comment passer de 1 à 100 000 utilisateurs

Source: habr.com

Ajouter un commentaire