Pas seulement le traitement : comment nous avons créé une base de données distribuée à partir de Kafka Streams et ce qui en est ressorti

Hé Habr !

Nous vous rappelons qu'en suivant le livre sur Kafka nous avons publié un ouvrage tout aussi intéressant sur la bibliothèque API Kafka Streams.

Pas seulement le traitement : comment nous avons créé une base de données distribuée à partir de Kafka Streams et ce qui en est ressorti

Pour l’instant, la communauté commence tout juste à découvrir les limites de cet outil puissant. Ainsi, un article a été récemment publié dont nous aimerions vous présenter la traduction. À partir de sa propre expérience, l'auteur explique comment transformer Kafka Streams en un stockage de données distribué. Bonne lecture!

Bibliothèque Apache Flux Kafka utilisé dans le monde entier dans les entreprises pour le traitement de flux distribué sur Apache Kafka. L'un des aspects sous-estimés de ce framework est qu'il vous permet de stocker l'état local produit sur la base du traitement des threads.

Dans cet article, je vais vous expliquer comment notre entreprise a réussi à utiliser cette opportunité de manière rentable lors du développement d'un produit pour la sécurité des applications cloud. À l'aide de Kafka Streams, nous avons créé des microservices à état partagé, chacun d'entre eux servant de source d'informations fiables, tolérante aux pannes et hautement disponible, sur l'état des objets dans le système. Pour nous, c’est un pas en avant tant en termes de fiabilité que de facilité d’assistance.

Si vous êtes intéressé par une approche alternative vous permettant d'utiliser une base de données centrale unique pour prendre en charge l'état formel de vos objets, lisez-la, ce sera intéressant...

Pourquoi nous avons pensé qu'il était temps de changer notre façon de travailler avec l'État partagé

Nous devions maintenir l'état de divers objets en fonction des rapports des agents (par exemple : le site était-il attaqué) ? Avant de migrer vers Kafka Streams, nous nous appuyions souvent sur une seule base de données centrale (+ API de service) pour la gestion des états. Cette approche a ses inconvénients : situations intensives en rendez-vous maintenir la cohérence et la synchronisation devient un véritable défi. La base de données peut devenir un goulot d'étranglement ou se retrouver dans condition de course et souffrent d'imprévisibilité.

Pas seulement le traitement : comment nous avons créé une base de données distribuée à partir de Kafka Streams et ce qui en est ressorti

Figure 1 : Un scénario typique d’état divisé observé avant la transition vers
Kafka et Kafka Streams : les agents communiquent leurs points de vue via l'API, l'état mis à jour est calculé via une base de données centrale

Découvrez Kafka Streams, qui facilite la création de microservices à état partagé

Il y a environ un an, nous avons décidé d’examiner attentivement nos scénarios d’État partagé pour résoudre ces problèmes. Nous avons immédiatement décidé d'essayer Kafka Streams - nous savons à quel point il est évolutif, hautement disponible et tolérant aux pannes, et quelles sont ses riches fonctionnalités de streaming (transformations, y compris celles avec état). Juste ce dont nous avions besoin, sans parler de la maturité et de la fiabilité du système de messagerie de Kafka.

Chacun des microservices avec état que nous avons créés a été construit sur une instance Kafka Streams avec une topologie assez simple. Il se composait de 1) une source 2) un processeur avec un magasin clé-valeur persistant 3) un récepteur :

Pas seulement le traitement : comment nous avons créé une base de données distribuée à partir de Kafka Streams et ce qui en est ressorti

Figure 2 : La topologie par défaut de nos instances de streaming pour les microservices avec état. Notez qu'il existe également ici un référentiel contenant des métadonnées de planification.

Dans cette nouvelle approche, les agents composent des messages qui sont introduits dans le sujet source et les consommateurs (par exemple, un service de notification par courrier électronique) reçoivent l'état partagé calculé via le récepteur (sujet de sortie).

Pas seulement le traitement : comment nous avons créé une base de données distribuée à partir de Kafka Streams et ce qui en est ressorti

Figure 3 : Nouvel exemple de flux de tâches pour un scénario avec des microservices partagés : 1) l'agent génère un message qui arrive au sujet source Kafka ; 2) un microservice avec état partagé (utilisant Kafka Streams) le traite et écrit l'état calculé dans le sujet Kafka final ; après quoi 3) les consommateurs acceptent le nouvel état

Hé, ce magasin de clés/valeurs intégré est en fait très utile !

Comme mentionné ci-dessus, notre topologie d'état partagé contient un magasin clé-valeur. Nous avons trouvé plusieurs options pour l'utiliser, et deux d'entre elles sont décrites ci-dessous.

Option n°1 : utiliser un magasin de valeurs-clés pour les calculs

Notre premier magasin clé-valeur contenait les données auxiliaires dont nous avions besoin pour les calculs. Par exemple, dans certains cas, l'État partagé était déterminé par le principe du « vote majoritaire ». Le référentiel peut contenir tous les derniers rapports d'agent sur l'état de certains objets. Ensuite, lorsque nous recevions un nouveau rapport d'un agent ou d'un autre, nous pouvions le sauvegarder, récupérer les rapports de tous les autres agents sur l'état du même objet depuis le stockage et répéter le calcul.
La figure 4 ci-dessous montre comment nous avons exposé le magasin clé/valeur à la méthode de traitement du processeur afin que le nouveau message puisse ensuite être traité.

Pas seulement le traitement : comment nous avons créé une base de données distribuée à partir de Kafka Streams et ce qui en est ressorti

Illustration 4 : Nous ouvrons l'accès au magasin clé-valeur pour la méthode de traitement du processeur (après cela, chaque script qui fonctionne avec l'état partagé doit implémenter la méthode doProcess)

Option n°2 : Création d'une API CRUD au-dessus de Kafka Streams

Après avoir établi notre flux de tâches de base, nous avons commencé à essayer d'écrire une API RESTful CRUD pour nos microservices à état partagé. Nous voulions pouvoir récupérer l'état de tout ou partie des objets, ainsi que définir ou supprimer l'état d'un objet (utile pour le support backend).

Pour prendre en charge toutes les API Get State, chaque fois que nous avions besoin de recalculer l'état pendant le traitement, nous l'avons stocké pendant une longue période dans un magasin clé-valeur intégré. Dans ce cas, il devient assez simple d'implémenter une telle API à l'aide d'une seule instance de Kafka Streams, comme indiqué dans la liste ci-dessous :

Pas seulement le traitement : comment nous avons créé une base de données distribuée à partir de Kafka Streams et ce qui en est ressorti

Figure 5 : Utilisation du magasin clé-valeur intégré pour obtenir l'état précalculé d'un objet

Mettre à jour l’état d’un objet via l’API est également simple à mettre en œuvre. Fondamentalement, tout ce que vous avez à faire est de créer un producteur Kafka et de l'utiliser pour créer un enregistrement contenant le nouvel état. Cela garantit que tous les messages générés via l'API seront traités de la même manière que ceux reçus d'autres producteurs (par exemple des agents).

Pas seulement le traitement : comment nous avons créé une base de données distribuée à partir de Kafka Streams et ce qui en est ressorti

Figure 6 : Vous pouvez définir l'état d'un objet à l'aide du producteur Kafka

Petite complication : Kafka a de nombreuses partitions

Ensuite, nous souhaitions répartir la charge de traitement et améliorer la disponibilité en fournissant un cluster de microservices à état partagé par scénario. La configuration a été un jeu d'enfant : une fois que nous avons configuré toutes les instances pour qu'elles s'exécutent sous le même ID d'application (et les mêmes serveurs d'amorçage), presque tout le reste a été fait automatiquement. Nous avons également précisé que chaque sujet source serait composé de plusieurs partitions, de sorte que chaque instance puisse se voir attribuer un sous-ensemble de ces partitions.

Je mentionnerai également qu'il est courant de faire une copie de sauvegarde du magasin d'état afin que, par exemple, en cas de récupération après une panne, transférer cette copie vers une autre instance. Pour chaque magasin d'état dans Kafka Streams, un sujet répliqué est créé avec un journal des modifications (qui suit les mises à jour locales). Ainsi, Kafka sauvegarde constamment le magasin d'État. Ainsi, en cas de panne de l'une ou l'autre instance de Kafka Streams, le magasin d'état peut être rapidement restauré sur une autre instance, où iront les partitions correspondantes. Nos tests ont montré que cela se fait en quelques secondes, même s'il y a des millions de disques dans le magasin.

En passant d'un microservice unique avec un état partagé à un cluster de microservices, il devient moins trivial d'implémenter l'API Get State. Dans la nouvelle situation, le magasin d'état de chaque microservice ne contient qu'une partie de l'image globale (les objets dont les clés ont été mappées sur une partition spécifique). Nous avons dû déterminer quelle instance contenait l'état de l'objet dont nous avions besoin, et nous l'avons fait en nous basant sur les métadonnées du thread, comme indiqué ci-dessous :

Pas seulement le traitement : comment nous avons créé une base de données distribuée à partir de Kafka Streams et ce qui en est ressorti

Figure 7 : À l'aide des métadonnées du flux, nous déterminons à partir de quelle instance interroger l'état de l'objet souhaité ; une approche similaire a été utilisée avec l'API GET ALL

principaux résultats

Les magasins d'état dans Kafka Streams peuvent servir de base de données distribuée de facto,

  • constamment répliqué dans Kafka
  • Une API CRUD peut facilement être construite sur un tel système
  • La gestion de plusieurs partitions est un peu plus compliquée
  • Il est également possible d'ajouter un ou plusieurs magasins d'état à la topologie de streaming pour stocker des données auxiliaires. Cette option peut être utilisée pour :
  • Stockage à long terme des données nécessaires aux calculs lors du traitement du flux
  • Stockage à long terme des données qui peuvent être utiles lors du prochain provisionnement de l'instance de streaming
  • beaucoup plus...

Ces avantages, parmi d’autres, rendent Kafka Streams bien adapté au maintien de l’état global dans un système distribué comme le nôtre. Kafka Streams s'est avéré très fiable en production (nous n'avons eu pratiquement aucune perte de message depuis son déploiement), et nous sommes convaincus que ses capacités ne s'arrêteront pas là !

Source: habr.com

Ajouter un commentaire