web-dev-qa-db-fra.com

RabbitMQ - Ordre de livraison des messages

Je dois choisir un nouveau courtier de file d'attente pour mon nouveau projet. 

Cette fois, j'ai besoin d'une file d'attente évolutive prenant en charge pub/sub, et il est impératif de conserver l'ordre des messages. 

J'ai lu le commentaire d'Alexis: Il écrit:

"En effet, nous pensons que RabbitMQ fournit un ordre plus fort que Kafka"

J'ai lu la section relative à la commande de messages dans rabbitmq docs:

"Les messages peuvent être renvoyés dans la file d'attente à l'aide de méthodes AMQP comportant le paramètre Un paramètre de remise en file d'attente .__ (basic.recover, basic.reject et basic.nack) ou en raison d'une fermeture de canal .__ tout en conservant les messages non acquittés. ..Avec la version 2.7.0 et les versions ultérieures Il est toujours possible pour les consommateurs individuels d’observer des messages en dehors de la commande Si la file d’attente compte plusieurs abonnés, en raison des actions de Autres abonnés pouvant remettre en file d'attente. messages. Du point de vue de la file d'attente, les messages sont toujours conservés dans l'ordre de publication. "

Si je dois gérer les messages en fonction de leur ordre, je ne peux utiliser que rabbitMQ avec une file d'attente exclusive pour chaque consommateur? 

RabbitMQ est-il toujours considéré comme une bonne solution pour la mise en file d'attente des messages ordonnés?

41
Bick

Eh bien, examinons de plus près le scénario que vous décrivez ci-dessus. Je pense qu'il est important de coller la documentation juste avant l'extrait de code dans votre question pour fournir le contexte:

La section 4.7 de la spécification de base AMQP 0-9-1 explique le conditions dans lesquelles la commande est garantie: messages publiés dans un canal, passant par un commutateur, une file d'attente et un le canal sortant sera reçu dans le même ordre qu’il était envoyé. RabbitMQ offre des garanties plus solides depuis la version 2.7.0.

Les messages peuvent être renvoyés dans la file d'attente à l'aide de méthodes AMQP comportant un paramètre de remise en file d'attente (basic.recover, basic.reject et basic.nack), ou en raison de la fermeture d’un canal tout en maintenant des messages non acquittés. N'importe quel ces scénarios ont entraîné la remise en file d'attente des messages à l'arrière du fichier file d'attente pour les versions de RabbitMQ antérieures à 2.7.0. De la version RabbitMQ 2.7.0, les messages sont toujours conservés dans la file d'attente dans l'ordre de publication, même en cas de remise en file d'attente ou de fermeture du canal.} _ (Caractères gras ajoutés)

Il est donc clair que RabbitMQ, à partir de la version 2.7.0, apporte une amélioration assez radicale par rapport à la spécification AMQP originale en ce qui concerne le classement des messages. 

Avec plusieurs consommateurs (parallèles), l'ordre de traitement ne peut pas être garanti.
Le troisième paragraphe (collé dans la question) contient ensuite une clause de non-responsabilité, que je vais paraphraser: "si vous avez plusieurs processeurs dans la file d'attente, rien ne garantit que les messages seront traités dans l'ordre." Tout ce qu'ils disent, c'est que RabbitMQ ne peut pas défier les lois des mathématiques.

Considérons une ligne de clients dans une banque. Cette banque est fière d'aider les clients dans l'ordre dans lequel ils sont entrés dans la banque. Les clients s'alignent dans une file d'attente et sont servis par le prochain des 3 guichets disponibles. 

Ce matin, il est arrivé que les trois guichets soient devenus disponibles en même temps et que les 3 prochains clients se soient approchés. Soudainement, le premier des trois guichets est tombé violemment malade et n'a pu finir de servir le premier client de la file. Au moment où cela s'est produit, le caissier 2 avait fini avec le client 2 et le caissier 3 avait déjà commencé à servir le client 3.

Maintenant, l'une des deux choses peut arriver. (1) Le premier client en ligne peut retourner au début de la ligne ou (2) le premier client peut préempter le troisième client, ce qui arrête de travailler sur le troisième client et commence à travailler sur le premier. Ce type de logique de préemption n'est pas pris en charge par RabbitMQ, ni par aucun autre courtier de messages que je sache. Dans les deux cas, le premier client ne finit pas par être aidé en premier. Le deuxième client le fait, ayant la chance d'obtenir un bon dépanneur rapide dès le départ. Le seul moyen de garantir que les clients soient bien aidés consiste à faire appel à un guichet unique pour chaque client, ce qui entraînera des problèmes de service client majeurs pour la banque.

J'espère que cela aide à illustrer le problème que vous demandez. Il n'est pas possible de garantir que les messages sont traités dans l'ordre dans tous les cas, étant donné que vous avez plusieurs consommateurs. Peu importe si vous avez plusieurs files d'attente, plusieurs consommateurs exclusifs, différents courtiers, etc. - il n'y a aucun moyen de garantir a priori que les messages sont répondus dans l'ordre à plusieurs consommateurs. Mais RabbitMQ fera de son mieux.

107
theMayer

L'ordre des messages est conservé dans Kafka, mais uniquement dans les partitions plutôt que globalement. Si vos données nécessitent à la fois un ordre global et des partitions, cela rend les choses difficiles. Cependant, si vous devez simplement vous assurer que tous les mêmes événements pour le même utilisateur, etc., se retrouvent dans la même partition afin d'être correctement ordonnés, vous pouvez le faire. Le producteur est responsable de la partition sur laquelle il écrit. Par conséquent, si vous êtes en mesure de partitionner logiquement vos données, cela peut être préférable.

6
tyler neely

Je pense qu'il y a deux choses dans cette question qui ne sont pas similaires, ordre de consommation et ordre de traitement.

Les files d'attente de messages peuvent - dans une certaine mesure - vous donner une garantie que les messages seront consommés dans l'ordre, ils ne peuvent toutefois pas vous donner de garantie sur l'ordre de leur traitement.

La principale différence est que certains aspects du traitement des messages ne peuvent pas être déterminés au moment de la consommation, par exemple:

  • Comme mentionné, un consommateur peut échouer lors du traitement. Dans ce cas, l'ordre de consommation du message était correct. Cependant, le consommateur n'a pas traité correctement le message, ce qui l'a fait revenir dans la file d'attente. Jusqu'à présent, l'ordre de consommation était toujours intact, mais nous ne le faisons pas. t savoir comment l'ordre de traitement est maintenant

  • Si, par "traitement", nous entendons que le message est maintenant mis au rebut et que son traitement est complètement terminé, alors considérons le cas où votre temps de traitement n'est pas linéaire, autrement dit, le traitement d'un message prend plus de temps que l'autre, donc si le message 3 prend plus de temps que le anticipé, les messages 4 et 5 peuvent être consommés et terminer le traitement avant le message 3.

Ainsi, même si vous parveniez à ramener le message au début de la file d'attente (ce qui en passant viole l'ordre de consommation), vous ne pouvez toujours pas garantir que tous les messages antérieurs au message suivant ont été traités.

Si vous voulez assurer l'ordre de traitement, alors:

  1. N'avoir qu'une seule instance de consommateur à tout moment
  2. Ou n'utilisez pas de file d'attente de messagerie et effectuez le traitement selon une méthode de blocage synchrone, ce qui peut paraître mauvais, mais dans de nombreux cas, les exigences de l'entreprise sont tout à fait valables et parfois même critiques.
2
engma

Il existe des moyens appropriés pour garantir l'ordre des messages dans les abonnements RabbitMQ.

Si vous utilisez plusieurs consommateurs, ils traiteront le message en utilisant une ExecutorService partagée. Voir aussi ConnectionFactory.setSharedExecutor(...). Vous pouvez définir une Executors.newSingleThreadExecutor().

Si vous utilisez une Consumer avec une seule file d'attente, vous pouvez la lier à l'aide de plusieurs bindingKeys (ils peuvent comporter des caractères génériques). Les messages seront placés dans la file d'attente dans l'ordre où ils ont été reçus par le courtier de messages.

Par exemple, vous avez un seul éditeur qui publie des messages lorsque l'ordre est important:

try (Connection connection2 = factory.newConnection();
        Channel channel2 = connection.createChannel()) {
    // publish messages alternating to two different topics
    for (int i = 0; i < messageCount; i++) {
        final String routingKey = i % 2 == 0 ? routingEven : routingOdd;
        channel2.basicPublish(exchange, routingKey, null, ("Hello" + i).getBytes(UTF_8));
    }
}

Vous voudrez peut-être maintenant recevoir des messages de topics dans une queue dans le même ordre de publication:

// declare a queue for the consumer
final String queueName = channel.queueDeclare().getQueue();

// we bind to queue with the two different routingKeys
final String routingEven = "even";
final String routingOdd = "odd";
channel.queueBind(queueName, exchange, routingEven);
channel.queueBind(queueName, exchange, routingOdd);
channel.basicConsume(queueName, true, new DefaultConsumer(channel) { ... });

La Consumer recevra désormais les messages dans l'ordre dans lequel ils ont été publiés, indépendamment du fait que vous ayez utilisé différents topics.

La documentation de RabbitMQ contient de bons didacticiels de 5 minutes qui pourraient être utiles: https://www.rabbitmq.com/tutorials/tutorial-five-Java.html

0
benez