2011-11-28 16 views
8

Lavoro su un'applicazione Web che è un'applicazione basata su multi-tenant basata su cloud (molti client, ognuno con il proprio "ambiente" separato, ma tutti su set di hardware condivisi) e stiamo introducendo la possibilità per un utente di raggruppare il lavoro per l'elaborazione successiva. Il tipo di lavoro in batch non è davvero importante, è solo una quantità sufficiente che farlo senza una coda di lavoro non è davvero pratico. Abbiamo selezionato RabbitMQ come struttura della coda sottostante.Pool di lavoratori e code multi-tenant con RabbitMQ

Poiché siamo un'app multi-tenant, non vogliamo necessariamente che i client siano in grado di causare lunghi tempi di elaborazione della coda per un altro client, quindi l'idea che abbiamo messo a punto è creare una coda su un base client e con un pool di lavoro condiviso puntato su TUTTE le code dei nostri client. Il problema è che, al meglio che posso immaginare, i lavoratori sono direttamente legati a una coda specifica, non a uno scambio. Nel nostro mondo ideale, le code dei nostri clienti verranno ancora elaborate, senza che un client ne blocchi un'altra, da un pool di lavoro condiviso che possiamo aumentare o ridurre, se necessario, avviando più lavoratori o chiudendo quelli inattivi. Avere lavoratori legati a una coda specifica ci impedisce di farlo in senso pratico, dato che spesso ci sono molti lavoratori in attesa su una coda senza attività.

Esiste un modo relativamente semplice per raggiungere questo obiettivo? Sono abbastanza nuovo su RabbitMQ e non sono stato in grado di realizzare ciò che cercavamo. Inoltre, non vogliamo dover scrivere un'applicazione per il multithreading molto complessa, è una perdita di tempo in dev e tempo di test che probabilmente non possiamo permetterci. Il nostro stack è basato su Windows/.Net/C# se si tratta di germaine, ma non penso che questo dovrebbe avere un ruolo importante nella domanda in questione.

risposta

1

Si può semplicemente fare in modo che tutti i pool di lavoratori consumino la stessa coda univoca. Il lavoro verrà quindi distribuito tra di loro e sarete in grado di aumentare/ridurre la piscina per aumentare/diminuire la capacità di elaborazione del lavoro.

+1

Non sto chiedendo sull'assegnazione di più lavoratori alla stessa coda, sto chiedendo il contrario. Voglio un pool finito di lavoratori per consumare da un grande (chiamiamolo ~ 500) numero di code. – bakasan

+1

Ho sperimentato in prima persona questo tipo di approccio e non è bello: è difficile trovare un'euristica adeguata per elaborare tutte queste code. Elaborate prima le code più complete? O quelli con i vecchi messaggi? In entrambi i casi, sei fuori dal protocollo AMQP e devi iniziare a gestire l'API di gestione dei conigli. Poi pensi: abbiamo lo stesso numero di code dei lavoratori e aggiungi una mappatura hash coerente tra 500 Q e le code di lavoro. Poi ti rendi conto che una sola fila e tutti i lavoratori in competizione sono tutto ciò di cui hai bisogno. –

+0

Ho un requisito simile, tuttavia desidero garantire che i messaggi di un determinato cliente vengano elaborati in sequenza. Un contatto non viene eliminato prima della sua creazione, ecc. Esiste qualche configurazione o configurazione di RabbitMQ che può farlo, pur condividendo la coda tra i lavoratori? (È una nuova Q ...?) – Aaron

1

Non capisco perché non si utilizzino i vhost di RabbitMQ e la propria app si colleghi a RabbitMQ e si autentifichino su una connessione separata per ciascun utente.

Ciò non significa che non sia possibile avere un supervisore operaio che assegna lavoratori a un utente o a un altro. Ma significa che tutti i messaggi per ogni utente vengono elaborati da scambi e code completamente separati.

0

Ai lavoratori sono assegnate 0+ code, non scambi.

La logica per cui verranno eseguite le attività da cui le code per ciascun operatore sono implementate nella classe indicata tramite CELERYD_CONSUMER, che per impostazione predefinita è celery.worker.consumer.Consumer.

È possibile creare una classe di consumatore personalizzata che implementa la logica desiderata. La parte difficile sarà decidere i dettagli dell'algoritmo "equità" che si desidera utilizzare; ma una volta deciso, è possibile implementarlo creando una classe consumer personalizzata e assegnandola a lavoratori appropriati.

1

si poteva guardare l'attuazione coda di priorità (che non è stata implementata quando questa domanda è stato originariamente chiesto): https://www.rabbitmq.com/priority.html

Se questo non funziona per voi, si potrebbe provare alcuni altri hack per ottenere ciò che si voglio (che dovrebbe funzionare con le versioni precedenti di RabbitMQ):

È possibile avere 100 code associate a uno scambio di argomenti e impostare la chiave di routing su un hash dell'ID utente% 100, ovvero ogni attività avrà una chiave tra 1 e 100 e le attività per lo stesso utente avranno la stessa chiave. Ogni coda è associata a un modello univoco compreso tra 1 e 100.Ora hai una flotta di lavoratori che inizia con un numero di coda casuale e poi incrementa il numero di coda dopo ogni lavoro, ancora% 100 per tornare alla coda 1 dopo la coda 100.

Ora la tua flotta di lavoratori può elaborare fino a 100 utenti unici in parallelo, o tutti i lavoratori possono concentrarsi su un singolo utente se non c'è altro lavoro da fare. Se i lavoratori hanno bisogno di scorrere tutte le 100 code tra ogni lavoro, nello scenario in cui solo un singolo utente ha molti lavori su una singola coda, si avrà naturalmente un certo overhead tra ogni lavoro. Un numero minore di code è un modo per affrontare questo problema. Si potrebbe anche avere ogni lavoratore in possesso di una connessione a ciascuna delle code e consumare fino a un messaggio non riconosciuto da ciascuno. L'operatore può quindi scorrere più velocemente i messaggi in sospeso nella memoria, a condizione che il timeout del messaggio non confermato sia impostato su un valore sufficientemente alto.

In alternativa è possibile creare due scambi, ciascuno con una coda vincolata. Tutto il lavoro va al primo scambio e alla coda, che un gruppo di lavoratori consuma. Se un'unità di lavoro impiega troppo tempo, il lavoratore può annullarla e spingerla nella seconda coda. I lavoratori elaborano solo la seconda coda quando non c'è nulla sulla prima coda. Potresti anche volere un paio di lavoratori con la priorità della coda opposta per assicurarti che le attività a esecuzione prolungata vengano ancora elaborate quando c'è un flusso infinito di brevi attività che arrivano, in modo che un batch di utenti venga sempre elaborato alla fine. Ciò non distribuirà realmente la flotta di lavoratori in tutte le attività, ma interromperà le attività a esecuzione prolungata da parte di un utente che impedisce ai dipendenti di eseguire attività di breve durata per lo stesso utente o altro. Presuppone anche che tu possa cancellare un lavoro e rieseguirlo in un secondo momento senza problemi. Significa anche che ci saranno risorse sprecate da attività che scadono e che devono essere rieseguite come priorità bassa. A meno che non sia possibile identificare in anticipo task veloci e lenti

Il primo suggerimento con le 100 code potrebbe anche avere un problema se ci sono 100 attività lente per un singolo utente, quindi un altro utente registra una serie di attività. Queste attività non verranno esaminate fino a quando non sarà terminata una delle attività lente. Se questo risulta essere un problema legittimo, potresti potenzialmente combinare le due soluzioni.

Problemi correlati