Event Driven Architecture : Apache Kafka sur le rivage

Après vous avoir tous convaincus, j’en suis sûr, dans le précédent épisode de cette série, de l’intérêt d’une gestion événementielle de vos processus, je m’en vais vous parler plus en détails du bus que nous utilisons chez mon employeur actuel préféré.

Apache Kafka est un produit à l’origine développé par LinkedIn. Ecrit en Scala, cette plate-forme est capable d’ingurgiter un nombre impressionnant de messages par seconde (potentiellement par millions selon ce benchmark de LinkedIn).

Son architecture est sur le principe assez simple finalement (même si son implémentation n’est évidemment pas aussi triviale).

Globalement, il faut voir Kafka comme un système de log distribué. Chaque log (appelée topic) peut être répliquée sur plusieurs nœuds du cluster pour assurer le failover. Chaque topic est découpé en partitions, principalement pour permettre la parallélisation de la consommation (on y reviendra plus tard, promis).

Le tout est orchestré par le célèbre Apache Zookeeper, qui permet aux membres du cluster Kafka de se synchroniser, de gérer les élections de leaders pour chaque partition de topic, de suivre l’état d’avancement des consommateurs…

Le principal intérêt de ce mode de fonctionnement : la persistance n’est pas une option qui ralentit le système comme souvent sur d’autres gestionnaires de messages. Les messages sont disponibles pendant toute la durée de rétention dans le topic, pour tout consommateur qui voudrait les récupérer (sans pour autant configurer d’exchange pour provisionner des queues).

Des topics partitionnés

Afin de faciliter la parallélisation des traitements par les consommateurs, disais-je, un topic est partitionnable (et donc souvent partitionné). En effet, c’est le nombre de partitions qui détermine le nombre maximum de consommateurs pouvant travailler en même temps. Le schéma ci-après résume le fonctionnement.

KafkaTopic

Lorsqu’un producteur envoie un message dans un topic, celui-ci est déposé dans une des partitions en fonction du résultat d’un hachage de la clé associée au message. 2 messages avec la même clé arrivent dans la même partition (ce qui permet de gérer des ordres d’arrivée dans certains cas).

C’est alors qu’arrive dans l’histoire un premier consommateur qui va venir boulotter ces messages innocents. Il va venir se connecter à ce cluster en se déclarant d’un certain groupe (partons sur « A »). Supposons un instant que ce consommateur se compose d’une unique instance. Celui-ci va se voir affecter l’ensemble des partitions du topic, et recevoir tous les messages qui arrivent quelle que soit la partition sur laquelle ce message a été publié.

Si vous instanciez un autre consommateur qui s’enregistre avec le même groupe « A », Kafka va faire un « re-balancing » des partitions pour les répartir parmi les consommateurs présents. Par exemple, si comme dans l’exemple le topic a 3 partitions, une instance va recevoir les messages de 2 partitions, et l’autre ceux de la partition restante. Et si une troisième instance arrive ? Chacune se verra affecter une et une seule partition.

Jusque là, c’est simple. Eh bien… ça ne va pas se compliquer !

Si une quatrième instance arrive, toujours pour le même groupe « A », elle recevra… rien ! Elle sera en stand-by, la bouche grande ouverte, tel un oisillon affamé attendant sa maman partie chasser les vers de terre, attendant le trépas d’une de ses aînées.

Une partition ne peut à un instant donné n’être consommée que par une unique instance d’un consommateur d’un groupe donné.

« Et si je désire consommer les mêmes messages pour une autre utilisation ? » me demandera le lecteur attentif. « Facile » répondrai-je : il suffit de se déclarer tout simplement comme étant d’un autre groupe de consommateur (« B » par exemple).

On a ainsi à la fois un mode « publish and subscribe » tout en ayant aussi un mode « queue » combinable.

Comment ça fonctionne sous le capot ?

Comme précisé plus tôt, toute la coordination est faite au travers de Zookeeper.

Chaque nœud du serveur Kafka envoie des informations aux autres via Zookeeper. Que ce soit pour ré-élire un leader du cluster, que ce soit pour élire un nouveau leader pour une partition d’un topic donné, ou même annoncer l’arrivée d’un nouveau larron dans le cluster.

Zookeeper est également en charge du pilotage des consommateurs, de la répartition des partitions à chaque instance d’un même groupe, et du stockage des derniers offsets consommés par chaque instance sur chaque partition. Ainsi, si un consommateur s’arrête, Zookeeper affecte la ou les partitions concernées aux autres instances du groupe, en passant aussi le ou les offsets où le consommateur en était au moment de son arrêt.

Comment ça s’installe ?

Réponse : très simplement.

  • Installer une JVM sur votre serveur (au moins 1.7)
  • Télécharger l’archive binaire sur la page de download de Kafka
  • La décompresser sur votre serveur
  • Lancer Zookeeper :
    $ bin/zookeeper-server-start.sh config/zookeeper.properties
  • Lancer Kafka :
    $ bin/kafka-server-start.sh config/server.properties
  • Vous pouvez créer un topic en utilisant une commande du genre :
    $ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test
  • Si vous voulez pousser quelques messages, le plus simple est d’utiliser l’outil console fourni dans la distribution
    $ bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test
    Un premier message
    Un second message (parce que je le vaux bien)
  • Pour consommer, idem, un outil console est fourni dans la distribution
    $ bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning
  • (je vous laisse deviner ce qui apparaîtra alors :))

Quel langage pour communiquer avec Kafka ?

Premier élément de réponse : pas PHP ! En effet, il n’y a aucun client mature pour ce langage. L’absence d’asynchronisme est ici le principal handicap, avec en plus le manque d’engouement des PHP-istes (qui préfèrent RabbitMQ en général).

Mais ce n’est pas grave n’est-ce pas ? Car nous sommes des développeurs polyglottes, nous sommes curieux. Beaucoup d’autres clients existent dans d’autres langages. Ceux de la JVM bien sûr (Java, Scala, JRuby,…) mais aussi en node.js, python, Go. La liste tenue à jour est disponible sur cette page du wiki de Kafka.

Deux niveaux d’API sont disponibles, une high level et une low level. Je ne saurais que trop vous conseiller de rester sur la high level qui fait tout pour vous (la gestion des offsets, le re-balancing, le suivi des changements de topologie du cluster,…). La low level est destinée aux cas marginaux… et aux masochistes.

Et comment on fait quand notre SI est en PHP ?

Je vous l’ai déjà dit, mon employeur actuel a son SI en grande partie en PHP. Donc comment faire… En consommation, autant le dire : oubliez ! Par contre, en production, il y a une solution toute simple : mettre en place une interface REST en façade du système.

Mon choix a été de l’écrire en Java (mais ça aurait pu être dans n’importe lequel des langages ayant un client Kafka disponible). En utilisant la librairie Dropwizard (développée par Yammer), c’est vraiment simple et rapide. D’autant que… d’autres l’on fait avant moi, ça aide !

Stay tuned

Voilà donc comment mettre en place ce magnifique Kafka. Nous le verrons dans la suite de ces articles, ce n’est pas forcément la technologie parfaite et il y a quelques points sur lesquels faire attention pour ne pas se planter.

Mais ça, je vous le garde pour la prochaine fois !

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *