Pourquoi Redis pour la file d'attente?
J'ai l'impression que Redis peut faire un bon candidat pour l'implémentation d'un système de file d'attente. Jusqu'à présent, nous avons utilisé notre base de données MySQL avec interrogation, ou RabbitMQ. Avec RabbitMQ, nous avons eu de nombreux problèmes - les bibliothèques clientes sont très pauvres et boguées et nous aimerions ne pas investir trop d'heures de développement pour les corriger, quelques problèmes avec la console de gestion du serveur, etc. Et, pour le moment étant au moins, nous ne saisissons pas les millisecondes ou n'augmentons pas sérieusement les performances, donc tant qu'un système a une architecture qui prend en charge une file d'attente intelligemment, nous sommes probablement en bonne forme.
D'accord, c'est donc l'arrière-plan. Essentiellement, j'ai un modèle de file d'attente très classique et simple - plusieurs producteurs produisant du travail et plusieurs consommateurs consommant du travail, et les producteurs et les consommateurs doivent pouvoir évoluer intelligemment. Il s'avère qu'un naïf PUBSUB
ne fonctionne pas, car je ne veux pas que tous les abonnés consomment du travail, je veux juste qu'un abonné reçoive le travail. Au premier passage, il me semble que BRPOPLPUSH
c'est un design intelligent.
Pouvons-nous utiliser BRPOPLPUSH?
La conception de base BRPOPLPUSH
est que vous avez une file d'attente de travail et une file d'attente de progression. Lorsqu'un consommateur reçoit du travail, il pousse atomiquement l'élément dans la file d'attente de progression et lorsqu'il termine le travail, c'est LREM
lui. Cela empêche le blackholing du travail si les clients meurent et rend la surveillance assez facile - par exemple, nous pouvons dire s'il y a un problème obligeant les consommateurs à prendre beaucoup de temps pour effectuer des tâches, en plus de dire s'il y a un grand volume de tâches.
Il assure
- le travail est livré à exactement un consommateur
- le travail se termine dans une file d'attente de progression, donc il ne peut pas trou noir si un consommateur
Les inconvénients
- Il me semble plutôt étrange que le meilleur design que j'aie trouvé n'utilise pas réellement,
PUBSUB
car c'est ce sur quoi la plupart des articles de blog sur la mise en file d'attente sur Redis se concentrent. J'ai donc l'impression de manquer quelque chose d'évident. La seule façon que je vois d'utiliserPUBSUB
sans consommer les tâches deux fois est simplement de pousser une notification indiquant que le travail est arrivé, que les consommateurs peuvent ensuite non bloquerRPOPLPUSH
. - Il est impossible de demander plusieurs éléments de travail à la fois, ce qui semble être un problème de performances. Pas énorme pour notre situation, mais il est plutôt évident que cette opération n'a pas été conçue pour un débit élevé ou cette situation
- En bref: est -ce que je manque quelque chose de stupide?
Ajout également de la balise node.js, car c'est la langue avec laquelle je traite le plus. Node peut offrir quelques simplifications dans l'implémentation, compte tenu de sa nature à thread unique et non bloquant, mais en outre j'utilise la bibliothèque node-redis et les solutions devraient ou peuvent être sensibles à ses forces et faiblesses également.
J'ai rencontré quelques difficultés jusqu'à présent, je voudrais documenter ici.
Comment gérez-vous la logique de reconnexion?
C'est un problème difficile et un problème particulièrement difficile dans la conception et la mise en œuvre d'une file d'attente de messages. Les messages doivent pouvoir être mis en file d'attente quelque part lorsque les consommateurs sont hors ligne, donc un simple pub-sub n'est pas assez fort, et les consommateurs doivent se reconnecter dans un état d'écoute. Les pops bloquants sont un état difficile à maintenir, car ils sont un état d'écoute non idempotent . L'écoute doit être une opération idempotente, mais lorsque vous traitez une déconnexion par rapport à un blocage pop, vous avez le plaisir de réfléchir très attentivement à savoir si la déconnexion s'est produite juste après la réussite de l'opération ou juste avant l'échec de l'opération. Ce n'est pas insurmontable, mais c'est indésirable.
De plus, l'opération d'écoute doit être aussi simple que possible. Idéalement, il devrait avoir ces propriétés:
En particulier, je suis allé avec une conception médiocre dans laquelle le retour dans une pop bloquante était subordonné au succès des opérations précédentes, qui était fragile et nécessitait une réflexion approfondie.
Je suis maintenant en faveur d'une solution Redis PUBSUB + RPOPLPUSH. Cela dissocie la notification du travail de la consommation du travail, ce qui nous permet de prendre en compte une solution d'écoute propre. Le PUBSUB est uniquement responsable de la notification des travaux. La nature atomique de RPOPLPUSH est responsable de la consommation et de la délégation du travail à exactement un consommateur. Au début, cette solution semblait inutilement compliquée par rapport à un pop bloquant, mais maintenant je vois que la complication n'était pas du tout inutile; cela résolvait un problème difficile.
Cependant, cette solution n'est pas tout à fait triviale:
Notez que la conception PUBSUB / RPOPLPUSH a également des problèmes de mise à l'échelle. Chaque consommateur reçoit une notification légère de chaque message, ce qui signifie qu'il y a un goulot d'étranglement inutile. Je soupçonne qu'il est possible d'utiliser des canaux pour partager le travail, mais c'est probablement une conception délicate pour bien fonctionner.
la source
Ainsi, la principale raison de choisir d'utiliser RabbitMQ sur Redis est les scénarios de défaillance et le clustering.
Cet article l'explique vraiment mieux, donc je vais simplement fournir le lien:
https://aphyr.com/posts/283-jepsen-redis
Redis sentinel et plus récemment redis clustering ne sont pas en mesure de gérer un certain nombre de scénarios de défaillance très basiques qui en ont fait un mauvais choix pour une file d'attente.
RabbitMQ a son propre ensemble de problèmes, mais cela étant dit, il est incroyablement solide en production et constitue une bonne file d'attente de messages.
Voici le post pour le lapin:
https://aphyr.com/posts/315-jepsen-rabbitmq
Lorsque vous regardez le théorème CAP (cohérence, disponibilité et gestion des partitions), vous ne pouvez en choisir que 2 sur 3. Nous tirons parti de RMQ pour le CP (cohérence et gestion des partitions) avec notre chargement de message, si nous ne sommes pas disponibles, ce n'est pas le cas. t la fin du monde. Afin de ne pas perdre de messages, nous utilisons ignore pour la gestion des partitions afin de ne pas perdre de messages. Les doublons peuvent être traités car la source gère l'UUID.
la source