Comment avons-nous assuré la croissance de CityMobile

image

Je m’appelle Ivan, je suis responsable du développement des serveurs chez Citimobil. Aujourd'hui, je vais parler de ce qu'est ce développement de serveur, des problèmes que nous avons rencontrés et de la façon dont nous prévoyons de le développer.

Début de croissance


Peu de gens savent que CityMobile existe depuis longtemps, 13 ans. Autrefois, c'était une petite entreprise qui ne travaillait qu'à Moscou. Il y avait très peu de développeurs, et ils savaient bien comment fonctionnait le système - parce qu'ils l'avaient eux-mêmes créé. Le marché des taxis commençait alors à peine à se développer, les charges étaient penny. Nous n'avions même pas la tâche d'assurer la tolérance aux pannes et la mise à l'échelle.

En 2018, Mail.Ru Group a investi dans CityMobile, et nous avons commencé à croître rapidement. Nous n'avons pas eu le temps de réécrire la plateforme, ou du moins une refactorisation importante, il a fallu développer ses capacités fonctionnelles pour rattraper notre principal concurrent, et embaucher rapidement des personnes. Lorsque j'ai rejoint l'entreprise, seuls 20 développeurs étaient impliqués dans le backend, et presque tous étaient en procès. Par conséquent, nous avons décidé de «cueillir des fruits bas»: apporter des changements simples qui donnent un résultat énorme.

À cette époque, nous avions un monolithe en PHP et trois services sur Go, ainsi qu'une base principale dans MySQL à laquelle le monolithe accédait (les services étaient utilisés comme référentiels de Redis et EasticSearch). Et progressivement, avec l'augmentation de la charge, de lourdes demandes système ont commencé à ralentir la base.

Que pourrait-on faire avec ça?

Tout d'abord, nous avons franchi l'étape évidente: mettre un esclave en production. Mais si de nombreuses demandes lourdes lui parviennent, le tiendra-t-il? Il était également évident qu'avec une abondance de demandes de rapports analytiques, l'esclave commencerait à traîner. Un important arriéré d'esclaves pourrait nuire aux performances de l'ensemble du CityMobile. En conséquence, nous avons mis un autre esclave pour les analystes. Son décalage ne conduit jamais à des problèmes sur la prod. Mais même cela ne nous a pas semblé suffisant. Nous avons écrit un réplicateur de table de MySQL dans Clickhouse. Et aujourd'hui, l'analyse vit seule, en utilisant une pile plus conçue pour OLAP.

Sous cette forme, le système a fonctionné pendant un certain temps, puis des fonctions ont commencé à apparaître qui étaient plus exigeantes sur le matériel. Il y avait de plus en plus de demandes chaque semaine: même pas une semaine sans un nouveau record. De plus, nous avons placé une bombe à retardement sous notre système. Auparavant, nous n'avions qu'un seul point de défaillance - la base maître MySQL, mais avec l'ajout d'un esclave, il y avait deux points de ce type: le maître et l'esclave. La défaillance de l'une de ces machines entraînerait une défaillance complète du système.

Pour se protéger contre cela, nous avons commencé à utiliser un proxy local pour effectuer des esclaves de contrôle de santé. Cela nous a permis d'utiliser de nombreux esclaves sans changer le code. Nous avons introduit des contrôles automatiques réguliers du statut de chaque esclave et de ses métriques générales:

  • la;
  • décalage esclave;
  • disponibilité du port;
  • nombre de serrures, etc.

Si un certain seuil est dépassé, le système retire l'esclave de la charge. Mais en même temps, pas plus de la moitié des esclaves peuvent être retirés, de sorte qu'en raison de l'augmentation de la charge sur les autres, ils ne peuvent pas organiser eux-mêmes un temps d'arrêt. En tant que proxy, nous avons utilisé HAProxy et inclus immédiatement un plan pour passer à ProxySQL dans le backlog. Le choix était quelque peu étrange, mais nos administrateurs avaient déjà une bonne expérience de travail avec HAProxy, et le problème était aigu et nécessitait une solution rapide. Ainsi, nous avons créé un système à sécurité intégrée pour les esclaves, qui évoluait assez facilement. Malgré toute sa simplicité, elle ne nous a jamais laissé tomber.

Poursuite de la croissance


À mesure que l'entreprise se développait, nous avons trouvé un autre goulot d'étranglement dans notre système. Avec les changements des conditions extérieures - par exemple, les précipitations ont commencé dans une grande région - le nombre de commandes de taxi a augmenté rapidement. Dans de telles situations, les conducteurs n'ont pas eu le temps de réagir assez rapidement et il y avait une pénurie de voitures. Alors que les commandes étaient distribuées, elles ont créé une charge sur les esclaves MySQL en boucle.

Nous avons trouvé une solution réussie - Tarantool. Il était difficile de réécrire le système pour cela, nous avons donc résolu le problème différemment: en utilisant l'outil de réplication mysql-tarantoolfait la réplication de certaines tables de MySQL vers Tarantool. Toutes les demandes de lecture qui ont surgi lors de la pénurie de voitures, nous avons commencé à diffuser à Tarantool et depuis, nous ne sommes plus préoccupés par les orages et les ouragans! Et nous avons résolu le problème avec le point de défaillance encore plus facilement: nous avons immédiatement installé plusieurs répliques auxquelles nous accédons healthcheck via HAProxy. Chaque instance de Tarantool est répliquée par un réplicateur distinct. Comme bonus agréable, nous avons également résolu le problème du retard des esclaves dans cette section de code: la réplication de MySQL vers Tarantool fonctionne beaucoup plus rapidement que de MySQL vers MySQL.

Cependant, notre base principale était toujours un point d'échec et n'a pas évolué lors des opérations d'enregistrement. Nous avons commencé à résoudre ce problème de cette façon.

Premièrement, à cette époque, nous avons déjà commencé à créer activement de nouveaux services (par exemple, la lutte contre la fraude, à propos de laquelle mes collègues ont déjà écrit ). De plus, les services ont immédiatement exigé une évolutivité du stockage. Pour Redis, nous avons commencé à utiliser uniquement Redis-cluster, et pour Tarantool - Vshard. Là où nous utilisons MySQL, nous avons commencé à utiliser Vitess pour une nouvelle logique . Ces bases de données sont immédiatement partageables, il n'y a donc presque aucun problème d'enregistrement, et si elles surviennent soudainement, il sera facile de les résoudre en ajoutant des serveurs. Maintenant, nous utilisons Vitess uniquement pour les services non critiques et étudions les pièges, mais, à l'avenir, ce sera sur toutes les bases de données MySQL.

Deuxièmement, comme il était difficile et long d'implémenter Vitess pour la logique déjà existante, nous sommes allés de manière plus simple, quoique moins universelle: nous avons commencé à distribuer la base maître sur différents serveurs, table par table. Nous avons eu beaucoup de chance: il s'est avéré que la charge principale de l'enregistrement est créée par des tables qui ne sont pas critiques pour la fonctionnalité principale. Et lorsque nous créons de tels tableaux, nous ne créons pas de points de défaillance supplémentaires. Le principal ennemi pour nous était la forte connectivité des tables dans le code à l'aide de JOIN (il y avait des JOIN et 50 à 60 tables chacune). Nous les avons coupés sans pitié.

Il est maintenant temps de rappeler deux modèles très importants pour la conception de systèmes à haute charge:

  • Graceful degradation. , - . , , , , .. , .
  • Circuit breaker. , . , , , . ? ( - graceful degradation). , FPM- , . - ( ) , . , - , ( ).

Nous avons donc commencé à évoluer à tout le moins, mais il y avait encore des points d'échec.

Ensuite, nous avons décidé de nous tourner vers la réplication semi-synchrone (et de l'implémenter avec succès). Quelle est sa caractéristique? Si au cours de la réplication asynchrone normale, le nettoyeur du centre de données verse un seau d'eau sur le serveur, les dernières transactions n'auront pas le temps de se répliquer sur les esclaves et seront perdues. Et nous devons être sûrs que dans ce cas, nous n'aurons pas de problèmes sérieux après que l'un des esclaves deviendra un nouveau maître. En conséquence, nous avons décidé de ne pas perdre du tout les transactions, et pour cela, nous avons utilisé la réplication semi-synchrone. Désormais, les esclaves peuvent être en retard, mais même si le serveur de base de données maître est détruit, les informations sur toutes les transactions seront stockées sur au moins un esclave.

Ce fut la première étape vers le succès. La deuxième étape consistait à utiliser l'utilitaire d'orchestrateur. Nous surveillons également en permanence tous les MySQL du système. En cas d'échec de la base maître, l'automatisation créera le plus récent maître esclave (et en tenant compte de la réplication semi-synchrone, elle contiendra toutes les transactions) et y basculera toute la charge d'écriture. Nous pouvons donc maintenant revivre l'histoire d'une femme de ménage et d'un seau d'eau.

Et après?

Quand je suis arrivé à CityMobile, nous avions trois services et un monolithe. Aujourd'hui, il y en a plus de 20. Et l'essentiel qui freine notre croissance, c'est que nous n'avons toujours qu'une seule base maître. Nous combattons héroïquement cela et le divisons en bases distinctes.

Comment évoluons-nous?


Développez l'ensemble des microservices. Cela résoudra bon nombre des problèmes auxquels nous sommes confrontés aujourd'hui. Par exemple, l'équipe n'a plus une seule personne qui connaît l'appareil de l'ensemble du système. Et comme en raison de la croissance rapide, nous n'avons pas toujours de documentation à jour, et il est très difficile de la maintenir, il est difficile pour les débutants de se plonger dans le cours des choses. Et si le système comprendra de nombreux services, la rédaction de la documentation pour chacun d'eux sera incomparablement plus facile. Et la quantité de code pour une étude ponctuelle est considérablement réduite.

Faut que j'y aille.J'adore vraiment cette langue, mais j'ai toujours cru qu'il n'était pas pratique de réécrire le code de travail d'une langue à une autre. Mais récemment, l'expérience a montré que les bibliothèques PHP, même les plus courantes et les plus populaires, ne sont pas de la plus haute qualité. Autrement dit, nous corrigeons de nombreuses bibliothèques. Disons que l'équipe SRE a corrigé la bibliothèque standard pour interagir avec RabbitMQ: il s'est avéré qu'une fonction aussi basique que le time out ne fonctionnait pas. Et plus l'équipe SRE est profonde et je comprends ces problèmes, plus il devient clair que peu de gens pensent aux délais d'attente en PHP, peu de gens se soucient de tester les bibliothèques, peu de gens pensent aux verrous. Pourquoi cela devient-il un problème pour nous? Parce que les solutions Go sont beaucoup plus faciles à maintenir.

Quoi d'autre m'impressionne avec Go? Curieusement, y écrire est très simple. De plus, Go facilite la création de diverses solutions de plate-forme. Ce langage dispose d'un ensemble d'outils standard très puissant. Si pour une raison quelconque, notre backend commence à ralentir soudainement, allez simplement à une URL spécifique et vous pouvez voir toutes les statistiques - le diagramme d'allocation de mémoire, pour comprendre où le processus est inactif. Et en PHP, il est plus difficile d'identifier les problèmes de performances.

De plus, Go a de très bons linters - des programmes qui trouvent automatiquement les erreurs les plus courantes pour vous. La plupart d'entre eux sont décrits dans l'article «50 nuances de Go», et le linter les détecte parfaitement.

Continuez à tailler les bases. Nous passerons à Vitess sur tous les services.

Traduction d'un monolithe PHP en redis-cluster.Dans nos services, redis-cluster s'est révélé excellent. Malheureusement, son implémentation en PHP est plus difficile. Le monolith utilise des commandes qui ne sont pas prises en charge par redis-cluster (ce qui est bien, ces commandes apportent plus de problèmes que d'avantages).

Nous étudierons les problèmes de RabbitMQ. On pense que RabbitMQ n'est pas le logiciel le plus fiable. Nous étudierons cette question, trouverons et résoudrons des problèmes. Peut-être penserons-nous à passer à Kafka ou Tarantool.

All Articles