L'architecture du service de file d'attente de messages distribués dans Yandex.Cloud

Salut, mon nom est Vasily Bogonatov. Je fais partie de ceux qui mettent ma main et ma tête et mettent mon âme au service des files d'attente de messages persistants distribués de Yandex Message Queue. Le service a été rendu public fin mai, mais à l'intérieur de Yandex, il a longtemps été activement utilisé dans divers produits.

Aujourd'hui, je veux parler aux lecteurs de Habr des files d'attente de messages en général et de Yandex Message Queue en particulier. Tout d'abord, je veux expliquer ce qu'est une "file d'attente de messages persistants distribués" et pourquoi elle est nécessaire. Montrez sa valeur pratique, les mécanismes de travail avec les messages, parlez de l'API et de l'utilisabilité. Dans la deuxième moitié de l'article, nous examinerons le côté technique: comment la base de données Yandex est utilisée dans nos files d'attente (c'est une base fiable de notre service), à ​​quoi ressemble une approche naïve et améliorée de la construction d'une architecture, quels problèmes sont causés par la distribution et comment ils peuvent être résolus.



Qu'est-ce qu'une file d'attente de messages persistants distribués?


Wikipédia définit la file d' attente de messages comme «un composant de génie logiciel utilisé pour l'interaction interprocessus ou inter-thread dans un processus». En fait, ce concept est un peu plus large: les processus interagissant à l'aide d'une file d'attente peuvent être situés sur différents serveurs et même dans différents centres de données.

Nous allons clarifier un peu les termes.

Une file d'attente de messages est un référentiel qui fournit le placement et la lecture des données dans un ordre spécifique.

Deux types d'entités interagissent généralement avec une file d'attente:

  • écrivains (producteurs) - envoyer des messages à la file d'attente;
  • lecteurs (consommateurs) - reçoivent (lisent) des messages de la file d'attente.

Lors de l'utilisation de la file d'attente, les lecteurs et les écrivains sont indépendants les uns des autres. Ils peuvent fonctionner avec différentes performances, fiabilité, disponibilité et peuvent même être écrits dans différents langages de programmation.

Le scénario principal pour la file d'attente: transmettre de manière fiable et rapide des messages de l'écrivain au lecteur. Contrairement à la base de données, la file d'attente n'est pas destinée au stockage à long terme des messages. Dans de nombreuses implémentations populaires, il existe un paramètre correspondant - «Période de rétention des messages». Il détermine la durée de conservation du message jusqu'à sa suppression définitive.

Nous avons compris le concept de file d'attente, allez dans la "distribution" et la "persistance".

  • Dans notre cas, la distribution signifie la présence d'un cluster qui stocke et traite les données et les métadonnées de file d'attente, combinant tous ses nœuds en un seul ensemble à l'aide d'un réseau informatique.
  • La persistance implique que tous les messages de la file d'attente sont écrits sur le disque et le rédacteur ne reçoit la confirmation de l'envoi qu'après un enregistrement réussi.

La distribution et la persistance n'affectent pas les fonctionnalités principales de la file d'attente, elles offrent une tolérance aux pannes et une fiabilité du stockage des données. Quels types de pannes peuvent se produire dans notre système, nous considérerons un peu plus tard. Cependant, je ne peux pas me refuser le plaisir et ouvrir un peu les cartes: dans toute l'histoire de l'existence du service, nous n'avons pas perdu un seul message enregistré du client.

À quoi sert la file d'attente de messages?


La file d'attente vous permet de séparer les parties de services logiquement indépendantes les unes des autres, c'est-à-dire qu'elle fournit le découplage , qui est si demandé dans les microservices désormais populaires. Cela augmente l'évolutivité et la fiabilité: vous pouvez toujours augmenter le flux d'enregistrements dans la file d'attente et ajouter plus de lecteurs - gestionnaires de messages, tandis que l'échec des lecteurs n'affecte pas le travail des écrivains.

Les files d'attente atténuent les pics de charge: elles agissent comme des tampons pour les lecteurs. Si les capacités actuelles du lecteur ne sont pas suffisantes pour le traitement instantané de tous les messages entrants, les messages en file d'attente seront traités ultérieurement lorsque la charge diminuera. La mise en mémoire tampon est utile pour les services avec une charge instable, où les événements entrants instantanés ne sont pas nécessaires.

Voyons comment cela fonctionne, en utilisant l'exemple d'un robot de recherche (après tout, Yandex a commencé par une recherche!), Qui télécharge, traite et place les pages Web dans une base de données. Prenons une telle architecture.



La file d'attente de messages résout ici les problèmes suivants:

  1. Le robot fonctionne beaucoup plus rapidement que les travailleurs chargés d'analyser et de charger les pages dans la base de données. Hors ligne, les liens s'accumuleraient et rempliraient la mémoire ou le disque disponible. La même chose se produirait si les travailleurs étaient temporairement indisponibles.
  2. Sans file d'attente, le robot a besoin de «connaître» l'interface de travail des travailleurs afin de leur assigner des tâches. L'interface peut changer à mesure que le produit se développe.
  3. Un travailleur individuel a une fiabilité assez faible, il n'y a donc aucune garantie que le lien transmis sera entièrement traité par lui.

La file d'attente fournit un stockage de données fiable avec mise à l'échelle, vous permet de retarder le traitement des liens. Si un travailleur échoue, le lien brut après une certaine période sera renvoyé à la file d'attente pour être traité par un autre travailleur. La file d'attente a sa propre interface, qui est testée et décrite dans la documentation, afin que les robots de recherche et les systèmes de travail puissent développer différentes équipes dans différents langages de programmation. Cela n'affectera pas les performances globales.

Comment Yandex Message Queue fonctionne avec les messages


On distingue ici trois étapes principales:

  • écrire un message dans la file d'attente;
  • lire un message dans la file d'attente;
  • Suppression d'un message de la file d'attente.

Un enregistrement est considéré comme réussi si le message a été stocké en toute sécurité et sera bientôt disponible pour les lecteurs. L'enregistrement avec déduplication est possible: lorsqu'une tentative répétée d'enregistrer un message envoyé est ignorée.

Au moment de la lecture, le message est caché de la file d'attente pendant une période appelée délai de visibilité et devient inaccessible aux autres lecteurs. Si le délai de visibilité expire, le message revient dans la file d'attente et redevient disponible pour le traitement. L'ordre dans lequel les messages sont lus est déterminé par la file d'attente et non par le lecteur.

Le lecteur lui-même et la connexion réseau avec lui sont potentiellement peu fiables. Un délai de visibilité est nécessaire pour pouvoir renvoyer un message à la file d'attente lorsque le lecteur tombe en panne ou que la connexion est déconnectée. Sinon, il est probable qu'un seul message ne sera jamais traité correctement.

Après une lecture réussie, le message est transmis au client avec l'identifiant ReceiptHandle. Un identifiant indique des données spécifiques qui doivent être supprimées de la file d'attente de messages.

Types de files d'attente dans Yandex Message Queue


Le premier type et le plus couramment utilisé est la file d'attente standard. Il se caractérise par un débit élevé (des milliers de messages par seconde), d'excellentes performances et un temps d'exécution court des opérations de base. Les files d'attente standard sont constituées de fragments logiques et prennent en charge une mise à l'échelle de la bande passante presque linéaire.


Les files d'attente standard ne prennent pas en charge la déduplication des messages lors de l'écriture dans une file d'attente et ne garantissent pas l'ordre de lecture. En raison de l'utilisation du partage, une demande de lecture peut ne pas renvoyer un seul message, même s'ils sont dans la file d'attente. Le plus souvent, cela se produit en mode d' interrogation courte , lorsque la lecture provient d'un fragment sélectionné au hasard.

Le deuxième type - FIFO - est l'opposé de la file d'attente standard. Il fournit un ordre de lecture strict, prend en charge la déduplication lors de l'écriture et les tentatives répétées de lecture des messages. Les performances et l'évolutivité sont inférieures à la norme. Les performances de la file d'attente FIFO sont limitées à 30 requêtes par seconde. Il est recommandé d'utiliser FIFO lorsque vous devez essayer de garantir la sémantique de livraison «exactement une fois». Habituellement, le mot «file d'attente» signifie FIFO.


API Yandex Message Queue


L'API est un composant extrêmement important de tout produit. Une bonne interface logicielle doit être simple et directe, nécessitant une familiarisation minimale avec la documentation pour une utilisation efficace. Ne devrait pas permettre de faire des actions étranges ou inutiles et se protéger contre des erreurs stupides, signaler en temps opportun la violation du «contrat».

Si le système possède une telle API, il reçoit rapidement des utilisateurs fidèles et est entouré de «wrappers» pratiques pour différentes plates-formes et langages de programmation.

L'API Amazon Simple Queue Service (API AWS SQS) est un exemple d'une telle interface, testée par le temps et un grand nombre de clients. Par conséquent, nous avons décidé de ne pas inventer une interface unique pour Yandex Message Queue, mais avons implémenté la prise en charge de l'API AWS SQS, et très soigneusement.

Dans la plupart des cas, il suffit à l'utilisateur SQS de changer le point de terminaison (adresse de service), la région (pour le moment, nous n'utilisons que «ru-central1») et d'obtenir de nouvelles informations d'identification dans Yandex.Cloud. Tout le reste, par exemple, un script utilisant la ligne de commande AWS , du code utilisant le kit SDK AWS ou un service prêt à l'emploi sur Celery ou boto , est très susceptible de ne pas être touché. La logique et la fonctionnalité du service de file d'attente resteront les mêmes.


Une description détaillée des méthodes de l'API Yandex Message Queue se trouve dans la documentation du service .

Un peu de commodité


Yandex Message Queue est un service géré, c'est-à-dire Yandex. Cloud est responsable du fonctionnement des serveurs et des logiciels. L'équipe de service surveille l'intégrité des files d'attente, remplace rapidement les disques défaillants, élimine les ruptures de réseau et déploie les mises à jour. La mise à jour n'arrête pas le service: pendant que nous installons la nouvelle version de YMQ sur un groupe de serveurs, l'équilibreur de charge redirige soigneusement le trafic vers les autres. Les utilisateurs ne remarquent donc rien.

Pour faciliter le contrôle du fonctionnement des files d'attente, nous avons ajouté un grand nombre de graphiques visuels à YMQ, seule une petite partie d'entre eux est montrée ici. Les graphiques sont situés dans la console Yandex.Cloud, dans la section "Statistiques".


Nous allons vous parler des quatre graphiques les plus utiles à notre avis:

  • Le graphique «Messages en file d'attente» vous aide à surveiller l'accumulation de données dans la file d'attente. Une croissance dans le graphique peut signifier que les gestionnaires ne gèrent pas la charge ou que le traitement s'est arrêté.
  • Graphique "Âge du message le plus ancien dans la file d'attente" : des valeurs élevées indiquent des problèmes de traitement des messages. Si tout fonctionne correctement, les messages ne devraient pas rester longtemps dans la file d'attente.
  • Le graphique «Nombre de tentatives de lecture d'un message» montre quand les messages commencent à être lus plusieurs fois. Cela peut signifier que les gestionnaires se bloquent lorsqu'ils reçoivent des messages.
  • Le graphique "Temps de mise en file d'attente" indique le temps qui s'écoule entre le moment où le message est envoyé dans la file d'attente et celui où le gestionnaire le reçoit.

Les graphiques permettent d'évaluer instantanément la dynamique de la file d'attente et la présence de pannes sans avoir à consulter les journaux.

Nous avons discuté de points plus ou moins généraux, passons maintenant aux détails.

Comment Yandex Database Queue utilise la base de données Yandex


Le service Yandex Message Queue est construit au-dessus de la base de données géodistribuée Yandex Database (YDB) à tolérance de pannes , qui fournit une cohérence et une prise en charge strictes des transactions ACID. Nous ne démonterons pas maintenant son appareil et ses caractéristiques, nous nous limiterons au schéma général.


Une file d'attente dans YMQ se compose de fragments logiques, représentés par un ensemble fixe de tables YDB. Chaque table stocke sa propre information. Par exemple, il existe une table d'état générale appelée State, qui stocke les offs et le nombre réel de messages. Il y a une table avec des métadonnées de données et de messages. Il existe une table avec des attributs associés.

Toutes les opérations principales avec une file d'attente - travailler avec des messages, modifier des attributs, créer et supprimer - fonctionnent avec une hiérarchie de tables et de répertoires YDB, ou des requêtes transactionnelles sur une ou plusieurs tables d'une file d'attente. Les données à l'intérieur des tables de file d'attente sont la source de la vérité absolue. Par conséquent, en plus du fonctionnement correct et stable de la base de données, il est nécessaire d'assurer un stockage fiable et une haute disponibilité des données.

Nos informations sont stockées dans plusieurs répliques: une copie dans chacun des trois centres de données Yandex. Si l'un des centres de données n'est pas disponible, le nombre de répliques dans les autres double. Ainsi, le niveau de fiabilité requis est restauré. Même si un centre de données entier et un centre de services dans un autre tombent en panne, les données seront entièrement accessibles.

La première version de l'architecture Yandex Message Queue


La première version de l'architecture YMQ, que nous appelions nous-mêmes naïve, ressemblait à ceci.


Le diagramme montre le chemin de la demande HTTPS du client YMQ vers le référentiel YDB. Regardons les principaux composants:

  1. L'équilibreur L3 envoie une demande au centre de données Yandex le plus proche de l'utilisateur. Cela réduit la latence du réseau, bien que la charge soit répartie de manière inégale.
  2. Nginx sur la machine virtuelle Yandex.Cloud met fin aux connexions HTTPS, fournit une protection contre les attaques réseau et envoie la requête par procuration au serveur YMQ, déjà en HTTP.
  3. Le serveur HTTP YMQ implémente la logique de l'API HTTP SQS, valide et traduit la demande dans un format protobuf fortement typé.
  4. YMQ Actor system - Système d'acteur . Il a simultanément lancé des milliers d'acteurs différents échangeant des informations. Le système d'acteurs de chaque hôte fait partie d'un cluster. Tous les acteurs du cluster vivent et agissent dans leur ensemble. La logique métier YMQ est implémentée dans différents acteurs impliqués dans les demandes de transaction à YDB.
  5. Tablettes YDB ("tablettes") - partie du noyau YDB, qui est responsable de l'utilisation des tables dans les requêtes et les transactions. Les tablettes elles-mêmes ne stockent pas de données. Ce sont des structures de contrôle en mémoire qui peuvent restaurer l'état en cas de panne matérielle.
  6. Le stockage est un stockage fiable, distribué et tolérant aux pannes.

Cette architecture présente un inconvénient: tous les serveurs du cluster fonctionnent indépendamment avec des tables de la même file d'attente. Cela affecte négativement les performances et empêche l'organisation de caches fiables de messages cachés et lisibles. Il est difficile de limiter le flux de demandes, ce qui est très important pour tout service très chargé.

Architecture de Yandex Message Queue avec des maîtres de file d'attente


Le chargement de charge a montré que la première version de l'architecture résiste à environ 450 messages par seconde et par file d'attente avec un fragment. C'était très petit.
Le problème principal était les requêtes de contention. Un grand nombre de transactions logiquement conflictuelles ont rapidement amené les caches de messages cachés dans un état incohérent. Pour résoudre le problème, nous avons introduit une entité spéciale - le maître de file d'attente.


Un maître de file d'attente est un acteur qui, dans des conditions normales, existe dans un cluster dans une seule instance et passe par toutes les demandes associées à une file d'attente particulière. Si une demande à la file d'attente arrive sur un serveur où le maître souhaité est manquant, un acteur proxy spécial redirige la demande, puis traduit la réponse reçue du maître.

Lorsque vous utilisez l'Assistant de file d'attente, le cache correct des messages déverrouillés réduit les conflits lors de l'utilisation des tables. La mise en œuvre de la restriction du flux de demandes est simplifiée, par exemple, grâce au bucket Leaky . Des métriques de file d'attente rapides et précises sont disponibles: nombre de messages, trafic total, etc. Vous pouvez regrouper des demandes similaires.

En théorie, une telle architecture présente certains inconvénients liés à la centralisation:

  1. Réduction de la tolérance aux pannes: si une machine virtuelle avec un maître échoue, toutes les files d'attente avec des maîtres dessus ne seront pas disponibles. Cependant, les mécanismes spéciaux de YDB vous permettent de former de nouveaux maîtres au sein du cluster en quelques secondes. Cela résout largement le problème.
  2. Évolutivité limitée: toutes les demandes passent par un seul hôte. L'inconvénient est les tablettes YDB nivelées. Ils font tout le travail acharné avec les données. Et le maître envoie de manière asynchrone les demandes et traite les résultats reçus. Cela en fait une entité «légère» qui ne crée pas d'effet «goulot d'étranglement» lors des tests de résistance.

File d'attente de l'Assistant Requête


Les transactions distribuées avec des tables de base de données entraînent certains coûts supplémentaires, donc l'idée de réduire le nombre de requêtes nous semblait logique. Cent transactions pour l'enregistrement des messages un à la fois est préférable de se transformer en une transaction pour l'enregistrement de cent messages à la fois. Avec les maîtres de file d'attente, la mise en œuvre d'un tel traitement par lots (traitement par lots) est beaucoup plus facile.


Le traitement par lots augmente légèrement la latence pendant les opérations. Au lieu de cela, la bande passante est considérablement augmentée. Avec le traitement par lots, une file d'attente à partition unique peut traiter jusqu'à 30 000 demandes par seconde.

En général, le chargement de la file d'attente peut être très différent: des milliers de messages par seconde et plusieurs messages par jour. Nous avions besoin d'optimiser le travail avec les files d'attente à l'aide d'un algorithme flexible. Les options frontales avec l'accumulation de messages dans le tampon vers le numéro de seuil ou une réinitialisation de la minuterie ne nous convenaient pas. Par conséquent, nous avons développé un algorithme de traitement par lots adaptatif pour YMQ qui fonctionne bien dans les deux cas. Son travail est présenté sous forme de chronogramme.


Ici, lorsqu'un nouveau message arrive, l'un des trois scénarios est possible:

  1. Une transaction démarre instantanément si aucune autre transaction de ce type n'est en cours d'exécution.
  2. S'il y a déjà des transactions en cours, le message est ajouté au tampon et attend la fin des transactions.
  3. Si la taille de la mémoire tampon dépasse la valeur seuil, une autre transaction parallèle est lancée. Le nombre de transactions simultanées est limité.

L'idée du batch adaptatif ressemble à l'algorithme Nagle pour TCP / IP. : , latency . , . .


Yandex Message Queue, , . , , -.

YDB . YMQ .

, , , .


YMQ . . «» .


YDB . , , , «». , . .


: . , «» . -, «» , «», .

« » . , , .

Yandex Message Queue


Yandex Message Queue – - . . , . .

  • - , , . .
  • API , . , .
  • , : , . , . . boto, 24/7, - .
  • , , . .

. - . . .

:

  • 5, ;
  • YDB;
  • , , , ;
  • , ;
  • . .

.

En conclusion


– , , , . ., ., ., . .

. . , Yandex Message Queue .

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


All Articles