Praktický návod
Jak navrhnout spolehlivý message consumer
Zprávu potvrď až po dokončení práce a počítej s tím, že po pádu dorazí znovu.
Nejdřív stručně
At-least-once znamená možné duplicity
Broker může zprávu doručit znovu, když consumer dokončí databázovou změnu, ale spadne před acknowledgementem. Spolehlivý návrh proto neslibuje právě jedno doručení; zajišťuje právě jeden obchodní efekt.
RabbitMQ drží nepotvrzenou zprávu a po ztrátě channelu ji zpřístupní jinému consumeru. Aplikace musí mít ruční ack, idempotenci a omezenou cestu selhání.
Připrav si
Co budeš potřebovat
Nejdřív definuj jeden business efekt zprávy a stabilní identitu, podle které poznáš opakované doručení.
- Verzované schéma zprávy s message ID, typem, occurred_at a identifikátorem business operace.
- Databázový unikátní klíč nebo přirozeně idempotentní operaci pro ochranu před duplicitou.
- Rozdělení chyb na dočasné a trvalé, retry limit a dead-letter cestu.
- Metriky počtu zpracování, chyb, retry, redelivery, délky práce, lagu a stavu workerů.
Kroky 1 až 3
Uzavři zpracování do bezpečné jednotky
Validace, idempotentní databázový efekt a acknowledgement mají přesné pořadí. Ack není cleanup v bloku finally.
1. Validuj zprávu před prací
- Ověř content type, typ události, verzi schématu, povinná pole a velikost ještě před voláním doménové logiky.
- Nepodporovanou verzi nebo trvale neplatná data odmítni bez requeue do DLQ s bezpečným důvodem.
- Propaguj correlation ID do logu a metrik, ale payload ani přístupové údaje neloguj bez redakce.
- Consumer nesmí důvěřovat názvu třídy nebo příkazu přímo ze zprávy. Typ mapuj přes explicitní allowlist handlerů.
message type + schema version → known handler Oficiální RabbitMQ dokumentace ke consumerům 2. Proveď idempotentní efekt a teprve potom ack
- Uvnitř jedné databázové transakce vlož message ID do tabulky zpracovaných zpráv a proveď business změnu. Unikátní constraint rozhodne souběžnou duplicitu.
- Pokud message ID již existuje, ověř úspěšně dokončený význam a zprávu potvrď bez opakování efektu.
- Ack odešli až po úspěšném commitu. Předčasný ack může při pádu nenávratně ztratit práci.
- Pokud zpracování publikuje další zprávu, zapiš ji do outboxu ve stejné transakci. Samotný ack a publish nejsou jedna atomická operace.
BEGIN → INSERT processed_message → business change → outbox → COMMIT → ACK Oficiální dokumentace k acknowledgementům 3. Omez souběh, retry a ukončení
- Nastav rozumný prefetch podle délky práce a paměti. Neomezený počet nepotvrzených zpráv zhoršuje rozdělení práce i obnovu po pádu.
- Dočasnou chybu opakuj s prodlužující se pauzou a limitem. Trvalou chybu pošli bez requeue do DLQ.
- Při SIGTERM přestaň přijímat nové zprávy, dokonči nebo bezpečně přeruš rozpracované a odešli ack pouze úspěšným.
- Dlouhou úlohu rozděl nebo nastav provozní timeout vědomě. Worker, který přestane potvrzovat zprávy, musí být viditelný v monitoringu.
prefetch=20; manualAck=true; retry=3; gracefulShutdown=true Oficiální dokumentace k prefetch Krok 4
Testuj pády v nejhorší chvíli
Happy path neověří spolehlivost. Proces ukonči mezi commitem a ackem a sleduj obchodní výsledek.
-
Spadni po commitu před ackem
Zpráva se doručí znovu, ale unikátní idempotency záznam zabrání druhé změně a druhému outbox eventu.
php bin/phpunit --filter ConsumerRedelivery -
Vrať dočasnou a trvalou chybu
Dočasná chyba projde jen omezeným retry; trvalá skončí přímo v DLQ. Žádná nesmí vytvořit horkou requeue smyčku.
-
Ukonči worker pod zátěží
Po SIGTERM nesmí přijmout nové zprávy. Potvrzené úlohy jsou dokončené a nepotvrzené broker znovu doručí.
Když to zlobí
Nejčastější chyby
Objednávka vznikla dvakrát
Consumer spoléhá na jediné doručení. Přidej stabilní business nebo message ID, unikátní constraint a záznam ve stejné transakci jako změnu.
Zpráva zmizela bez provedené práce
Ack byl odeslán před commitem nebo automaticky při převzetí. Zapni manual ack a potvrzuj jen úspěšný dokončený efekt.
Jeden worker drží příliš mnoho zpráv
Sniž prefetch a zkontroluj délku handleru. Hodnota musí odpovídat souběhu a paměti, ne maximálnímu throughputu v prázdném testu.
Po deployi roste počet redelivery
Worker se ukončuje bez drain fáze nebo překračuje acknowledgement timeout. Přidej graceful shutdown a měř dobu handlerů.
Hotovo
Consumer zvládá duplicity, chyby i restart.
RabbitMQ může zprávu bezpečně doručit znovu: business efekt chrání idempotence, ack přichází až po commitu a neúspěch má omezenou cestu.