www.ITNetwork.fr

Le Blog > RabbitMQ : messages prioritaires

— Benoüt Petitcollot

RabbitMQ : messages prioritaires

Dans cet article, je vous prĂ©sente un cas d’utilisation de messages rabbitMQ avec diffĂ©rents niveaux de prioritĂ©.

Nous verrons Ă  travers un exemple de consumer en javascript qu’il ne suffit pas d’ajouter un attribut priority sur un message pour qu’il soit effectivement traitĂ© prioritairement.

Cas d’utilisation

On a une application web qui permet Ă  un utilisateur d’envoyer une newsletter Ă  un nombre assez important d’utilisateurs, disons par exemple 10.000. Chaque email est personnalisĂ© (“Bonjour, <Nom> <PrĂ©nom>”) donc on doit envoyer ces email un par un. On utilise un service SMTP limitĂ© Ă  200 envois par minute. L’envoi Ă  nos 10.000 contacts va donc prendre 50 minutes. Pour ne pas bloquer l’envoyeur sur le formulaire d’envoi, on va publier un message rabbit pour chaque email et Ă©crire un consumer chargĂ© d’envoyer les emails. Comme on veut rĂ©utiliser des Ă©lĂ©ments du design du site dans les emails, on Ă©crit le consumer en javascript.

Maintenant, un utilisateur demande Ă  rĂ©initialiser son mot de passe pendant que la newsletter est en cours d’envoi. Si on publie simplement un message rabbit dans la queue, celui-ci ne sera consommĂ© qu’aprĂšs les messages dĂ©jĂ  prĂ©sents et l’utilisateur va attendre de longues minutes avec de recevoir son email. C’est lĂ  qu’entre en jeu le paramĂštre priority.

Priorité des messages dans une queue

Pour commencer, il faut savoir qu’un message ne pourra ĂȘtre dĂ©livrĂ© prioritairement que par une queue paramĂ©trĂ©e pour gĂ©rer les messages prioritaires. La queue doit ĂȘtre crĂ©Ă©e avec un argument x-max-priority qui dĂ©finit le niveau maximal de prioritĂ© des messages dans la queue. C’est un entier entre 0 et 255. Attention, ce paramĂštre ne peut pas ĂȘtre modifiĂ© donc si votre queue existe dĂ©jĂ , il vous faudra la supprimer et la recrĂ©er avec le bon paramĂ©trage.

Par exemple en mettant x-max-priority = 3, on aura quatre niveaux de priorité : 0 = pas urgent du tout, 1 = pas urgent, 2 = urgent, 3 = trÚs urgent.

création d'une queue avec priorité

queue avec priorité

On peut maintenant publier un message prioritaire dans cette queue, comme on le fait d’habitude, simplement en ajoutant la propriĂ©tĂ© priority

  • si on ne met pas de prioritĂ©, la prioritĂ© sera de 0 (la plus basse)
  • si on dĂ©passe la prioritĂ© maximale de la queue, la queue considĂšre que le message a la prioritĂ© maximale (la valeur de x-max-priority)

Voici ce que ça donne avec l’interface rabbitMQ manager.

Publication des messages :

publication d'un message avec priorité 0

publication d'un message sans priorité

publication d'un message avec priorité 10

publication d'un message avec priorité 1

publication d'un message avec priorité 3

Récupération des messages :

récupération des messages dans l'ordre de priorité

On constate que les messages sortent bien dans l’ordre attendu : les plus prioritaires en premiers puis, pour une mĂȘme prioritĂ©, par ordre d’entrĂ©e dans la queue.

Consommer les messages en tenant compte de leur priorité

Mais ce n’est pas tout ! Si on se contente de ce paramĂ©trage (qui est correct) et qu’on consomme les messages, par exemple comme ceci


async function sendMail(rabbitMessage){
  // envoi de l'email via le service SMTP...
}

await channel.consume('mailer', async mailMessage => {
  await sendMail(mailMessage);
});

on va attendre l’email de rĂ©initialisation de mot de passe un certain temps


La doc de rabbitMQ prĂ©vient : “It’s important to understand how consumers work when working with priority queues.”

En effet, un consumer consomme d’un seul coup autant de messages que le lui permettent les limites de capacitĂ© du rĂ©seau ! Autant dire que nos 10.000 messages de newsletter sont consommĂ©s en une seule fois. Le message prioritaire qui arrive ensuite devra attendre que le consumer ait fini de traiter les 10.000 messages pour ĂȘtre consommĂ© Ă  son tour. En fait, on n’a pas laissĂ© Ă  notre queue l’occasion de gĂ©rer la prioritĂ© des messages.

Pour rĂ©soudre ce problĂšme, on va indiquer le nombre de messages qu’un consumer sera autorisĂ© Ă  consommer en une seule fois. C’est l’option prefetch.

channel.prefetch(3, false);
await channel.consume('mailer', async mailMessage => {
  await sendMail(mailMessage);
});

Ainsi notre consumer consommera les messages trois par trois, ce qui correspond Ă  un dĂ©lai de traitement d’environ une seconde pour notre serveur SMTP. AprĂšs ce delai, le consumer retourne vers la queue et si des messages prioritaires ont Ă©tĂ© ajoutĂ©s, ils seront bien traitĂ©s en premiers.

Pour finir

Je n’ai pas abordĂ© la gestion des erreurs. Si vous utilisez des queues de retry, n’oubliez pas de leur mettre aussi une propriĂ©tĂ© x-max-priority. En pratique, si le taux d’erreur dans le traitement des messages est assez faible, ce paramĂštre n’est pas indispensable. Vous pouvez publier un message avec prioritĂ© dans une queue non prioritaire. La prioritĂ© du message sera bien transmise quand il reviendra dans la queue principale.

Documentation

RabbitMQ : Priority Queue Support

RabbitMQ : Consumer Prefetch