Comment implémenter une file d'attente de messages sur Redis?

29

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 PUBSUBne 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 BRPOPLPUSHc'est un design intelligent.

Pouvons-nous utiliser BRPOPLPUSH?

La conception de base BRPOPLPUSHest 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 LREMlui. 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, PUBSUBcar 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'utiliser PUBSUBsans consommer les tâches deux fois est simplement de pousser une notification indiquant que le travail est arrivé, que les consommateurs peuvent ensuite non bloquer RPOPLPUSH.
  • 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.

Djechlin
la source

Réponses:

5

Si vous souhaitez utiliser Redis pour une file d'attente de messages dans Node.js et que cela ne vous dérange pas d'utiliser un module pour cela, vous pouvez essayer RSMQ - la Redis Simple Message Queue pour Node. Il n'était pas disponible au moment où cette question a été posée, mais aujourd'hui, c'est une option viable.

Si vous voulez réellement implémenter la file d'attente vous-même comme vous l'avez dit dans votre question, vous voudrez peut-être lire la source de RSMQ car ce ne sont que 20 écrans de code qui font exactement ce que vous demandez.

Voir:

rsp
la source
J'accepterai cela à moins que j'apprenne plus tard que c'est vraiment défectueux ou cassé ou quelque chose.
djechlin
22

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:

  • L'écoute est idempotente.
  • Le consommateur écoute toujours et la logique de limitation est traitée en dehors du code de la logique d'écoute. RabbitMQ résume cela en laissant le consommateur lié le nombre de messages non empilés qu'il peut avoir.
    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:

  • les consommateurs doivent également vérifier les travaux de reconnexion.
  • les consommateurs peuvent vouloir faire un sondage pour de nouveaux travaux de toute façon, pour la redondance. Si le sondage réussit, un avertissement doit être émis, car cela ne doit se produire qu'entre la consommation sur le PUBSUB et le sondage sur un RPOPLPUSH. Par conséquent, de nombreux succès de sondage indiquent un système d'abonnement défectueux.

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.

Djechlin
la source
Je ne suis pas sûr de suivre le problème du blocage des consommateurs. Il me semble que s'il n'y a pas de travail à traiter, les consommateurs devraient bloquer jusqu'à ce qu'il y en ait, mais je suppose que si le consommateur fait également d'autres choses qui pourraient être une histoire différente, mais ce n'est pas plus un problème dans l'application et pas tant pour la file d'attente? IE ne serait pas un blocage de thread dans une application plus grande serait une solution plus élégante, où le thread serait alors en mesure d'avertir l'application quand il avait récupéré un travail de la file d'attente. C'est peut-être juste l'utilisation du nœud qui crée la complication.
AaronM
9
Je suis curieux de savoir jusqu'où vous êtes allé depuis août dernier. Avez-vous pu résoudre vos problèmes à votre satisfaction? Comment les avez-vous résolus?
AaronM
3
AAA: tout comme @AaronM, j'aimerais savoir comment vous avez progressé.
bjornl
D'accord. Comment cela a-t-il progressé? J'aime l'idée de supprimer RabbitMQ de la pile et d'utiliser Redis qui est là de toute façon. Mon problème est de savoir comment enregistrer un consommateur à l'aide de RSMQ (node ​​lib).
ra9r
@raiglstorfer n'y travaille pas depuis deux ans: P n'hésitez pas à rechercher et publier ...
djechlin
0

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.

ra9r
la source