Éliminer les pannes de traitement des tâches en remplaçant RabbitMQ par Apache Kafka Sans Temps d’arrêt

La mise à l’échelle de l’infrastructure backend pour gérer l’hyper-croissance est l’un des nombreux défis passionnants de travailler chez DoorDash. À la mi-2019, nous avons été confrontés à d’importants défis de mise à l’échelle et à des pannes fréquentes impliquant Celery et RabbitMQ, deux technologies alimentant le système qui gère le travail asynchrone permettant des fonctionnalités critiques de notre plate-forme, y compris le paiement des commandes et les affectations Dasher.

Nous avons rapidement résolu ce problème avec un système de traitement de tâches asynchrone simple basé sur Apache Kafka qui a arrêté nos pannes pendant que nous continuions à itérer sur une solution robuste. Notre version initiale a implémenté le plus petit ensemble de fonctionnalités nécessaires pour accueillir une grande partie des tâches Céleri existantes. Une fois en production, nous avons continué à prendre en charge davantage de fonctionnalités de Céleri tout en abordant les nouveaux problèmes rencontrés lors de l’utilisation de Kafka.

Les problèmes que nous avons rencontrés avec Celery et RabbitMQ

RabbitMQ et Celery étaient des éléments essentiels de notre infrastructure qui ont alimenté plus de 900 tâches asynchrones différentes chez DoorDash, y compris le paiement des commandes, la transmission des commandes marchandes et le traitement de la localisation Dasher. Le problème rencontré par DoorDash était que RabbitMQ descendait fréquemment en raison d’une charge excessive. Si le traitement des tâches était interrompu, DoorDash était effectivement en panne et les commandes ne pouvaient pas être terminées, ce qui entraînait une perte de revenus pour nos marchands et nos Dashers, et une mauvaise expérience pour nos consommateurs. Nous avons rencontré des problèmes sur les fronts suivants:

  • Disponibilité: Pannes causées par une disponibilité réduite de la demande.
  • Évolutivité: RabbitMQ n’a pas pu évoluer avec la croissance de notre entreprise.
  • Observabilité: RabbitMQ offrait des métriques limitées et les travailleurs de Céleri étaient opaques.
  • Efficacité opérationnelle : Le redémarrage de ces composants était un processus manuel fastidieux.

Pourquoi notre système de traitement des tâches asynchrone n’était pas hautement disponible

Ce plus gros problème auquel nous avons été confrontés était des pannes, et elles arrivaient souvent lorsque la demande était à son apogée. RabbitMQ baisserait en raison de la charge, du désabonnement excessif des connexions et d’autres raisons. Les commandes seraient interrompues et nous devions redémarrer notre système ou parfois même mettre en place un tout nouveau courtier et un basculement manuel afin de nous remettre de la panne.

En approfondissant les problèmes de disponibilité, nous avons trouvé les sous-problèmes suivants:

  • Le céleri permet aux utilisateurs de planifier des tâches à l’avenir avec un compte à rebours ou une ETA. Notre utilisation intensive de ces comptes à rebours a entraîné une augmentation notable de la charge sur le courtier. Certaines de nos pannes étaient directement liées à une augmentation des tâches avec compte à rebours. Nous avons finalement décidé de restreindre l’utilisation des comptes à rebours au profit d’un autre système que nous avions en place pour planifier les travaux à l’avenir.
  • Des rafales soudaines de trafic laisseraient RabbitMQ dans un état dégradé où la consommation de tâches était significativement plus faible que prévu. D’après notre expérience, cela ne pouvait être résolu qu’avec un rebond RabbitMQ. RabbitMQ a un concept de contrôle de flux où il réduira la vitesse des connexions qui publient trop rapidement afin que les files d’attente puissent suivre. Le contrôle du flux était souvent, mais pas toujours, impliqué dans ces dégradations de disponibilité. Lorsque le contrôle de flux entre en jeu, les éditeurs le considèrent effectivement comme une latence réseau. La latence du réseau réduit nos temps de réponse ; si la latence augmente pendant les pics de trafic, des ralentissements importants peuvent entraîner une cascade lorsque les demandes s’accumulent en amont.
  • Nos web workers python uWSGI avaient une fonctionnalité appelée harakiri qui était activée pour tuer tous les processus qui dépassaient un délai d’expiration. Lors de pannes ou de ralentissements, harakiri a entraîné une perte de connexion aux courtiers RabbitMQ, car les processus ont été tués et redémarrés à plusieurs reprises. Avec des milliers de travailleurs Web en cours d’exécution à un moment donné, toute lenteur qui déclencherait harakiri contribuerait à son tour encore plus à la lenteur en ajoutant une charge supplémentaire à RabbitMQ.
  • En production, nous avons connu plusieurs cas où le traitement des tâches chez les consommateurs de céleri s’est arrêté, même en l’absence de charge importante. Nos efforts d’enquête n’ont donné aucune preuve de contraintes de ressources qui auraient interrompu le traitement, et les travailleurs ont repris le traitement une fois qu’ils ont été renvoyés. Ce problème n’a jamais été causé par la racine, bien que nous soupçonnions un problème chez les travailleurs du Céleri eux-mêmes et non chez RabbitMQ.

Dans l’ensemble, tous ces problèmes de disponibilité étaient inacceptables pour nous, car une fiabilité élevée est l’une de nos plus grandes priorités. Étant donné que ces pannes nous coûtaient beaucoup en termes de commandes manquées et de crédibilité, nous avions besoin d’une solution qui résoudrait ces problèmes dès que possible.

Pourquoi notre solution héritée n’a pas mis à l’échelle

Le problème suivant était l’échelle. DoorDash se développe rapidement et nous atteignons rapidement les limites de notre solution existante. Nous devions trouver quelque chose qui suivrait notre croissance continue car notre solution héritée rencontrait les problèmes suivants :

Atteindre la limite de mise à l’échelle verticale

Nous utilisions la plus grande solution RabbitMQ à nœud unique disponible. Il n’y avait pas de chemin pour évoluer verticalement plus loin et nous commencions déjà à pousser ce nœud à ses limites.

Le mode Haute disponibilité a limité notre capacité

En raison de la réplication, le mode Haute disponibilité primaire-secondaire (HA) a réduit le débit par rapport à l’option à nœud unique, nous laissant encore moins de marge de manœuvre que la solution à nœud unique. Nous ne pouvions pas nous permettre d’échanger le débit contre la disponibilité.

Deuxièmement, le mode HA primaire-secondaire n’a pas, en pratique, réduit la gravité de nos pannes. Les basculements prenaient plus de 20 minutes et étaient souvent bloqués nécessitant une intervention manuelle. Les messages étaient souvent perdus dans le processus.

Nous étions rapidement à court d’espace libre alors que DoorDash continuait de croître et de pousser le traitement de nos tâches à ses limites. Nous avions besoin d’une solution qui puisse évoluer horizontalement à mesure que nos besoins de traitement augmentaient.

Comment Celery et RabbitMQ offraient une observabilité limitée

Savoir ce qui se passe dans n’importe quel système est fondamental pour assurer sa disponibilité, son évolutivité et son intégrité opérationnelle.

En naviguant sur les problèmes décrits ci-dessus, nous avons remarqué que:

  • Nous étions limités à un petit ensemble de métriques RabbitMQ à notre disposition.
  • Nous avions une visibilité limitée sur les travailleurs du céleri eux-mêmes.

Nous devions être en mesure de voir les métriques en temps réel de tous les aspects de notre système, ce qui signifiait que les limitations d’observabilité devaient également être abordées.

Les défis de l’efficacité opérationnelle

Nous avons également rencontré plusieurs problèmes avec l’exploitation de RabbitMQ :

  • Nous avons souvent dû basculer notre nœud RabbitMQ vers un nouveau pour résoudre la dégradation persistante que nous avons observée. Cette opération était manuelle et prenait beaucoup de temps pour les ingénieurs impliqués et devait souvent être effectuée tard dans la nuit, en dehors des heures de pointe.
  • Il n’y avait aucun expert interne en Céleri ou en RabbitMQ chez DoorDash sur lequel nous pouvions nous appuyer pour élaborer une stratégie de mise à l’échelle de cette technologie.

Le temps d’ingénierie consacré à l’exploitation et à la maintenance de RabbitMQ n’était pas durable. Nous avions besoin de quelque chose qui réponde mieux à nos besoins actuels et futurs.

Solutions potentielles à nos problèmes avec le Céleri et RabbitMQ

Avec les problèmes décrits ci-dessus, nous avons envisagé les solutions suivantes:

  • Changez le courtier de céleri de RabbitMQ en Redis ou Kafka. Cela nous permettrait de continuer à utiliser Celery, avec une banque de données de support différente et potentiellement plus fiable.
  • Ajoutez une prise en charge multi-courtiers à notre application Django afin que les consommateurs puissent publier sur N courtiers différents en fonction de la logique souhaitée. Le traitement des tâches sera réparti entre plusieurs courtiers, de sorte que chaque courtier subira une fraction de la charge initiale.
  • Mise à niveau vers des versions plus récentes de Celery et RabbitMQ. Les versions plus récentes de Celery et RabbitMQ devaient résoudre les problèmes de fiabilité, nous faisant gagner du temps car nous extrayions déjà des composants de notre monolithe Django en parallèle.
  • Migrez vers une solution personnalisée soutenue par Kafka. Cette solution demande plus d’efforts que les autres options que nous avons énumérées, mais a également plus de potentiel pour résoudre tous les problèmes que nous avions avec la solution héritée.

Chaque option a ses avantages et ses inconvénients:

Option Avantages Inconvénients
Redis en tant que courtier
  • Disponibilité améliorée avec le support ElasticCache et multi-AZ
  • Courtier amélioré observabilité avec ElasticCache en tant que courtier
  • Amélioration de l’efficacité opérationnelle
  • Expérience et expertise opérationnelles internes avec Redis
  • Un échange de courtier est direct en tant qu’option prise en charge dans Celery
  • Le désabonnement de la connexion Harakiri ne dégrade pas significativement les performances de Redis
  • Incompatible avec le mode en cluster Redis
  • Le nœud unique Redis ne s’adapte pas horizontalement
  • Aucune amélioration de l’observabilité du Céleri
  • Cette solution ne résout pas le problème observé lorsque les travailleurs du Céleri ont cessé de traiter les tâches
Kafka en tant que courtier
  • Kafka peut être hautement disponible
  • Kafka est évolutif horizontalement
  • Amélioration de l’observabilité avec Kafka en tant que broker
  • Amélioration de l’efficacité opérationnelle
  • DoorDash avait une expertise interne de Kafka
  • Un échange de courtier est direct comme option prise en charge dans Celery
  • Le désabonnement de la connexion Harakiri ne dégrade pas de manière significative les performances de Kafka
  • Kafka n’est pas encore pris en charge par le Céleri
  • Ne résout pas le problème observé lorsque les travailleurs du Céleri arrêtent de traiter les tâches
  • Aucune amélioration de l’observabilité du céleri
  • Malgré l’expérience interne, nous n’avions pas exploité Kafka à grande échelle à DoorDash.
Plusieurs courtiers
  • Amélioration de la disponibilité
  • Évolutivité horizontale
  • Aucune amélioration de l’observabilité
  • Aucune amélioration de l’efficacité opérationnelle
  • Ne résout pas le problème observé lorsque les travailleurs de Céleri arrêtent de traiter des tâches
  • Ne résout pas le problème de désabonnement de connexion induit par harakiri
  • /li>
Versions de mise à niveau
  • Peut améliorer le problème où RabbitMQ est bloqué dans un état dégradé
  • Peut améliorer le problème où les travailleurs de Céleri sont bloqués
  • Pourrait nous acheter de la marge de manœuvre pour mettre en œuvre une stratégie à plus long terme
  • Non garanti pour corriger nos bogues observés
  • Ne résoudra pas immédiatement nos problèmes de disponibilité, d’évolutivité, d’observabilité et d’efficacité opérationnelle
  • Les versions plus récentes de RabbitMQ et Celery nécessitent des versions plus récentes de Python.
  • Ne résout pas le problème de la perte de connexion induite par harakiri
Solution Kafka personnalisée
  • Kafka peut être hautement disponible
  • Kafka est évolutif horizontalement
  • Amélioration de l’observabilité avec Kakfa en tant que courtier
  • Amélioration de l’efficacité opérationnelle
  • Expertise Kafka interne
  • Un courtier le changement est direct
  • Le désabonnement de la connexion Harakiri ne dégrade pas de manière significative les performances de Kafka
  • Résout le problème observé lorsque les travailleurs de Céleri arrêtent de traiter les tâches
  • Nécessite plus malgré l’expérience interne, nous n’avions pas exploité Kafka à grande échelle chez DoorDash

Notre stratégie d’intégration de Kafka

Compte tenu de la disponibilité requise du système, nous avons conçu notre stratégie d’intégration basé sur les principes suivants pour maximiser les avantages de fiabilité dans les plus brefs délais. Cette stratégie comportait trois étapes :

  • Frapper le sol en cours d’exécution: Nous voulions tirer parti des bases de la solution que nous construisions alors que nous itérions sur d’autres parties de celle-ci. Nous comparons cette stratégie à la conduite d’une voiture de course tout en échangeant une nouvelle pompe à carburant.
  • Choix de conception pour une adoption transparente par les développeurs: Nous voulions minimiser les efforts gaspillés de la part de tous les développeurs qui auraient pu résulter de la définition d’une interface différente.
  • Déploiement incrémental sans temps d’arrêt: Au lieu d’une grande version flashy testée dans la nature pour la première fois avec un risque plus élevé d’échecs, nous nous sommes concentrés sur l’expédition de fonctionnalités indépendantes plus petites qui pourraient être testées individuellement dans la nature sur une plus longue période.

Frapper le sol

Le passage à Kafka a représenté un changement technique majeur dans notre pile, mais qui était cruellement nécessaire. Nous n’avions pas de temps à perdre car chaque semaine, nous perdions des affaires en raison de l’instabilité de notre solution RabbitMQ héritée. Notre priorité première était de créer un produit minimum viable (MVP) pour nous apporter une stabilité provisoire et nous donner la marge de manœuvre nécessaire pour itérer et nous préparer à une solution plus complète avec une adoption plus large.

Notre MVP était composé de producteurs qui publiaient des noms complets de tâches (FQN) et des arguments marinés à Kafka pendant que nos consommateurs lisaient ces messages, importaient les tâches du FQN et les exécutaient de manière synchrone avec les arguments spécifiés.

L'architecture de Produit minimal Viable (MVP) que nous avons décidé de construire comprenait un état intermédiaire où nous publierions des tâches mutuellement exclusives à la fois sur l'héritage (lignes pointillées rouges) et sur les nouveaux systèmes (lignes continues vertes), avant l'état final où nous arrêterions de publier des tâches sur RabbitMQ.1

Figure 1: L’architecture de produit minimal Viable (MVP) que nous avons décidé de construire comprenait un état intermédiaire où nous publierions des tâches mutuellement exclusives à la fois sur l’héritage (lignes pointillées rouges) et sur les nouveaux systèmes (lignes continues vertes), avant l’état final où nous arrêterions de publier des tâches sur RabbitMQ.

Choix de conception pour une adoption transparente par les développeurs

Parfois, l’adoption par les développeurs est un plus grand défi que le développement. Nous avons facilité la tâche en implémentant un wrapper pour l’annotation @task de Celery qui acheminait dynamiquement les soumissions de tâches vers l’un ou l’autre système en fonction d’indicateurs de fonctionnalité configurables dynamiquement. Maintenant, la même interface pourrait être utilisée pour écrire des tâches pour les deux systèmes. Avec ces décisions en place, les équipes d’ingénierie n’ont dû faire aucun travail supplémentaire pour s’intégrer au nouveau système, sauf à mettre en œuvre un indicateur de fonctionnalité unique.

Nous voulions déployer notre système dès que notre MVP était prêt, mais il ne prenait pas encore en charge toutes les mêmes fonctionnalités que Celery. Celery permet aux utilisateurs de configurer leurs tâches avec des paramètres dans leur annotation de tâche ou lorsqu’ils soumettent leur tâche. Pour nous permettre de lancer plus rapidement, nous avons créé une liste blanche de paramètres compatibles et avons choisi de prendre en charge le plus petit nombre de fonctionnalités nécessaires pour prendre en charge la majorité des tâches.

Nous avons rapidement augmenté le volume de tâches au MVP basé sur Kafka, en commençant par les tâches à faible risque et à faible priorité. Certaines de ces tâches étaient exécutées aux heures creuses, ce qui explique les pointes de la métrique décrite ci-dessus.

Figure 2: Nous avons rapidement augmenté le volume de tâches au MVP basé sur Kafka, en commençant par les tâches à faible risque et à faible priorité. Certaines de ces tâches étaient exécutées aux heures creuses, ce qui explique les pointes de la métrique décrite ci-dessus.

Comme on le voit sur la figure 2, avec les deux décisions ci-dessus, nous avons lancé notre MVP après deux semaines de développement et avons atteint une réduction de 80% de la charge de tâche RabbitMQ une semaine après le lancement. Nous avons rapidement réglé notre problème principal des pannes et, au cours du projet, nous avons pris en charge de plus en plus de fonctionnalités ésotériques pour permettre l’exécution des tâches restantes.

Déploiement incrémental, absence de temps d’arrêt

La possibilité de changer de clusters Kafka et de basculer dynamiquement entre RabbitMQ et Kafka sans impact commercial était extrêmement importante pour nous. Cette capacité nous a également aidés dans diverses opérations telles que la maintenance des clusters, le délestage et les migrations progressives. Pour mettre en œuvre ce déploiement, nous avons utilisé des indicateurs de fonctionnalité dynamiques à la fois au niveau de la soumission des messages et du côté de la consommation des messages. Le coût d’être pleinement dynamique ici était de maintenir notre flotte de travailleurs à double capacité. La moitié de cette flotte était consacrée à RabbitMQ et le reste à Kafka. Faire fonctionner le parc de travailleurs à double capacité pesait définitivement sur notre infrastructure. À un moment donné, nous avons même créé un tout nouveau cluster Kubernetes pour héberger tous nos travailleurs.

Au cours de la phase initiale de développement, cette flexibilité nous a bien servis. Une fois que nous avions plus confiance en notre nouveau système, nous avons cherché des moyens de réduire la charge sur notre infrastructure, tels que l’exécution de plusieurs processus consommateurs par machine de travail. Au fur et à mesure que nous avons abordé divers sujets, nous avons pu commencer à réduire le nombre de travailleurs pour RabbitMQ tout en maintenant une petite capacité de réserve.

Aucune solution n’est parfaite, itérer au besoin

Avec notre MVP en production, nous avions la marge nécessaire pour itérer et polir notre produit. Nous avons classé chaque fonctionnalité de Céleri manquante en fonction du nombre de tâches qui l’utilisaient pour nous aider à décider lesquelles implémenter en premier. Les fonctionnalités utilisées par seulement quelques tâches n’ont pas été implémentées dans notre solution personnalisée. Au lieu de cela, nous avons réécrit ces tâches pour ne pas utiliser cette fonctionnalité spécifique. Avec cette stratégie, nous avons finalement déplacé toutes les tâches du Céleri.

L’utilisation de Kafka a également introduit de nouveaux problèmes nécessitant notre attention :

  • Blocage de la tête de ligne qui a entraîné des retards de traitement des tâches
  • Les déploiements ont déclenché un rééquilibrage des partitions qui a également entraîné des retards

Problème de blocage de la tête de ligne de Kafka

Les sujets de Kafka sont partitionnés de telle sorte qu’un seul consommateur (par groupe de consommateurs) lit les messages pour les partitions qui lui sont attribuées dans l’ordre dans lequel ils sont arrivés. Si un message dans une seule partition prend trop de temps à être traité, il bloquera la consommation de tous les messages derrière lui dans cette partition, comme le montre la figure 3 ci-dessous. Ce problème peut être particulièrement désastreux dans le cas d’un sujet hautement prioritaire. Nous voulons pouvoir continuer à traiter les messages dans une partition en cas de retard.

Dans le problème de blocage en tête de ligne de Kafka, un message lent dans une partition (en rouge) empêche tous les messages derrière elle d'être traités. D'autres partitions continueraient à être traitées comme prévu.

Figure 3: Dans le problème de blocage en tête de ligne de Kafka, un message lent dans une partition (en rouge) empêche le traitement de tous les messages derrière celle-ci. D’autres partitions continueraient à être traitées comme prévu.

Bien que le parallélisme soit fondamentalement un problème Python, les concepts de cette solution sont également applicables à d’autres langages. Notre solution, illustrée à la figure 4 ci-dessous, consistait à héberger un processus Kafka-consommateur et plusieurs processus d’exécution de tâches par travailleur. Le processus Kafka-consumer est responsable de la récupération des messages de Kafka et de leur placement dans une file d’attente locale lue par les processus d’exécution des tâches. Il continue à consommer jusqu’à ce que la file d’attente locale atteigne un seuil défini par l’utilisateur. Cette solution permet aux messages de la partition de circuler et un seul processus d’exécution de tâche sera bloqué par le message lent. Le seuil limite également le nombre de messages en vol dans la file d’attente locale (qui peut se perdre en cas de crash du système).

Figure 4: Notre Worker Kafka non bloquant se compose d'une file d'attente de messages locale et de deux types de processus : un processus kafka-consommateur et plusieurs processus task-executor. Alors qu'un consommateur kafka peut lire à partir de plusieurs partitions, pour plus de simplicité, nous n'en décrirons qu'une seule. Ce diagramme montre qu'un message à traitement lent (en rouge) ne bloque qu'un seul exécuteur de tâches jusqu'à sa fin, tandis que d'autres messages derrière lui dans la partition continuent d'être traités par d'autres exécuteurs de tâches.

Figure 4 :Notre travailleur Kafka non bloquant se compose d’une file d’attente de messages locale et de deux types de processus: un processus kafka-consommateur et plusieurs processus task-executor. Alors qu’un consommateur kafka peut lire à partir de plusieurs partitions, pour plus de simplicité, nous n’en décrirons qu’une seule. Ce diagramme montre qu’un message à traitement lent (en rouge) ne bloque qu’un seul exécuteur de tâches jusqu’à sa fin, tandis que d’autres messages derrière lui dans la partition continuent d’être traités par d’autres exécuteurs de tâches.

La disruptivité des déploiements

Nous déployons notre application Django plusieurs fois par jour. Un inconvénient de notre solution que nous avons remarqué est qu’un déploiement déclenche un rééquilibrage des affectations de partition dans Kafka. Malgré l’utilisation d’un groupe de consommateurs différent par sujet pour limiter la portée du rééquilibrage, les déploiements ont tout de même provoqué un ralentissement momentané du traitement des messages car la consommation de tâches a dû s’arrêter lors du rééquilibrage. Les ralentissements peuvent être acceptables dans la plupart des cas lorsque nous effectuons des versions planifiées, mais peuvent être catastrophiques lorsque, par exemple, nous effectuons une version d’urgence pour corriger un bogue. La conséquence serait l’introduction d’un ralentissement du traitement en cascade.

Les versions plus récentes de Kafka et de ses clients prennent en charge le rééquilibrage coopératif incrémental, ce qui réduirait massivement l’impact opérationnel d’un rééquilibrage. La mise à niveau de nos clients pour soutenir ce type de rééquilibrage serait notre solution de choix à l’avenir. Malheureusement, le rééquilibrage coopératif incrémental n’est pas encore pris en charge chez notre client Kafka choisi.

Key wins

Avec la conclusion de ce projet, nous avons réalisé des améliorations significatives en termes de disponibilité, d’évolutivité, d’observabilité et de décentralisation. Ces victoires ont été cruciales pour assurer la croissance continue de notre entreprise.

Plus de pannes répétées

Nous avons arrêté les pannes répétées presque dès que nous avons commencé à déployer cette approche Kafka personnalisée. Les pannes entraînaient une expérience utilisateur extrêmement médiocre.

  • En implémentant seulement un petit sous-ensemble des fonctionnalités de Céleri les plus utilisées dans notre MVP, nous avons pu expédier le code de travail en production en deux semaines.
  • Avec le MVP en place, nous avons pu réduire considérablement la charge sur RabbitMQ et Celery alors que nous continuions à durcir notre solution et à implémenter de nouvelles fonctionnalités.

Le traitement des tâches n’était plus le facteur limitant de la croissance

Avec Kafka au cœur de notre architecture, nous avons construit un système de traitement des tâches hautement disponible et évolutif horizontalement, permettant à DoorDash et à ses clients de poursuivre leur croissance.

Observabilité massivement augmentée

Comme il s’agissait d’une solution personnalisée, nous avons pu créer plus de métriques à presque tous les niveaux. Chaque file d’attente, chaque travailleur et chaque tâche était entièrement observable à un niveau très granulaire dans les environnements de production et de développement. Cette observabilité accrue a été une énorme victoire non seulement au sens de la production, mais aussi en termes de productivité des développeurs.

Décentralisation opérationnelle

Grâce aux améliorations de l’observabilité, nous avons pu modéliser nos alertes en modules Terraform et attribuer explicitement des propriétaires à chaque sujet et, implicitement, à plus de 900 tâches.

Un guide d’exploitation détaillé pour le système de traitement des tâches rend les informations accessibles à tous les ingénieurs pour déboguer les problèmes opérationnels avec leurs sujets et leurs travailleurs, ainsi que pour effectuer des opérations globales de gestion de cluster Kafka, selon les besoins. Les opérations quotidiennes sont en libre-service et le soutien de notre équipe d’infrastructure est rarement nécessaire.

Conclusion

Pour résumer, nous avons atteint le plafond de notre capacité à mettre à l’échelle RabbitMQ et avons dû chercher des alternatives. L’alternative que nous avons choisie était une solution personnalisée basée sur Kafka. Bien qu’il y ait quelques inconvénients à utiliser Kafka, nous avons trouvé un certain nombre de solutions de contournement, décrites ci-dessus.

Lorsque les flux de travail critiques dépendent fortement du traitement des tâches asynchrones, il est de la plus haute importance de garantir l’évolutivité. Lorsque vous rencontrez des problèmes similaires, n’hésitez pas à vous inspirer de notre stratégie, qui nous a accordé 80% du résultat avec 20% de l’effort. Cette stratégie, dans le cas général, est une approche tactique pour atténuer rapidement les problèmes de fiabilité et gagner du temps cruellement nécessaire pour une solution plus robuste et stratégique.

Remerciements

Les auteurs tiennent à remercier Clement Fang, Corry Haines, Danial Asif, Jay Weinstein, Luigi Tagliamonte, Matthew Anger, Shaohua Zhou et Yun-Yu Chen d’avoir contribué à ce projet.

Photo de tian kuan sur Unsplash

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.