Przejdź do treści
Architektura 7 min czytania

Kolejki w e-commerce - RabbitMQ, Redis, SQS, Kafka

Każdy projekt sklepu B2B w skali ma moment, w którym kolejki przestają być „dobrym pomysłem na później" i stają się ratunkiem. Najczęściej moment ten przychodzi w nocy, gdy ERP klienta restartuje się na 4 godziny i wszystkie nowe zamówienia walą się 500-tkami. Z kolejkami - nikt nawet nie zauważy. Bez nich - telefon o 3 nad ranem.

Jakub Owsianka Autor
Zaktualizowano:
Okladka artykulu: Kolejki w e-commerce - RabbitMQ, Redis, SQS, Kafka (kategoria: Architektura)
Okladka artykulu: Kolejki w e-commerce - RabbitMQ, Redis, SQS, Kafka (kategoria: Architektura)
Spis treści (11)

W skrócie

  • 1. Kolejki = każda operacja asynchroniczna w architekturze sklepu
  • 2. Co przez kolejkę: zamówienia → ERP, synchronizacje, emaile, reindeksowanie, marketplace sync
  • 3. Co synchronicznie: koszyk, checkout, walidacje przy zamówieniu
  • 4. Technologie polskiego B2B: RabbitMQ (najczęściej), Redis Streams (mniejsza skala), SQS (na AWS)
  • 5. Must-have wzorce: idempotencja, retry z exponential backoff, dead letter queue, monitoring

Co konkretnie powinno przejść przez kolejkę

Lista operacji typowego sklepu B2B i decyzja kolejka / synchronicznie:

Operacja Kolejka? Powód
Zamówienie → ERP TAK ERP może być wolne / niedostępne
Email "Dziękujemy za zamówienie" TAK SMTP może być wolne
SMS o statusie TAK API SMS może mieć rate limit
Synchronizacja stanów ERP → sklep TAK Wolumen + retry
Synchronizacja cenników ERP → sklep TAK Wolumen + retry
Webhook do BaseLinkera o nowym zamówieniu TAK External system, niestabilność
Reindeksowanie Elasticsearch TAK Powolne, ale niekrytyczne
Walidacja koszyka NIE Klient czeka
Walidacja limitu kredytowego przy checkout NIE Decyzja synchroniczna
Płatność online (callback z bramki) TAK Bramki to external
Generowanie raportu PDF TAK Może trwać sekundy
Push notification TAK External provider

Reguła: jeśli klient nie czeka na rezultat - kolejka. Jeśli czeka (koszyk, checkout) - synchronicznie.

RabbitMQ - domyślny wybór dla średniego B2B

RabbitMQ to message broker oparty na AMQP, najpopularniejszy w polskim PHP/Symfony świecie.

Mocne strony:

  • Bardzo dojrzały (od 2007)
  • Dobre wsparcie w PHP/Symfony (Symfony Messenger ma RabbitMQ transport)
  • Exchange + Queue + Binding = elastyczne routing
  • Dead letter queue (DLQ) wbudowane
  • Management UI (przeglądasz kolejki w przeglądarce)
  • Klastrowanie i replikacja

Słabe strony:

  • Wymaga osobnego procesu (RabbitMQ server) i konfiguracji
  • Persistence przez Mnesia (specyficzna baza Erlanga)
  • Performance dobry, ale nie świetny dla bardzo dużych wolumenów (powyżej setek tysięcy / sekundę)

Typowa konfiguracja w sklepie B2B:

Exchange: orders (direct)
  Queue: orders.to_erp (binding: routing_key=erp)
  Queue: orders.to_baselinker (binding: routing_key=baselinker)
  Queue: orders.to_email (binding: routing_key=email)

Exchange: products (fanout)
  Queue: products.reindex_search
  Queue: products.invalidate_cache
  Queue: products.sync_marketplace

Dead Letter Exchange (DLX): dlq
  Queue: dlq.all (catch-all dla failed messages)

Hosting RabbitMQ:

  • Self-hosted: 1-2 maszyny (HA cluster), ~500-1500 zł / mies.
  • Managed: CloudAMQP (od $20/mies. do $1000+ dla większych)
  • AWS: Amazon MQ for RabbitMQ

Redis Streams - lekka alternatywa

Redis Streams (dodane w Redis 5.0, 2018) to wbudowany w Redis mechanizm kolejek. Lżejszy niż RabbitMQ.

Mocne strony:

  • Jeden serwis (Redis), nie dwa
  • Bardzo szybki (memory-based)
  • Consumer groups dla skalowania
  • Persistence przez Redis (RDB/AOF)
  • Dobry dla mniejszej skali

Słabe strony:

  • Mniej funkcji niż RabbitMQ (brak exchange-routing, słabsze DLQ patterns)
  • Persistence słabsza (Redis może gubić dane przy awarii)
  • Mniej dojrzały ekosystem

Kiedy Redis Streams:

  • Mała / średnia skala (do ~10k messages/min)
  • Już używasz Redis dla cache i sesji
  • Chcesz minimum dodatkowej infrastruktury

Kiedy NIE Redis Streams:

  • Duża skala (powyżej 50k+ messages/min)
  • Skomplikowane routing (fanout, topic exchanges)
  • Krytyczne wymagania persistence (nie chcesz tracić nawet jednego message)

AWS SQS / Google Pub/Sub - dla cloud-hosted

Jeśli sklep na AWS - naturalny wybór to SQS. Plusy:

  • Managed (nie utrzymujesz)
  • Skalowanie automatyczne
  • Wbudowany DLQ
  • Tania (kilka USD / mies. dla średniego sklepu)

Minus:

  • Lock-in w AWS
  • Mniej elastyczne routing niż RabbitMQ

Analogicznie Google Cloud Pub/Sub i Azure Service Bus.

Kafka - gdy skala wymusza

Apache Kafka to message broker dla bardzo dużej skali. Eventy persistowane jako log, replay możliwy.

Kiedy Kafka:

  • Skala 100k+ events / sec
  • Multi-konsument (kilku konsumentów tego samego eventu)
  • Event sourcing
  • Stream processing

W polskim B2B średniej skali: rzadko. Kafka to znaczna inwestycja w setup i operacje.

Symfony Messenger - abstrakcja, którą warto

Symfony Messenger to component frameworka Symfony, który abstraktuje konkretną technologię kolejek.

// Wysyłanie message - niezależnie od transportu
$bus->dispatch(new OrderToERPMessage($orderId));

// Konfiguracja transportu w yaml - można zmienić bez ruszania kodu
framework:
  messenger:
    transports:
      erp_sync: 'amqp://guest:guest@rabbitmq:5672/%2f/messages'
      # lub: 'redis://redis:6379/messages'
      # lub: 'sqs://...'

Plus: zaczynasz z Redis (proste), przechodzisz na RabbitMQ (skala), bez zmian w kodzie aplikacji.

Dla Magento / Shopware: używaj wbudowanego mechanizmu kolejek (Magento Message Queue Framework, Shopware Symfony Messenger).

Idempotencja - must-have

Idempotencja = wykonanie tej samej operacji wielokrotnie ma ten sam skutek co wykonanie jednokrotne.

Dlaczego ważne: retry. Jeśli operacja padnie, kolejka spróbuje ponownie. Jeśli operacja nie jest idempotentna - przy retry stworzysz duplikat.

Przykład złej praktyki:

// ZŁY KOD: każde wywołanie tworzy nowy dokument w ERP
$erp->createOrder($order);

Przykład dobrej praktyki:

// DOBRY KOD: external_reference jako idempotency key
$externalRef = 'WEB-' . $order->getId();
$existing = $erp->findOrderByReference($externalRef);
if ($existing) {
    return $existing;  // już istnieje, nic nie rób
}
$erp->createOrder($order, externalReference: $externalRef);

Idempotency keys w praktyce:

  • Każde zdarzenie ma unikalny ID (UUID lub combo sklep_id + entity_id)
  • Consumer sprawdza czy już przetworzył ten ID
  • Jeśli tak - skip, oznaczy jako sukces
  • Jeśli nie - przetwórz, zapisz processed_event_id w tabeli idempotency

Retry i Dead Letter Queue

Retry z exponential backoff:

Message padnie? Nie próbuj natychmiast. Próbuj po: 1s, 2s, 4s, 8s, 16s, 32s, 60s. Razem 8 prób w okolicach 2 minut.

Powód: jeśli ERP padł - natychmiastowy retry nic nie da. Exponential backoff daje ERP-owi czas na powrót.

W Symfony Messenger:

framework:
  messenger:
    transports:
      erp_sync:
        dsn: 'amqp://...'
        retry_strategy:
          max_retries: 8
          delay: 1000        # 1s
          multiplier: 2      # 2x każdy retry
          max_delay: 60000   # max 60s

Dead Letter Queue (DLQ):

Po wyczerpaniu retry - message ląduje w DLQ. To kolejka „wszystko poszło źle, zobacz".

DLQ to alarm. Każda wiadomość w DLQ to incydent do zbadania. Bez monitorowania DLQ - wiadomości giną.

Monitoring i alerty

Kolejka bez monitoringu = bomba zegarowa.

Co monitorować:

  1. Liczba wiadomości w kolejce - rośnie? Consumer nie nadąża, problem.
  2. Liczba wiadomości w DLQ - > 0 = alarm.
  3. Czas oczekiwania message (age) - wiadomość czeka 30 minut zamiast 30 sekund = problem.
  4. Consumer health - czy procesy konsumentów działają (Supervisor, systemd, k8s pods).
  5. Throughput - ile messages / sec przetwarzane.

Narzędzia:

  • RabbitMQ Management Plugin (UI)
  • Prometheus + Grafana
  • Datadog APM
  • New Relic

Alerty:

  • DLQ > 0 → ticket / pager
  • Queue depth > 10000 → warning
  • Consumer down > 5 min → pager

Workery - jak je uruchamiać

Workery to procesy, które konsumują wiadomości z kolejek. Musi być coś, co je uruchamia i restartuje przy awariach.

Opcje:

1. Supervisor (Linux service manager).

Klasyczny wybór dla VPS / dedykowanych. Konfiguracja w INI, restart przy awarii, zarządzanie logami.

[program:order-sync-worker]
command=php bin/console messenger:consume erp_sync --time-limit=3600 --memory-limit=512M
process_name=%(program_name)s_%(process_num)02d
numprocs=4
autostart=true
autorestart=true

2. systemd.

Nowsze, w nowoczesnych Linuxach. Podobne do Supervisor.

3. Kubernetes Pods.

Każdy worker jako pod, replicas konfigurowalne, autoscaling. Dla projektów na K8s.

4. AWS ECS / Fargate.

Managed, dla AWS.

Liczba workerów:

Reguła: jeden worker per kolejka per CPU rdzeń. Czyli na 8-rdzeniowej maszynie z 4 kolejkami: 2 workery per kolejka.

Skalowanie: gdy queue depth rośnie - dodaj workerów. Gdy maleje - zmniejsz.

Najczęstsze błędy

1. Brak idempotencji. Retry → duplikaty.

2. Brak DLQ. Wiadomości giną cicho.

3. Synchronous calls w workerze. Worker czeka 30s na response z ERP, blokuje kolejkę.

4. Brak time limit / memory limit workerów. Workery puchną w pamięci, OOM po dniu.

5. Nieobsługiwane exceptions. Wyjątek nie złapany → worker crash → message zgubione.

6. Zbyt duże messages. Wpychanie całych obiektów do queue. Lepiej entity_id, dane pobiera worker.

FAQ

Czy mogę używać tylko cron'a zamiast kolejek? Cron działa dla batch'owych synchronizacji (co X minut). Nie działa dla zdarzeń real-time (zamówienie złożone teraz musi iść do ERP teraz). Dla typowego sklepu B2B kolejki są krytyczne.

Czy Magento ma własne kolejki? Tak, Magento Message Queue Framework (oparty na RabbitMQ lub MySQL). Dla custom integracji można i powinno się go używać.

Co z Shopware? Shopware używa Symfony Messenger. Standardowy stack PHP.

Kafka czy RabbitMQ? Dla większości polskich B2B średniej skali - RabbitMQ. Kafka dla bardzo dużych skala (50k+ events/sec) i event sourcing.

Czy potrzebuję managed RabbitMQ (CloudAMQP) czy self-hosted? Dla małych projektów self-hosted OK. Powyżej 100k messages/dziennie - managed jest spokojniej (HA, monitoring, backups).

Co dalej

O autorze

Jakub Owsianka

Architekt rozwiązania w WiseB2B - silniku platform B2B. Zaczynał po stronie biznesu (własne sklepy), potem deweloper, dziś projektuje wdrożenia dla sklepów z katalogami w dziesiątkach tysięcy SKU. W ostatnich latach wdrożył AI-development w zespole i funkcjonalności oparte o AI bezpośrednio w silniku sklepu.

Masz pytanie do tego artykułu?

Dodatkowy kontekst, problem z własnym wdrożeniem, druga opinia - napisz wprost. Odpowiadam osobiście w 1-2 dni robocze.