2012-10-29 10 views
6

Avere un problema in cui uno dei membri del mio team sta eseguendo un processo di accodamento in PHP. Lo script PHP viene eseguito sulla riga di comando e si richiama in modo ricorsivo dopo ogni ciclo per verificare se nel nostro DB ci sono elementi in attesa di essere elaborati. Se ci sono, si forca da sé, elabora l'elemento in coda e si ripete. Se non c'è nulla, muore e un processo cron riavvia la coda ogni 5 minuti.Problema di accodamento PHP - Processi ridondanti

Occasionalmente, due processi vengono eseguiti contemporaneamente, acquisendo la stessa coda e ottenendo il reciproco passaggio. Stavo pensando di introdurre un jitter per il processo di sveglia in modo che sia una quantità casuale di tempo tra l'inizio del processo.

C'è un modo migliore?

risposta

4

Il jitter rende solo le collisioni meno prevedibili.

Inserire un mutex su disco. Controlla se è più vecchio di due cicli fa. Se lo è, ignoralo ed eliminalo; è stato lasciato indietro da qualcosa che si è schiantato. Altrimenti ce n'è già uno in esecuzione, quindi su cauzione.

2

Se gli elementi nella coda non devono essere eseguiti in modo da poter fare qualcosa di simile e lasciare entrambi i processi per eseguire in parallelo.

Selezionare prossimo lavoro dalla coda (ovviamente la sintassi sarà di database dipendente)

LOCK TABLE queue; 

SELECT * FROM queue WHERE status <> 'INPROGRESS' ORDER BY id LIMIT 1 

UPDATE queue SET status = 'INPROGRESS' WHERE id = ? 

UNLOCK TABLES; 

fare il lavoro quindi contrassegnare lavoro come completato o cancellare dalla coda

Se si vuole trovare un lavoro bloccato in un limbo, è possibile memorizzare un timestamp di lavoro avviato e rimetterlo in coda se è presente per più di x minuti

0

LOCK TABLE queue; è un po 'eccessivo perché sembra l'intera tabella in modo che non vi sia alcuna concorrenza. Non è necessario alcun blocco se si utilizza un approccio di aggiornamento sul campo (lo stato è aggiornato a INPROGRESS). L'operazione di aggiornamento di SQL fa la serratura internamente, in modo da qualcosa di simile deve lavorare contemporaneamente:

UPDATE queue SET consumer_id='a_consumer_unique_id' status='INPROGRESS' LIMIT 1 

SELECT * FROM queue WHERE consumer_id='a_consumer_unique_id' LIMIT 1 

Ma questo approccio ha effetti collaterali. Dobbiamo cambiare la bandiera INPROGRESS o impostare consumer_id. Se un consumatore fa capolino un messaggio (il consumer_id impostato su di esso) e muore cosa accadrebbe al messaggio? Rimarrà in una coda per anni, quindi è necessario una sorta di script di pulizia da affrontare in quelle situazioni.

Quello che possiamo fare è contrassegnare un messaggio come nostro, caricare in memoria ed eliminarlo dalla coda. Solo dopo averlo fatto, inizieremo l'elaborazione. Ecco come lo fa enqueue/dbal.

Se avete bisogno di una garanzia di riconsegna utilizzate un vero broker come RabbitMQ.