web-dev-qa-db-fra.com

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

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 simple et très classique - 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 PUBSUB naïf ne fonctionne pas, car je ne veux pas que tous les abonnés consomment du travail, je veux juste n que l'abonné reçoive le travail. Au premier passage, il me semble que BRPOPLPUSH est une conception intelligente.

Pouvons-nous utiliser BRPOPLPUSH?

La conception de base avec 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. 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'utiliser PUBSUB sans consommer deux fois les tâches est simplement de pousser une notification indiquant que le travail est arrivé, que les consommateurs peuvent ensuite sans blocage 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 comme bien.

29
djechlin

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 - le Redis File d'attente de messages simple pour le nœud. 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, alors vous voudrez peut-être lire la source de RSMQ car ce ne sont que 20 écrans de code qui fait exactement ce que vous demandez.

Voir:

5
rsp

J'ai rencontré des 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 devrait être une opération idempotente, mais lorsque vous avez affaire à 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 devrait être aussi simple que possible. Idéalement, elle devrait avoir les propriétés suivantes:

  • 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.
    .

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 inutile du tout; il 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.

22
djechlin

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 de la partition afin de ne pas perdre de messages. Les doublons peuvent être traités car la source gère l'UUID.

0
ra9r