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.

30 minut · RabbitMQ

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í

  1. Ověř content type, typ události, verzi schématu, povinná pole a velikost ještě před voláním doménové logiky.
  2. Nepodporovanou verzi nebo trvale neplatná data odmítni bez requeue do DLQ s bezpečným důvodem.
  3. Propaguj correlation ID do logu a metrik, ale payload ani přístupové údaje neloguj bez redakce.
  4. 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

  1. 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.
  2. Pokud message ID již existuje, ověř úspěšně dokončený význam a zprávu potvrď bez opakování efektu.
  3. Ack odešli až po úspěšném commitu. Předčasný ack může při pádu nenávratně ztratit práci.
  4. 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í

  1. 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.
  2. Dočasnou chybu opakuj s prodlužující se pauzou a limitem. Trvalou chybu pošli bez requeue do DLQ.
  3. 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.
  4. 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.

  1. 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
  2. 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.

  3. 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.

Zavolejte mi

Zavolám vám následující pracovní den mezi 9:00 a 17:00.

Můžete mi také zavolat rovnou.

+420 605 181 728

Nechte mi telefonní číslo a pošlete žádost o zpětné zavolání.

Odesláním souhlasíte se zpracováním údajů pro vyřízení žádosti.