9
votes

Questions sur la concurrence Bull Queue

J'ai besoin d'aide pour comprendre comment Bull Queue (bull.js) traite les travaux simultanés.

Supposons que j'ai 10 instances Node.js qui instancient chacune une Bull Queue connectée à la même instance Redis:

const bullQueue = require('bull');
const queue = new bullQueue('taskqueue', {...})
const concurrency = 5;
queue.process('jobTypeA', concurrency, job => {...do something...});

Cela signifie-t-il que globalement, sur les 10 instances de nœuds, il y aura un maximum de 5 tâches (simultanéité) exécutées simultanément de type jobTypeA ? Ou suis-je mal compris et le paramètre de concurrence est une instance par nœud?

Que se passe-t-il si une instance de nœud spécifie une valeur de concurrence différente?

Puis-je être certain que les tâches ne seront pas traitées par plus d'une instance de Node?


0 commentaires

5 Réponses :


1
votes

Ah Bienvenue! Ceci est une méta-réponse et probablement pas ce que vous espériez, mais un processus général pour résoudre ce problème:

Vous pouvez spécifier un argument de concurrence. Bull appellera alors votre gestionnaire en parallèle en respectant cette valeur maximale.

Personnellement, je ne comprends pas vraiment cela ni les garanties que Bull offre. Comme ce n'est pas très clair:

À l'OMI, la chose la plus importante est:

Puis-je être certain que les tâches ne seront pas traitées par plus d'un nœud instance?

Si le traitement exclusif des messages est un invariant et entraînerait une erreur pour votre application, même avec une excellente documentation, je vous recommande vivement d'effectuer une diligence raisonnable sur la bibliothèque: p


0 commentaires

2
votes

En y regardant de plus près, je pense que Bull ne gère pas du tout la distribution sur plusieurs instances Node, donc le comportement est au mieux indéfini.


1 commentaires

Eh bien, les emplois de taureau sont bien répartis, tant qu'ils consomment le même sujet sur un redis unique. Chaque taureau consomme un travail sur la file d'attente redis, et votre code définit qu'au plus 5 peuvent être traités par nœud simultanément, cela devrait faire 50 (semble beaucoup).



3
votes

Le TL; DR est: dans des conditions normales, les travaux ne sont traités qu'une seule fois. Si les choses tournent mal (par exemple, le processus Node.js plante), les tâches peuvent être traitées deux fois.

Citation du README.md officiel de Bull:

Remarques importantes

La file d'attente vise une stratégie de travail «au moins une fois». Cela signifie que dans certaines situations, un travail peut être traité plusieurs fois. Cela se produit principalement lorsqu'un collaborateur ne parvient pas à conserver un verrou pour une tâche donnée pendant la durée totale du traitement.

Lorsqu'un collaborateur traite un travail, il le maintient «verrouillé» afin que les autres employés ne puissent pas le traiter.

Il est important de comprendre comment fonctionne le verrouillage pour éviter que vos travaux ne perdent leur verrouillage - deviennent bloqués - et ne soient redémarrés en conséquence. Le verrouillage est implémenté en interne en créant un verrou pour lockDuration sur l'intervalle lockRenewTime (qui est généralement la moitié de lockDuration ). Si lockDuration s'écoule avant que le verrou ne puisse être renouvelé, le travail sera considéré comme bloqué et redémarré automatiquement; il sera traité deux fois . Cela peut se produire lorsque:

  1. Le processus Node exécutant votre processeur de travaux se termine de manière inattendue.
  2. Votre processeur de tâches était trop gourmand en CPU et a bloqué la boucle d'événements Node, et par conséquent, Bull n'a pas pu renouveler le verrou de tâche (voir # 488 pour savoir comment nous pourrions mieux détecter cela). Vous pouvez résoudre ce problème en divisant votre processeur de travaux en parties plus petites afin qu'aucune partie ne puisse bloquer la boucle d'événements Node. Vous pouvez également transmettre une valeur plus élevée pour le paramètre lockDuration (le compromis étant qu'il faudra plus de temps pour reconnaître un vrai travail bloqué).

En tant que tel, vous devez toujours écouter l'événement bloqué et le consigner dans votre système de surveillance des erreurs, car cela signifie que vos travaux sont susceptibles d'être traités en double.

Pour garantir que les jobs problématiques ne seront pas redémarrés indéfiniment (par exemple, si le processeur de jobs plante toujours son processus Node), les jobs seront récupérés à partir d'un état bloqué un maximum de maxStalledCount fois (par défaut : 1 ).


1 commentaires

Merci beaucoup !



2
votes

J'ai passé beaucoup de temps à y creuser après avoir affronté un problème avec trop de threads de processeur .

La petite histoire est que la concurrence de Bull est au niveau de la file d'attente objet , pas au niveau de la file d'attente.

Si vous creusez dans le code, le paramètre de concurrence est appelé au moment où vous appelez .process sur votre objet de file d'attente. Cela signifie que même dans la même application Node, si vous créez plusieurs files d'attente et que vous appelez .process plusieurs fois, elles ajouteront au nombre de tâches simultanées pouvant être traitées.

Un contributeur a publié ce qui suit:

Oui, c'était un peu surprenant pour moi aussi quand j'ai utilisé Bull pour la première fois temps. Les options de file d'attente ne sont jamais conservées dans Redis. Vous pouvez en avoir autant Les instances de file d'attente par application comme vous le souhaitez, chacune peut avoir des réglages. Le paramètre de concurrence est défini lorsque vous enregistrez un processeur, il est en fait spécifique à chaque appel de fonction process (), et non Queue. Si vous utilisez des processeurs nommés, vous pouvez appeler process () multiple fois. Chaque appel enregistrera N gestionnaires de boucle d'événements (avec Node process.nextTick ()), par le montant de la concurrence (la valeur par défaut est 1).

La réponse à votre question est donc: oui, vos processus SERONT traités par plusieurs instances de nœuds si vous enregistrez des gestionnaires de processus dans plusieurs instances de nœuds.


1 commentaires

Après avoir réalisé que la concurrence "s'accumule" chaque fois qu'une file d'attente enregistre .process () , cela rend plus difficile la mise à l'échelle horizontale, encore plus difficile pour la mise à l'échelle élastique. Lorsque le processus nodejs principal se propage sur N nœuds / machine - peut-être en tant que répliques du déploiement de kubernetes, chacun exécutera le même code et enregistrera donc .process () N fois. Vous devrez corriger cela explicitement pour ne pas appeler .process () dans certaines répliques. Je pense que les gens s'attendent à une gestion centralisée de la concurrence ici - après tout, Bull utilise déjà Redis; Malheureusement, Bull ne le fournit pas encore.



2
votes

Bull est conçu pour traiter des jobs simultanément avec une sémantique "au moins une fois", bien que si les processeurs fonctionnent correctement, c'est-à-dire qu'ils ne se bloquent pas ou ne plantent pas, ils livrent en fait "exactement une fois". Cependant, vous pouvez définir le nombre maximal de tentatives bloquées sur 0 (maxStalledCount https: // github.com/OptimalBits/bull/blob/develop/REFERENCE.md#queue ) et ensuite la sémantique sera "au plus une fois".

Cela dit, je vais essayer de répondre aux 2 questions posées par l'affiche:

Que se passe-t-il si une instance de Node spécifie une valeur de concurrence différente?

Je suppose que vous voulez dire "instance de file d'attente". Si tel est le cas, la concurrence est spécifiée dans le processeur. Si la concurrence est X, ce qui se passe, c'est qu'au plus X travaux seront traités simultanément par ce processeur donné.

Puis-je être certain que les tâches ne seront pas traitées par plus d'une instance de Node?

Oui, tant que votre tâche ne plante pas ou que le paramètre de nombre maximal de tâches bloquées est de 0.


0 commentaires