RabbitMQ - курсv4.3.0

RabbitMQ 4.3

для Senior PHP backend (Laravel/Symfony) + базовий DevOps
Brokers, exchanges, queues, bindings, channels - від основ AMQP 0-9-1 до production-ready кластера. Кожен крок з посиланням на офіційне джерело.
/ - наступний / попередній слайд  |  / / Space - скрол всередині слайда
Home / End - перший / останній  |  T - зміст і закладки  |  B - закладка  |  Esc - закрити
~12 годин матеріалу  ·  6 розділів
map

Карта курсу

Шість розділів, від примітивів AMQP до моніторингу і production checklist. Час орієнтовний.

01
Основи та оточення
Що таке RabbitMQ, AMQP 0-9-1, primitives (exchange/queue/binding), 4 типи exchange, sandbox у Docker, management UI.
≈ 1.5 год
02
Базові патерни обміну
5 офіційних RabbitMQ tutorials на PHP: Hello World, Work Queues, Pub/Sub, Routing, Topics.
≈ 2.5 год
03
Reliability
Publisher Confirms, Consumer Acks, Dead Letter Exchange, retry-патерни, idempotency.
≈ 2.0 год
04
Quorum Queues і Streams
Replicated FIFO на Raft, append-only логи з offset-based consume, replay.
≈ 1.5 год
05
Інтеграція з PHP
php-amqplib, Symfony Messenger, Laravel Queue + RabbitMQ. Коли який інструмент.
≈ 2.5 год
06
DevOps базовий
3-нодний кластер (Khepri), моніторинг через Prometheus, production checklist.
≈ 2.0 год
Версія матеріалу: RabbitMQ 4.3.0 (latest stable) Перевірено: 2026-05-09 Джерело release: github.com/rabbitmq/rabbitmq-server v4.3.0
РОЗДІЛ 01 · ≈ 1.5 ГОД

Основи та оточення

Розуміти, навіщо RabbitMQ, його архітектурну модель і primitives. Підняти sandbox у Docker і зайти у management UI.

1.1.1

Що таке RabbitMQ

Цитата з офіційної головної сторінки:

a powerful, enterprise grade open source messaging and streaming broker that enables efficient, reliable and versatile communication for applications

Дві ролі в одному продукті

Позиціонування з тієї ж сторінки: "ideal for distributed microservices, real-time data, and IoT".

Джерело: rabbitmq.com Версія: 4.3.0 Перевірено: 2026-05-09
1.1.1a

AMQP - розшифровка

AMQP = Advanced Message Queuing Protocol.

Wire-level vs API-level

AMQP - це wire-level протокол (бінарний, по TCP), а не API. Wire-level означає, що специфікація фіксує точний формат байтів, які летять по мережі ("over the wire"): фрейми, opcodes (коди операцій), кодування типів.

Контраст - API-level (як JDBC, PDO): описує функції бібліотеки, а як саме воно йде по мережі - справа драйвера.

Наслідок: php-amqplib (PHP), pika (Python), amqplib (Node.js) гарантовано говорять з тим самим RabbitMQ - бо специфікація фіксує байти на дроті, а не API клієнта.

Дві несумісні версії в одному broker'і

AMQP 0-9-1 is a messaging protocol that enables conforming client applications to communicate with conforming messaging middleware brokers
Джерело: rabbitmq.com/tutorials/amqp-concepts Версія: 4.3.0 Перевірено: 2026-05-09
1.1.2

Use cases

Чотири канонічні сценарії з офіційної головної (rabbitmq.com).

СценарійЩо означає термінПриклад з сайту
Decoupling services Decoupling = розчеплення: producer (відправник) не знає про consumer (отримувач), broker буферизує. Consumer лежить - producer все одно пише. один event → email + push
RPC RPC = Remote Procedure Call (віддалений виклик процедури). Producer шле запит у чергу, чекає відповідь у reply-черзі. Синхронний сценарій поверх асинхронного брокера. обробка замовлень білетів з кількох каналів продажу
Streaming Append-only лог з offset (зміщенням), повідомлення НЕ видаляється після прочитання, новий consumer може зробити replay (перечитати з минулого). відеоплатформа: post-upload analysis, transcode, notify
IoT IoT = Internet of Things. Пристрої з нестабільним зв'язком. Брокер буферизує телеметрію поки пристрій offline. status reports з розподілених пристроїв
Загальний знаменник: producer і consumer не зв'язані ні в часі, ні в просторі, ні в кількості (один event може йти багатьом отримувачам, або навпаки).
Джерело: rabbitmq.com Версія: 4.3.0 Перевірено: 2026-05-09
1.1.3

Місце в стеку: producer → broker → consumer

messaging brokers receive messages from publishers and route them to consumers
Producer PHP-сервіс basic_publish() publish RabbitMQ Broker + буфер (черга) + маршрутизація + durability (диск) + ack-tracking producer/consumer not coupled deliver Consumer PHP worker basic_consume()

Що дає брокер посередині

Важливо для AMQP 0-9-1: producer НЕ публікує напряму в чергу. Він публікує в exchange, і вже exchange за правилами bindings вирішує, у які черги покласти. Це - тема 1.2.
Джерело: rabbitmq.com/tutorials/amqp-concepts Версія: 4.3.0 Перевірено: 2026-05-09
РОЗДІЛ 01.2

Архітектура AMQP 0-9-1

Сутності протоколу та як вони складаються у систему доставки повідомлень. Producer → exchange → binding → queue → consumer.

1.2.1

Primitives: exchange, queue, binding

СутністьЦитата з документації
Exchange "AMQP 0-9-1 entities where messages are sent to." Producer публікує завжди в exchange (не в queue). Exchange "take a message and route it into zero or more queues".
Queue "store messages that are consumed by applications". Місце, де повідомлення лежить до прочитання.
Binding "rules that exchanges use to route messages to queues". Щоб exchange E доставив у queue Q, треба зробити Q bound to E. Binding може мати routing key - рядок, за яким exchange вирішує куди класти.
Producer publish(...) EXCHANGE "orders" JFK airport binding binding queue A → consumer 1 queue B → consumer 2 "zero or more"
Queue - це твій пункт призначення в Нью-Йорку. Exchange - аеропорт JFK. Bindings - маршрути від JFK до пункту призначення. (офіц. аналогія)
Джерело: rabbitmq.com/tutorials/amqp-concepts Версія: 4.3.0 Перевірено: 2026-05-09
1.2.1a

Queue - properties (прапори при declare)

Queue - не "просто черга". При queue_declare задається 4 незалежних прапори, які визначають lifecycle і поведінку.

ПрапорЦитата з документаціїПрактичне значення
durable "the queue will survive a broker restart" Метадата queue зберігається на диск. Без durable - queue зникає після рестарту broker'а. Окремо від цього - повідомлення мають мати delivery_mode=2 (persistent), див. 2.2.4.
exclusive "used by only one connection and the queue will be deleted when that connection closes" Прив'язана до конкретного connection-а. Інший connection не може ні читати, ні declare ту саму queue. Closed connection → queue знесено. Use case: temporary subscribers (Pub/Sub - 2.3), RPC reply queues.
auto-delete "queue that has had at least one consumer is deleted when last consumer unsubscribes" Тригер видалення - останній consumer відписався. Якщо ніколи не було consumer'а - не видалиться. Не плутати з exclusive (там тригер - close connection).
passive (не в публ. summary, але стандарт AMQP 0-9-1) "Тільки перевір, чи queue існує". Не створює, не змінює. Якщо queue нема - помилка NOT_FOUND. Корисно для validate-перед-publish без побічних ефектів.

Як вони комбінуються

Use casedurableexclusiveauto-delete
Production work queue (orders)
Pub/Sub temporary subscriber(implicit при exclusive)
RPC reply queue(implicit)
Дешбоард, що сам зникає коли всі дашбоарди закрилися
Зміна 4.3: non-durable + non-exclusive queues відхиляються за замовчуванням. Якщо потрібно (legacy code) - deprecated_features.permit.transient_nonexcl_queues = true у rabbitmq.conf. Стимулює перехід на durable + quorum.
Прапори - immutable після створення. Не можна "конвертувати" non-durable в durable. Треба видалити і створити заново.
Джерело: rabbitmq.com/docs/queues Версія: 4.3.0 Перевірено: 2026-05-10
1.2.1b

Queue - три типи у 4.x

Окрім прапорів, у 4.x queue має ще тип - спосіб реалізації. Задається при declare через x-queue-type argument. Default історично - classic, але production-стандарт у 4.x - quorum.

ТипЩо цеКоли
classic Single-node queue. Зберігається на одній ноді кластера. Найшвидша, найдешевша. Transient subscribers (exclusive auto-delete), RPC reply queues, dev/sandbox. НЕ для production-критичних work queues - падіння ноди = втрата queue.
quorum "replicated, data safety and consistency-oriented queue type". Реплікація на 3+ ноди через Raft консенсус. Завжди durable. Production work queues (orders, payments, ETL). Default-вибір у 4.x для будь-чого важливого.
stream "immutable append-only log" з offset-based consumption. Повідомлення НЕ видаляється після прочитання. Event sourcing, audit log, fan-out з replay для нових consumer'ів. Великі backlogs. Розділ 4.2.

Як задавати тип

$channel->queue_declare(
    'orders', false, true, false, false, false,
    new AMQPTable(['x-queue-type' => 'quorum'])  // ← обов'язково для production
);

Базові queue arguments (огляд, deep-dive у пізніших розділах)

ArgumentЩо робитьДеталі
x-queue-typeТип (classic / quorum / stream).1.2.1b (тут), 4.1, 4.2
x-message-ttlTTL для всіх повідомлень у queue, ms.3.3.2 (TTL + DLX retry)
x-max-lengthЛіміт по кількості повідомлень.з x-overflow - reject-publish, drop-head
x-dead-letter-exchangeКуди слати rejected/expired.3.3.1, 3.3.2 (DLX)
x-delivery-limitСкільки разів re-queue до відмови (тільки quorum).3.3.1, 4.1.3
FIFO ordering: за замовчуванням "messages are enqueued and dequeued in a FIFO manner". Ламається при: priorities (вищий priority обходить FIFO), redelivery ("any redelivery can change order"), кілька consumer'ів з різною швидкістю обробки.
Джерела: queues · quorum-queues Версія: 4.3.0 Перевірено: 2026-05-10
1.2.2

Routing key

Routing key (ключ маршрутизації) - рядок, який producer додає до кожного publish. Адресна "етикетка" повідомлення.

The purpose of the routing key is to select certain messages published to an exchange to be routed to the bound queue. In other words, the routing key acts like a filter.

Хто його ставить

Як трактується різними типами exchange

ExchangeЩо робить з routing key
directТочна рівність: доставка тільки в queues, де binding_key == routing_key.
fanout"the routing key is ignored" - повністю ігнорується, доставка в усі bound queues.
topicPattern matching: routing key матчиться проти binding patterns з wildcards * (одне слово) і # (нуль і більше слів). Приклад: order.paid.eu матчить order.*.eu і order.#.
headers"Headers exchanges ignore the routing key attribute". Маршрутизація через message headers.
Конвенція для topic-exchange: слова через крапку, <entity>.<action>.<region>user.created.eu. Це не вимога специфікації - це прийнята практика. Довжина routing key на wire-рівні - до 255 байт (shortstr).
Джерело: rabbitmq.com/tutorials/amqp-concepts Версія: 4.3.0 Перевірено: 2026-05-09
1.2.3

Direct exchange

A direct exchange delivers messages to queues based on the message routing key. A direct exchange is ideal for the unicast routing of messages.

Unicast (одноадресна доставка) - "одне повідомлення йде в одну конкретну точку". Контраст: broadcast (всім - радіо) - це fanout; multicast (кільком конкретним) - підмножина.

Алгоритм

З документації: "When a new message with routing key R arrives at the direct exchange, the exchange routes it to the queue if K = R", де K - binding key. Якщо в кількох queues однаковий K - всі отримають копію.

publish(exchange="orders", routing_key="paid") DIRECT exchange "orders" K=paid ✓ K=created ✗ K=paid ✓ queue paid отримує queue created не отримує queue paid_audit отримує (multicast)

Default exchange (нюанс)

Default exchange - "a direct exchange with no name (empty string) pre-declared by the broker". Broker автоматично робить binding від default exchange до кожної queue з binding key = ім'ям queue. Тому publish("", "my_queue", body) виглядає як пряма публікація в queue, але технічно йде через default direct exchange. Цей трюк - в "Hello World" tutorial.
Джерело: rabbitmq.com/tutorials/amqp-concepts Версія: 4.3.0 Перевірено: 2026-05-09
1.2.4

Fanout exchange

ideal for the broadcast routing of messages

Алгоритм: fanout "routes messages to all of the queues that are bound to it and the routing key is ignored". При publish "a copy of the message is delivered to all N queues".

publish(exchange="events", routing_key="будь-що, ігнорується") FANOUT exchange "events" queue email → consumer 1 queue analytics → consumer 2 queue crm → consumer 3 queue audit → consumer 4

Use cases (з документації)

Якщо в exchange нема binding'ів - повідомлення дропається мовчки (правило "zero or more queues"). Часта пастка: producer публікує, ще ніхто не підписався (наприклад при першому деплої consumer'а), повідомлення зникають.
Джерело: rabbitmq.com/tutorials/amqp-concepts Версія: 4.3.0 Перевірено: 2026-05-09
1.2.5

Topic exchange

Topic exchanges route messages to one or many queues based on matching between a message routing key and the pattern that was used to bind a queue to an exchange.

Жорстка вимога до routing key (з tutorial-five-php): "it must be a list of words, delimited by dots". Довжина: до 255 байт.

Wildcards (підстановочні символи у binding key)

Приклади matching

Binding patternЗбігаєтьсяНе збігається
*.orange.*quick.orange.rabbit, lazy.orange.elephantorange, quick.orange.new.rabbit
*.*.rabbitquick.orange.rabbit, lazy.brown.rabbitquick.brown.fox
lazy.#lazy, lazy.brown.fox, lazy.pink.rabbit.malequick.brown.fox
Виродження topic у direct/fanout: binding # поводиться як fanout (матчить усе); binding без * і # поводиться як direct (точна рівність). Topic - надмножина direct і fanout за виразністю.

Use cases

Джерела: amqp-concepts · tutorial-five-php Версія: 4.3.0 Перевірено: 2026-05-09
1.2.6

Headers exchange

designed for routing on multiple attributes that are more easily expressed as message headers than a routing key

Як працює

Аргумент binding'а x-match

publish(exchange="docs", headers={format: "pdf", region: "eu"}) HEADERS exchange "docs" x-match=all {format=pdf, region=eu} x-match=any {format=pdf} queue eu_pdf_processor всі headers збіглись ✓ queue any_pdf format збігся ✓
"Headers exchanges can be looked upon as 'direct exchanges on steroids'". Значення headers можуть бути не лише рядки - integers, hashes (асоціативні масиви), складні структури. Routing key - тільки рядок.
Headers повільніший за direct/topic - matching іде порівнянням мап, не хешем routing_key. У типових backend-задачах непомітно, але на високих rates - аргумент проти.
Джерело: rabbitmq.com/tutorials/amqp-concepts Версія: 4.3.0 Перевірено: 2026-05-09
1.2.7

Connection vs Channel

Connection (з'єднання)

Channel (канал)

AMQP 0-9-1 connections are multiplexed with channels that can be thought of as 'lightweight connections that share a single TCP connection'

Multiplexing (мультиплексування) - "кілька логічних потоків живуть в одному фізичному каналі зв'язку, кожен зі своїм id". Один TCP - багато паралельних "логічних з'єднань".

Застосунок PHP worker $connection 1 TCP connection (long-lived) ch#1 publish "orders" ch#2 consume "tasks" ch#3 declare queues ... тисячі channels RabbitMQ broker

Чому через channels, а не багато connections

it is undesirable to keep many TCP connections open at the same time because doing so consumes system resources and makes it more difficult to configure firewalls

TCP - дорогий ресурс (file descriptors, kernel-стан, TLS-handshake, NAT/firewall). Channels - дешеві логічні id, тисячі на одне з'єднання.

Best practice: "open a new channel per thread/process and not share channels between them". Channel не thread-safe.
Поширена пастка: код, що відкриває нову Connection на КОЖНЕ повідомлення. Кожен publish = новий TCP-handshake + auth = latency 5-50ms на ровному місці. Правильно: connection - на старті процесу, тримати, channel - для publish.
Джерело: rabbitmq.com/tutorials/amqp-concepts Версія: 4.3.0 Перевірено: 2026-05-09
РОЗДІЛ 01.3

Підняти sandbox у Docker

Compose-файл, запуск, перевірка readiness. Сюди відкриваєш management UI у наступному 1.4.

1.3.1

Розбір sandbox/compose.yaml

services:
  rabbitmq:
    image: rabbitmq:4.3-management
    container_name: rabbitmq-study
    hostname: rabbitmq-study
    ports:
      - "5672:5672"     # AMQP 0-9-1 / 1.0
      - "15672:15672"   # management UI + HTTP API
      - "15692:15692"   # Prometheus metrics
    environment:
      RABBITMQ_DEFAULT_USER: guest
      RABBITMQ_DEFAULT_PASS: guest
      RABBITMQ_DEFAULT_VHOST: /
    volumes:
      - rabbitmq-data:/var/lib/rabbitmq
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s
    restart: unless-stopped

volumes:
  rabbitmq-data:
Env-змінні (RABBITMQ_DEFAULT_*) спрацьовують тільки при першому запуску, коли data-volume порожній. Подальша зміна env нічого не дасть - credentials уже в Khepri-стані.
Джерела: hub.docker.com/_/rabbitmq · docs.docker.com/compose Версія: 4.3.0 Перевірено: 2026-05-09
1.3.2

Запуск sandbox

Команди

cd /home/serhii/study/courses/rabbitmq/sandbox

# Pull image (~250MB) і запуск у detached mode
docker compose up -d

# Перевірити стан контейнера
docker compose ps

# Лог-стрім (Ctrl+C для виходу, контейнер працює далі)
docker compose logs -f rabbitmq

Очікуваний вивід docker compose ps

NAME             IMAGE                     COMMAND                   STATUS
rabbitmq-study   rabbitmq:4.3-management   "docker-entrypoint.s…"    Up 35s (healthy)

Що відбувається при першому запуску

  1. Docker pull rabbitmq:4.3-management з Docker Hub.
  2. Створюється named volume rabbitmq-data (порожній).
  3. Контейнер стартує, entrypoint застосовує env-змінні (створює user guest, vhost /).
  4. Erlang VM піднімає broker, ініціалізує Khepri-store на диску.
  5. Healthcheck після 30 с (start_period) починає викликати rabbitmq-diagnostics ping.
  6. Коли ping проходить - status стає healthy.

Як зупинити / перезапустити

docker compose stop          # стоп без видалення контейнера
docker compose start         # старт існуючого контейнера
docker compose restart       # alias

docker compose down          # стоп + видалення контейнера (volume лишається!)
docker compose down -v       # ⚠ + видалення volume - стирає УСІ дані
Джерело: docs.docker.com/compose CLI Версія: Docker Compose v2 Перевірено: 2026-05-09
1.3.3

Перевірка readiness: rabbitmq-diagnostics ping

rabbitmq-diagnostics - офіційний CLI broker'а. Команда ping перевіряє, що нода відповідає на Erlang-distribution rpc-виклик.

docker compose exec rabbitmq rabbitmq-diagnostics -q ping
# Pong

Прапор -q (quiet) виводить тільки результат без banner'а. Exit code 0 = ping OK.

Інші діагностичні команди (часто стають у нагоді)

# статус ноди (uptime, memory, queues, consumers)
docker compose exec rabbitmq rabbitmq-diagnostics status

# coverage check всіх listeners (порти 5672, 15672, тощо)
docker compose exec rabbitmq rabbitmq-diagnostics check_port_listener 5672

# health check (комплексний - listeners + ack-tracking + alarms)
docker compose exec rabbitmq rabbitmq-diagnostics -q check_running

# overview через CLI rabbitmqctl
docker compose exec rabbitmq rabbitmqctl list_queues
docker compose exec rabbitmq rabbitmqctl list_exchanges
docker compose exec rabbitmq rabbitmqctl list_bindings
Healthcheck в compose.yaml саме rabbitmq-diagnostics -q ping. Docker викликає його сам - тому docker compose ps показує status healthy або unhealthy.
Джерело: rabbitmq.com/docs/cli Версія: 4.3.0 Перевірено: 2026-05-09
1.3.4

Порти і їх призначення

RabbitMQ - мульти-протокольний broker. Кожен протокол слухає на окремому порту. У нашому sandbox експонуємо три з них.

ПортПротоколЩо там
5672AMQP 0-9-1 / 1.0Основний канал для PHP-клієнтів (php-amqplib, ext-amqp). Producer і consumer connect'яться сюди.
15672HTTP (Management UI + API)Web-інтерфейс і REST API: /api/queues, /api/overview, /api/exchanges. Включений тільки в *-management tag.
15692HTTP (Prometheus)Endpoint /metrics у форматі Prometheus exposition. Включений у плагіні rabbitmq_prometheus (built-in у дистрибутиві з 3.8+).
5552RabbitMQ Streams (native)Stream protocol для high-throughput append-only. У sandbox НЕ експонуємо - використаємо AMQP 0-9-1 access до streams.
1883 / 8883MQTT (з/без TLS)Поза скоупом курсу.
61613 / 61614STOMPПоза скоупом курсу.
4369epmd (Erlang Port Mapper)Discovery для cluster-формування. У single-node sandbox не експонуємо.
25672Inter-node Erlang distributionМіж нодами кластера. У single-node sandbox не експонуємо.
Перевірити які порти реально слухає нода:
docker compose exec rabbitmq rabbitmq-diagnostics listeners
Джерело: rabbitmq.com/docs/networking#ports Версія: 4.3.0 Перевірено: 2026-05-09
РОЗДІЛ 01.4

Management UI: огляд

Web-інтерфейс RabbitMQ - читай queues, оголошуй exchanges, дивись live-метрики. Доступний на http://localhost:15672.

1.4.1

Доступ до management UI

URL і credentials

a user named guest with a default password of guest, granted full access to the / virtual host

Обмеження guest user'а

Цитата з access-control: "By default, the guest user is prohibited from connecting from remote hosts; it can only connect over a loopback interface (localhost)".

З не-localhost log на broker'і покаже:

PLAIN login refused: user 'guest' can only connect via localhost

Чому це важливо. Якщо ви розгортаєте sandbox на віддаленому сервері і не можете залогінитись через UI - це не баг, це security default. Рішення:

  1. Створити нового користувача з паролем (production-style):
docker compose exec rabbitmq rabbitmqctl add_user me <strong-password>
docker compose exec rabbitmq rabbitmqctl set_user_tags me administrator
docker compose exec rabbitmq rabbitmqctl set_permissions -p / me ".*" ".*" ".*"
  1. Або (тільки для dev) - дозволити guest з remote: env-змінна RABBITMQ_LOOPBACK_USERS="" у compose.yaml. Не для production.
У нашому sandbox броker і браузер на тому ж хості (localhost), тому guest/guest працює без ніяких змін.
Джерела: access-control · management Версія: 4.3.0 Перевірено: 2026-05-09
1.4.2

Можливості management UI

Цитати з офіційної сторінки docs/management. Згруповано за функцією.

Адміністрування

Моніторинг

Дані

Стандартні tabs у UI (станом на 4.x)

Джерело: rabbitmq.com/docs/management Версія: 4.3.0 Перевірено: 2026-05-09
1.4.3

Hands-on: створити exchange + queue + binding

Мета: побачити primitives з 1.2 у живому UI і отримати feel за них. Без коду.

Крок 1: declare exchange

  1. Exchanges tab → Add a new exchange.
  2. Name: demo.events. Type: fanout. Durable: ✓. Auto delete: ✗.
  3. Add exchange. Має з'явитись у списку.

Крок 2: declare queues

  1. Queues tab → Add a new queue.
  2. Type: Classic (для цього прикладу - але production-default уже Quorum; туди прийдемо в розділі 4).
  3. Name: demo.email. Durable. Add queue.
  4. Повторити для demo.analytics.

Крок 3: bind queues to exchange

  1. Відкрити exchange demo.events → секція Bindings → Add binding from this exchange.
  2. To queue: demo.email. Routing key порожній (fanout ігнорує). Bind.
  3. Повторити для demo.analytics.

Крок 4: publish тестове повідомлення

  1. На сторінці exchange demo.events → секція Publish message.
  2. Routing key: будь-який (ігнорується для fanout). Payload: {"event": "user_signed_up"}. Publish.
  3. UI покаже "Message published".

Крок 5: подивитися де воно

  1. Queues tab → видно що demo.email і demo.analytics мають Ready: 1 (по копії в кожній - типовий fanout).
  2. Відкрити queue → Get messages → Get Message(s). Побачити payload.
Це precisely те, що в коді робить consumer через basic_get або basic_consume. UI - просто HTTP-обгортка над тим самим API.
Через UI не варто робити boilerplate-топологію в production. Topology має оголошуватись із застосунку (idempotent queue_declare, exchange_declare, queue_bind) - тоді деплой нового сервісу автоматично створює свої queues і bindings.
Джерело: rabbitmq.com/docs/management Версія: 4.3.0 Перевірено: 2026-05-09
1.4.4

Hands-on: побачити queue properties у дії

Закріплення 1.2.1a (properties) і 1.2.1b (types) на живому broker'і. Усі дії - через management UI на http://localhost:15672.

Крок 1: оголосити три queues з різними прапорами

На Queues and Streams tab → Add a new queue. Створи по черзі:

NameTypeDurableAuto-deleteArguments
demo.productionQuorum(implicit ✓)(empty)
demo.transientClassic(empty)
demo.boundedQuorum(implicit ✓)x-max-length=5, x-overflow=reject-publish

Важливо: для demo.transient (classic + non-durable + auto-delete без exclusive) можеш отримати помилку "queue declaration not allowed" у 4.3 - це той самий deprecated_features з 1.2.1a. Якщо так - просто пропусти або змінь на classic + durable.

Крок 2: подивись що видно у UI

Відкрий кожну queue по черзі. У секції Details:

Крок 3: тест auto-delete

  1. Створи fanout-exchange demo.test і binding'и до всіх трьох queues.
  2. Publish тестове повідомлення з UI у demo.test exchange. Усі три queues отримують копію (depth = 1).
  3. Відкрий demo.transientGet messages → Get Message(s) (натисни кілька разів).
  4. Це не симулює "consumer subscribed/unsubscribed" - get-mode не лічиться. Auto-delete не спрацює.
  5. Щоб реально побачити auto-delete - запусти PHP basic_consume на цю queue (з 2.1), потім Ctrl+C. Queue зникне з UI.

Крок 4: тест x-max-length + reject-publish

  1. На сторінці demo.bounded exchange → Publish message. Опублікуй 6 повідомлень підряд.
  2. Перші 5 - депт стає 5.
  3. 6-те (без publisher confirms) - тихо дропнеться. З publisher confirms - повернеться basic.nack.
  4. Перевір у demo.bounded → Messages = 5, не більше. Це і є back-pressure про який питав вище.

Cleanup

docker compose exec rabbitmq rabbitmqctl delete_queue demo.production
docker compose exec rabbitmq rabbitmqctl delete_queue demo.bounded
# demo.transient або зникне сама, або:
docker compose exec rabbitmq rabbitmqctl delete_queue demo.transient
docker compose exec rabbitmq rabbitmqctl delete_exchange demo.test
Це останній UI-крок розділу 1. Далі (Розділ 2) - те саме, але через PHP-код. UI лишиться інструментом для diagnostics, не для declare-у production-топології.
Джерела: queues · management · maxlength Версія: 4.3.0 Перевірено: 2026-05-10
РОЗДІЛ 02 · ≈ 2.5 ГОД

Базові патерни обміну

П'ять офіційних RabbitMQ tutorials на PHP. Клієнт - php-amqplib v3.7.4. Кожен tutorial демонструє один тип exchange або одну concurrency-властивість.

2.1.1

"Hello World" - producer (send.php)

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->queue_declare(
    'hello',                              // name
    false,                                // passive
    true,                                 // durable
    false,                                // exclusive
    false,                                // auto_delete
    false,                                // nowait
    new AMQPTable(['x-queue-type' => 'quorum'])  // arguments
);

$msg = new AMQPMessage('Hello World!');
$channel->basic_publish($msg, '', 'hello');   // exchange="", routing_key="hello"
echo " [x] Sent 'Hello World!'\n";

$channel->close();
$connection->close();
Дві ключові деталі:
  • basic_publish($msg, '', 'hello') - порожній exchange = default direct exchange. Routing key = ім'я queue. Це єдиний випадок, коли publish "виглядає" як прямий запис у queue (див. 1.2.3).
  • x-queue-type: quorum - офіц. tutorial 2026 використовує quorum-черги за дефолтом замість class. (Розділ 4 - чому).
Джерело: tutorial-one-php Клієнт: php-amqplib v3.7.4 Перевірено: 2026-05-09
2.1.2

"Hello World" - consumer (receive.php)

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->queue_declare('hello', false, true, false, false, false,
    new AMQPTable(['x-queue-type' => 'quorum']));

echo " [*] Waiting for messages. To exit press CTRL+C\n";

$callback = function (AMQPMessage $msg) {
    echo ' [x] Received ', $msg->getBody(), "\n";
};

$channel->basic_consume(
    'hello',     // queue
    '',          // consumer_tag (auto)
    false,       // no_local
    true,        // no_ack ⚠ auto-ack (продакшн: false)
    false,       // exclusive
    false,       // nowait
    $callback    // callback
);

try {
    $channel->consume();
} catch (\Throwable $e) {
    echo $e->getMessage();
}
У "Hello World" no_ack=true для простоти. У production - завжди manual ack (no_ack=false), інакше повідомлення вважається доставленим у ту саму секунду, як broker його відправив - і губиться, якщо consumer впав під час обробки. Тема 2.2.
Idempotent declare: producer і consumer обидва оголошують queue з ОДНАКОВИМИ параметрами. Хто перший підняв - створив; другий - знайшов наявну. Топологію не треба готувати наперед.
Джерело: tutorial-one-php Перевірено: 2026-05-09
РОЗДІЛ 02.2

Work Queues

Розподіл важких задач між кількома worker'ами. Round-robin dispatch + ack + prefetch + durability.

2.2.1

Round-robin dispatch

Кілька consumer'ів підписуються на ту саму queue → broker роздає повідомлення по черзі.

RabbitMQ will send each message to the next consumer, in sequence.
Producer queue "tasks" m1 m2 m3 m4 m1, m4 ... m2, ... m3, ... worker 1 worker 2 worker 3

Code не потребує спецналаштувань - просто запусти кілька receive.php з тим самим queue.

Round-robin сліпий: broker не знає, чи worker зайнятий важкою задачею. Якщо m1 - "оброби 4ГБ відео", а m2-m4 - "відправ email", round-robin може засипати worker'а 1 важкими задачами. Лікується fair dispatch (2.2.3).
Джерело: tutorial-two-php Перевірено: 2026-05-09
2.2.2

Manual acknowledgement

Ack (acknowledgement, підтвердження) - сигнал від consumer'а до broker'а: "повідомлення оброблено успішно, можна стерти". Broker тримає повідомлення in-flight (на льоту) до отримання ack.

$callback = function (AMQPMessage $msg) {
    echo ' [x] Received ', $msg->getBody(), "\n";
    sleep(substr_count($msg->getBody(), '.')); // імітація роботи
    echo " [x] Done\n";
    $msg->ack();   // ← підтвердження після успішної обробки
};

$channel->basic_consume(
    'task_queue',
    '',
    false,
    false,    // no_ack=false → manual ack обов'язковий
    false,
    false,
    $callback
);

Що буває без ack

Три способи відмови від обробки

МетодЩо робитьКоли вживати
$msg->ack()Підтвердити, broker стирає.Успішна обробка.
$msg->nack(requeue: true)Відкинути + повернути в queue.Тимчасова помилка (DB недоступна, retry може допомогти).
$msg->reject(requeue: false)Відкинути назавжди (→ DLX, якщо налаштовано).Принципово невалідне повідомлення (poison message).
У RabbitMQ 4.x є consumer acknowledgement timeout - default 30 хв. Якщо worker мовчить 30+ хв (не ack/nack), broker закриває channel з помилкою delivery acknowledgement timeout. Розділ 3.2 - як налаштувати.
Джерела: tutorial-two-php · confirms Перевірено: 2026-05-09
2.2.3

Fair dispatch (basic_qos + prefetch_count)

Round-robin сліпий. Рішення: сказати broker'у "не давай worker'у наступного, поки попереднє не ack'ed".

$channel->basic_qos(
    null,    // prefetch_size (bytes; null = unlimited)
    1,       // prefetch_count = скільки in-flight на consumer'а
    false    // global (per-channel vs per-consumer)
);
don't dispatch a new message to a worker until it has processed and acknowledged the previous one.

Як це змінює поведінку

prefetch - це back-pressure механізм (зворотний тиск). Worker не отримує більше, ніж може прожувати. Без prefetch broker pipe'ає в socket worker'а сотні повідомлень - вони сидять у TCP-буфері, неack'ed, і якщо worker впав - всі rе-queue'ються одночасно.

Залежність від worker speed

Час обробкиРекомендоване prefetch
> 1 сек1 - 5
10-100 мс20 - 50
< 1 мс200 - 1000
Джерела: tutorial-two-php · consumer-prefetch Перевірено: 2026-05-09
2.2.4

Durability: durable queue + persistent message

Дві НЕЗАЛЕЖНІ опції, які мусять бути ОБИДВІ ввімкнені для гарантії "повідомлення переживе рестарт broker'а".

Durable queue

$channel->queue_declare(
    'task_queue',
    false,     // passive
    true,      // durable ← переживає рестарт broker'а
    false,     // exclusive
    false,     // auto_delete
    false,
    new AMQPTable(['x-queue-type' => 'quorum'])
);

Без durable=true queue видаляється при рестарті broker'а - разом з усіма повідомленнями.

Persistent message

$msg = new AMQPMessage($data, [
    'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,  // = 2
]);
$channel->basic_publish($msg, '', 'task_queue');

Без delivery_mode=2 повідомлення сидить тільки у RAM. Broker впав → повідомлення зникло, навіть якщо queue durable.

Матриця гарантій

Queue durableMsg persistentЩо буде з повідомленням після рестарту?
будь-якийQueue зникне, повідомлення з нею.
Queue відновиться порожньою.
Queue відновиться, повідомлення на місці.
Для quorum queues (x-queue-type: quorum) durability у дещо іншому форматі: дані пишуться через Raft на 3+ ноди, повідомлення переживають падіння будь-якої меншості нод. Розділ 4 - детально.
Джерело: tutorial-two-php Перевірено: 2026-05-09
2.3

Publish / Subscribe з fanout

Сценарій: один producer кидає event - кожен consumer отримує копію (broadcast). Реалізація - fanout exchange + temporary exclusive queues.

Producer

$channel->exchange_declare(
    'logs',     // name
    'fanout',   // type
    false,      // passive
    false,      // durable
    false       // auto_delete
);

$msg = new AMQPMessage($data);
$channel->basic_publish($msg, 'logs');
// routing_key не вказаний → fanout все одно ігнорує
the fanout exchange ... just broadcasts all the messages it receives to all the queues it knows.

Consumer (логіка одноразової підписки)

$channel->exchange_declare('logs', 'fanout', false, false, false);

// queue з порожнім ім'ям → broker згенерує унікальне (amq.gen-XYZ)
// exclusive=true → видалиться при disconnect
list($queue_name, ,) = $channel->queue_declare(
    "",      // name (auto-generate)
    false,   // passive
    false,   // durable
    true,    // exclusive ← тільки для цього connection
    false    // auto_delete
);

$channel->queue_bind($queue_name, 'logs');

$channel->basic_consume($queue_name, '', false, true, false, false, $callback);

Чому exclusive queue, а не named

Цей патерн (exclusive auto-named queue) - класичний для transient subscribers (notifications, dashboards). Для persistent worker'ів (ETL, audit-log) - окрема named queue з durable=true.
Джерело: tutorial-three-php Перевірено: 2026-05-09
2.4

Routing з direct exchange

Класичний приклад tutorial - логи з рівнями info, warning, error. Consumer обирає, які рівні слухати.

Exchange + публікація

$channel->exchange_declare('direct_logs', 'direct', false, false, false);

$msg = new AMQPMessage($data);
$channel->basic_publish($msg, 'direct_logs', $severity);
// $severity ∈ {'info', 'warning', 'error'}

Consumer з кількома bindings

$channel->exchange_declare('direct_logs', 'direct', false, false, false);

list($queue_name, ,) = $channel->queue_declare("", false, false, true, false);

// Кілька bindings на ту саму queue з РІЗНИМИ routing keys
foreach ($severities as $severity) {
    $channel->queue_bind($queue_name, 'direct_logs', $severity);
}

$channel->basic_consume($queue_name, '', false, true, false, false, $callback);

Use case з консолі

# Логер що пише ВСЕ у файл
php receive_logs_direct.php info warning error  > logs/all.log

# Окрема панель для критичних
php receive_logs_direct.php error  | mail -s "ERROR" oncall@example.com
Producer DIRECT "direct_logs" key=info key=warning,error key=error queue all_logs (info+warn+err) queue ops (warn+err) queue oncall_pager (err only)
Джерело: tutorial-four-php Перевірено: 2026-05-09
2.5

Topics - routing з pattern matching

Direct - точна рівність. Topic - patterns з wildcards. Routing key стає ієрархічним: <facility>.<severity> або <entity>.<action>.<region>.

$channel->exchange_declare('topic_logs', 'topic', false, false, false);

// Producer публікує з ієрархічним routing key
$channel->basic_publish($msg, 'topic_logs', 'kern.critical');
$channel->basic_publish($msg, 'topic_logs', 'auth.warning');
$channel->basic_publish($msg, 'topic_logs', 'kern.info');

// Consumer 1: всі kern-events
$channel->queue_bind($queueA, 'topic_logs', 'kern.*');

// Consumer 2: всі critical, незалежно від facility
$channel->queue_bind($queueB, 'topic_logs', '*.critical');

// Consumer 3: ВСЕ
$channel->queue_bind($queueC, 'topic_logs', '#');

Wildcards (нагадування з 1.2.5)

Сценарій з курсу: e-commerce events

// Producer - кожна доменна подія публікується з ієрархічним key
publish($msg, 'events', 'order.created.eu');
publish($msg, 'events', 'order.paid.us');
publish($msg, 'events', 'user.signed_up.eu');

// Consumer'и за зоною відповідальності:
queue_bind($euAnalyticsQ, 'events', '*.*.eu');         // тільки EU events
queue_bind($paymentsQ,    'events', 'order.paid.*');   // тільки payments
queue_bind($auditQ,       'events', '#');              // ВСЕ для аудиту
Topic exchange - надмножина direct і fanout: binding без wildcards = direct поведінка; binding # = fanout. У production часто стартують з topic, бо легко додавати нових consumer'ів з новими patterns без зміни producer'а.
Джерело: tutorial-five-php Перевірено: 2026-05-09
РОЗДІЛ 03 · ≈ 2.0 ГОД

Reliability

Як гарантувати доставку без втрат і без дублікатів-привидів. Найбільш практично-важливий розділ для backend-команди.

3.1.1

Publisher Confirms vs AMQP Transactions

Питання, що вирішується: як producer'у дізнатись, що broker реально прийняв повідомлення? Без додаткових механізмів basic_publish - "fire-and-forget": producer запхав у TCP-сокет, broker міг впасти між publish і flush на диск.

Два механізми

ПідхідЯк працюєThroughput
AMQP Transactions (tx.select / tx.commit)Стандарт AMQP. Synchronous round-trip після кожного publish."unnecessarily heavyweight and decrease throughput by a factor of 250". Не використовується.
Publisher Confirms (confirm.select)Async. Broker шле basic.ack/basic.nack для кожного publish (за seq #). Producer пише далі без блокування.~production throughput, без транзакційного оверхеду. De facto стандарт.

Як вмикати в php-amqplib

$channel->confirm_select();   // confirm.select - переводить channel у confirm mode

// Реєструємо handlers до ack/nack від broker'а
$channel->set_ack_handler(function (AMQPMessage $msg) {
    // повідомлення з sequence number $msg->getDeliveryTag() прийнято
});
$channel->set_nack_handler(function (AMQPMessage $msg) {
    // broker відмовився - log, retry, alert
});

for ($i = 0; $i < 1000; $i++) {
    $channel->basic_publish(new AMQPMessage("msg $i"), '', 'tasks');
}

// Чекати, поки прийдуть всі ack'и (або timeout 5 сек)
$channel->wait_for_pending_acks_returns(5);
Once a channel is in confirm mode, both the broker and the client count messages (counting starts at 1 on the first confirm.select). The broker then confirms messages as it handles them by sending a basic.ack on the same channel.
Коли приходить basic.nack: internal error броker'а (диск переповнений, queue з ack-mode publisher confirms не може прийняти). Це сигнал "повтори через якийсь час".
Джерело: rabbitmq.com/docs/confirms Версія: 4.3.0 Перевірено: 2026-05-09
3.1.2

Mandatory flag + return listener

Що буде, якщо publish у exchange без bindings (або routing key не матчить жодну binding)? За замовчуванням - тиха втрата ("zero or more queues" з 1.2.1).

Прапор mandatory

$msg = new AMQPMessage('payload');

$channel->basic_publish(
    $msg,
    'orders',
    'unmatched.key',
    true   // mandatory = true ← вимагати, щоб повідомлення потрапило хоча б в одну queue
);

$channel->set_return_listener(function ($replyCode, $replyText, $exchange, $routingKey, $msg) {
    // повідомлення повернуто broker'ом, бо нікуди не маршрутизувалось
    error_log("Returned: $replyText for $exchange/$routingKey");
});
For unroutable messages... If the message is also published as mandatory, the basic.return is sent to the client before basic.ack.

Чому це важливо

Order of broker callbacks для mandatory + confirms: спершу basic.return (повернення невмаршрутизованого), потім basic.ack (підтвердження що broker його обробив). Не плутати - ack не означає "доставлено в queue", означає "broker його прийняв (і повернув якщо unroutable)".
Джерело: rabbitmq.com/docs/confirms Версія: 4.3.0 Перевірено: 2026-05-09
3.2

Consumer ack: timeout у 4.3 (важлива зміна!)

Базовий manual ack-flow вже знайомий з 2.2.2. У RabbitMQ 4.3 - значуща зміна щодо acknowledgement timeout.

Зміна 4.3: "Starting with RabbitMQ 4.3, delivery acknowledgement timeouts are only supported by quorum queues."
Тобто classic queues у 4.3 НЕ enforces ack timeout. Це означає, що x-queue-type: quorum стає необхідним для production-надійності.

Що таке consumer ack timeout

Consumer 'consumer-tag-998754663370' on channel 1 and queue 'qq.1' in vhost '/'
has timed out waiting for a consumer acknowledgement of a delivery with delivery tag = 10.
Timeout used: 1800000 ms.

Як налаштувати

# /etc/rabbitmq/rabbitmq.conf (мілісекунди)
consumer_timeout = 1800000   # 30 хв (default)
# або 1 година для довгих ETL-задач:
consumer_timeout = 3600000
Constraints (з документації): "Values lower than one minute are not supported, and values lower than five minutes are not recommended." Ставити менше 5 хв ризиковано: GC pause / повільний disk write можуть викликати false-positive timeout.

Practical impact для backend-команди

Джерело: rabbitmq.com/docs/consumers#acknowledgement-timeout Версія: 4.3.0 Перевірено: 2026-05-09
3.3.1

Dead Letter Exchange - коли повідомлення стає "мертвим"

Messages from a queue can be 'dead-lettered', which means these messages are republished to an exchange when any of the following four events occur.

Чотири причини dead-letter

#ПричинаХто викликає
1"basic.reject or basic.nack with requeue=false"Consumer явно: "відмовляюсь, не вертайте назад".
2"The message expires due to per-message TTL"Час повідомлення (expiration property) вийшов.
3"The message is dropped because its queue exceeded a length limit"Queue має x-max-length або x-max-length-bytes, новіші витискають старіші.
4"The message is returned more times to a quorum queue than the delivery-limit"Quorum queue з x-delivery-limit - повідомлення re-queue'ed забагато разів.

Що таке "стало мертвим"

Broker re-publish'є його у Dead Letter Exchange (DLX) з:

queue tasks x-dead-letter-exchange="dlx" dead-letter DLX exchange "dlx" queue dead_letters для аудиту / retry
Джерело: rabbitmq.com/docs/dlx Версія: 4.3.0 Перевірено: 2026-05-09
3.3.2

DLX: налаштування і retry-патерни

Базове налаштування queue

$channel->exchange_declare('dlx', 'direct', false, true, false);
$channel->queue_declare('dead_letters', false, true, false, false);
$channel->queue_bind('dead_letters', 'dlx', 'failed');

$channel->queue_declare('tasks', false, true, false, false, false, new AMQPTable([
    'x-queue-type' => 'quorum',
    'x-dead-letter-exchange'    => 'dlx',
    'x-dead-letter-routing-key' => 'failed',
]));
"hardcoded arguments are strongly recommended against, since they cannot be updated without redeploying applications, while policies offer greater flexibility". У production топологію керують через policies (regex по queue-name + arguments). Code оголошує queue без DLX-args, policy їх додає.

Retry з exponential backoff (через TTL + DLX)

Класичний патерн: повідомлення впало → іде в "park"-queue з TTL → expire → DLX повертає назад у головну queue. Між спробами - exponential delay.

// Дві queues:
// 1) main "tasks" - звичайна
// 2) "tasks.retry.5s" з x-message-ttl=5000 і DLX = main exchange

$channel->queue_declare('tasks.retry.5s', false, true, false, false, false, new AMQPTable([
    'x-message-ttl'             => 5000,         // 5 секунд
    'x-dead-letter-exchange'    => '',           // default exchange
    'x-dead-letter-routing-key' => 'tasks',      // повертає в main queue
]));

// Consumer при тимчасовій помилці:
function onTransientError(AMQPMessage $msg, $channel) {
    // Re-publish у retry queue замість nack
    $channel->basic_publish($msg, '', 'tasks.retry.5s');
    $msg->ack();   // ack оригіналу
}

Patterns на DLX

Джерела: dlx · ttl Версія: 4.3.0 Перевірено: 2026-05-09
3.4

Idempotency на consumer-side

RabbitMQ дає гарантію at-least-once (хоча б раз), не exactly-once. Це означає: те саме повідомлення може прийти 2-3 рази у випадках мережевого сплеску, redelivery після crash, ack не дійшов до broker'а. Це не баг - це характеристика розподіленої системи (FLP, two generals).

Idempotent (ідемпотентна) операція

Операція, повторне виконання якої не змінює результат після першого. Формально: f(f(x)) == f(x).

ОпераціяIdempotent?Чому
SET balance = 100Скільки разів не повторюй - balance залишається 100.
UPDATE balance = balance + 10Повтор два рази → +20 замість +10. Фінансова катастрофа.
DELETE FROM users WHERE id = 5Друге виконання нічого не робить.
INSERT INTO users (...)Дублікат рядка або UNIQUE-помилка.

Дві стратегії на consumer-side

1. Idempotent by design

Спроектувати операцію так, щоб повтор був безпечний. Useful patterns:

2. Deduplication через зовнішній storage

$messageId = $msg->get('message_id');  // або власний idempotency key у payload

// Redis SETNX (set if not exists) з TTL
$inserted = $redis->set("processed:$messageId", "1", ['EX' => 86400, 'NX' => true]);

if ($inserted) {
    // Перша спроба - обробити
    handleMessage($msg);
    $msg->ack();
} else {
    // Дублікат - просто ack без обробки
    $msg->ack();
}
Idempotency - це не патерн RabbitMQ, а design pattern, який consumer ОБОВ'ЯЗКОВО реалізує сам. Broker не може його забезпечити - не знає семантику бізнес-операції. Кожне повідомлення в production-payload має мати message_id або doc-specific idempotency_key.
Джерело: rabbitmq.com/blog 2014 (still authoritative) Перевірено: 2026-05-09
РОЗДІЛ 04 · ≈ 1.5 ГОД

Quorum Queues і Streams

Сучасні replicated primitives RabbitMQ 4.x. Classic Mirrored Queues свідомо пропускаємо - "removed starting with RabbitMQ 4.0".

4.1.1

Quorum Queues - replicated FIFO на Raft

The RabbitMQ quorum queue is a modern queue type which implements a durable, replicated queue based on the Raft consensus algorithm.

Як працює реплікація

"All operations (state changes) on a quorum queue are sent to a primary member called a leader which in turn replicates the operations to the remaining members, called followers."

Декларування

$channel->queue_declare('orders', false, true, false, false, false, new AMQPTable([
    'x-queue-type' => 'quorum',
    // 'x-quorum-initial-group-size' => 3, // default 3
    // 'x-delivery-limit' => 10,           // після 10 redelivers → DLX
]));

"To declare a quorum queue set the x-queue-type queue argument to quorum (the default is classic)."

Розмір кластера

Чому не 2 ноди: Raft вимагає majority. У 2-нодному кластері majority = 2, тобто будь-яка одна нода вниз = no quorum = no writes. 3 ноди = majority 2, витримує падіння 1.
Джерело: rabbitmq.com/docs/quorum-queues Версія: 4.3.0 Перевірено: 2026-05-09
4.1.2

Коли НЕ варто використовувати quorum queues

З документації - чотири категорії, де quorum queues неефективні або непридатні:

КатегоріяЧомуЩо замість
"Temporary queues: transient or exclusive queues, high queue churn" Raft має overhead на ініціалізацію. Створювати/видаляти 1000 queues/хв через Raft - марно. Classic queue (для exclusive use, transient subscribers - як у 2.3 Pub/Sub).
"Lowest possible latency" Raft round-trip між нодами додає 1-3 мс. Для прямих RPC-чатів недопустимо. Classic queue (одна нода, без реплікації).
"Very long queue backlogs (5M+ messages)" Quorum queue зберігає весь стан у пам'яті лідера. Великі backlogs з'їдають RAM. Streams (диск-first, мільярди повідомлень).
"Large fanouts" Реплікація через Raft не оптимізована для broadcast. Streams (один write, багато consumers читають з offset).

Як вирішити: classic, quorum чи stream

СценарійТип
Production-критична work queue (платежі, замовлення)Quorum
Transient subscribers (live notifications, dashboards)Classic (exclusive auto-delete)
Event sourcing, audit log, високий throughputStream
RPC reply queues (короткі, exclusive)Classic (exclusive)
Великий backlog (DAG retries, ETL з годинами)Stream
Зміна 4.3: non-durable non-exclusive queues тепер відхиляються за замовчуванням. Якщо legacy-код намагається створити таку queue, broker поверне помилку. Workaround: deprecated_features.permit.transient_nonexcl_queues = true у rabbitmq.conf (тільки тимчасово).
Джерела: quorum-queues · v4.3.0 release notes Версія: 4.3.0 Перевірено: 2026-05-09
4.1.3

Quorum queues - новинки 4.3

Версія 4.3.0 принесла кілька значущих покращень саме для quorum queues. Цитати з release notes.

1. Strict priority queues

strict priority queues with per-priority message counts, correct redelivery ordering across priorities, and priority-aware message expiration scans

2. Delayed retry з configurable backoff

delayed retry with configurable backoff based on delivery count. When messages are returned... they can be held in a delayed state before becoming available again.

3. Configurable consumer timeout per queue

Quorum queues now support a configurable consumer timeout. When a consumer holds unacknowledged messages beyond the timeout, the messages are returned to the queue.

4. Memory optimization

Message references now use compact packed integer representation, halving per-message memory overhead in many scenarios.

Для systems з мільйонами queued messages - до 50% memory зменшення на broker'і.

Headline: у 4.3 quorum queue стало повноцінною заміною classic для всіх production-сценаріїв, окрім exclusive/transient. Класичні queue лишаються тільки для тимчасових сценаріїв.
Джерело: github.com/rabbitmq/rabbitmq-server v4.3.0 release notes Версія: 4.3.0 Перевірено: 2026-05-09
4.2.1

Streams - append-only лог

a persistent replicated data structure that can complete the same tasks as queues. Streams model an append-only log of messages that can be repeatedly read until they expire.

Ключова відмінність від queues

Queue (classic / quorum)Stream
Семантика consumeDestructive: ack → broker стирає."non-destructive consumer semantics": повідомлення лишається.
ReplayНеможливий (повідомлення вже стерто).Так. Будь-який offset → читаєш з минулого.
Кілька consumersRound-robin (work queue) або копії в окремі queues.Кожен consumer читає той самий лог зі свого offset, незалежно.
ЗберіганняMemory + Disk (durable).Disk-first, append-only сегменти.
CleanupЧерез ack або TTL.За часом (x-max-age) або розміром (x-stream-max-segment-size-bytes).

Декларування

$channel->queue_declare('events', false, true, false, false, false, new AMQPTable([
    'x-queue-type' => 'stream',
    'x-max-age'    => '7D',         // зберігати 7 днів
    'x-stream-max-segment-size-bytes' => 100_000_000, // 100MB segments
]));

"set the x-queue-type queue argument to stream ... must be provided by a client at declaration time; it cannot be set or changed using a policy."

Use cases

Джерело: rabbitmq.com/docs/streams Версія: 4.3.0 Перевірено: 2026-05-09
4.2.2

Stream native protocol vs AMQP 0-9-1 access

Stream доступний по двох протоколах одночасно:

Native protocol (port 5552)AMQP 0-9-1 (port 5672)
ThroughputСотні тисяч/сек на consumer'а.Десятки тисяч/сек.
Specific stream featuresServer-side filter, super-streams (sharding), single active consumer.Базова robocon: read-from-offset, basic_consume.
PHP clientНемає офіційного. Тільки Java, .NET, Go, Python, Rust.php-amqplib працює як з queue.
Рекомендується дляProduction stream-heavy load.Прості use cases, де PHP - не bottleneck.
Data access ... via a RabbitMQ client library or through a dedicated binary protocol [is] highly recommended as it provides access to all stream-specific features.

PHP сценарій (через AMQP 0-9-1)

// Декларуємо stream
$channel->queue_declare('events', false, true, false, false, false, new AMQPTable([
    'x-queue-type' => 'stream',
]));

// Consumer з offset = "first" (з самого початку)
$channel->basic_qos(null, 100, false);  // prefetch обов'язковий для streams
$channel->basic_consume(
    'events',
    '',
    false,
    false,   // manual ack
    false,
    false,
    $callback,
    null,
    new AMQPTable([
        'x-stream-offset' => 'first',  // або: 'last', 'next', timestamp, або int offset
    ])
);

Offset стратегії

У RabbitMQ 4.3 PHP офіційно НЕ має stream-native клієнта. Якщо stream-features (server-side filter, super-streams) критичні - використовуйте Java/Python sidecar або переходьте на AMQP 1.0 (php-amqplib його підтримує частково).
Джерела: streams · streams-overview blog Версія: 4.3.0 Перевірено: 2026-05-09
РОЗДІЛ 05 · ≈ 2.5 ГОД

Інтеграція з PHP

Три рівні абстракції для роботи з RabbitMQ у PHP-проєктах. Від low-level wire-protocol клієнта до drop-in queue driver'а.

5.1

php-amqplib - native AMQP 0-9-1 клієнт

The most widely used PHP client for RabbitMQ ... a pure PHP implementation of the AMQP 0-9-1 protocol.

Чому "pure PHP"

Інсталяція

composer require php-amqplib/php-amqplib

Базовий flow

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;

// 1. Connection (TCP) - один раз на процес
$connection = new AMQPStreamConnection(
    'rabbitmq-host', 5672, 'user', 'password',
    '/',                  // vhost
    false,                // insist
    'AMQPLAIN',           // login_method
    null,                 // login_response
    'en_US',              // locale
    3.0,                  // connection_timeout
    3.0,                  // read_write_timeout
    null,                 // context (TLS)
    false,                // keepalive
    60,                   // heartbeat (секунди!)
    0.0,                  // channel_rpc_timeout
);

// 2. Channel - дешевий, можна багато на 1 connection
$channel = $connection->channel();

// 3. Декларуй topology idempotently
$channel->exchange_declare('orders', 'topic', false, true, false);
$channel->queue_declare('order_processing', false, true, false, false, false,
    new AMQPTable(['x-queue-type' => 'quorum']));
$channel->queue_bind('order_processing', 'orders', 'order.*.processing');

// 4. Publish (з headers і properties)
$msg = new AMQPMessage(json_encode($payload), [
    'content_type'   => 'application/json',
    'delivery_mode'  => AMQPMessage::DELIVERY_MODE_PERSISTENT,
    'message_id'     => Uuid::v4()->toRfc4122(),
    'timestamp'      => time(),
    'application_headers' => new AMQPTable(['source' => 'orders-service']),
]);
$channel->basic_publish($msg, 'orders', 'order.created.eu');

// 5. Завжди закривати в кінці процесу
$channel->close();
$connection->close();

Дві варіанти Connection класу

У production важливо явно вказувати heartbeat (рекомендоване 60 сек) - інакше "тихий" TCP не виявить розрив до first IO. На AWS NLB/firewall'и часто закривають idle conn через 5-10 хв без RST.
Джерело: github.com/php-amqplib/php-amqplib Версія: v3.7.4 Перевірено: 2026-05-09
5.2

Symfony Messenger з AMQP transport

Messenger provides a message bus with the ability to send messages and then handle them immediately in your application or send them through transports (e.g. queues) to be handled later.

Інсталяція (зверни увагу: PECL-extension!)

composer require symfony/amqp-messenger
# І в Docker - PECL extension:
# docker-php-ext-install ... && pecl install amqp && docker-php-ext-enable amqp
Symfony AMQP transport використовує ext-amqp (PECL), не php-amqplib. Це принципова відмінність - бінарне залежить від C-extension. Якщо хочеш pure-PHP transport - є community-пакет php-amqplib/symfony-bundle або власний transport через RabbitMqBundle.

DSN format

# .env
MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
#                        scheme   user   pass   host  port vhost  exchange
#                        amqps:// для TLS

%2f - це URL-encoded / (default vhost).

Конфігурація з retry strategy

# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            async:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
                options:
                    auto_setup: true   # автоматично створює exchange/queue/bindings
                    exchange:
                        name: 'orders'
                        type: 'topic'
                    queues:
                        order_processing:
                            arguments:
                                x-queue-type: 'quorum'
                            binding_keys: ['order.*.processing']
                retry_strategy:
                    max_retries: 3
                    delay: 1000        # ms перед першим retry
                    multiplier: 2      # exponential: 1s, 2s, 4s
                    max_delay: 10000
                    jitter: 0.1        # ±10% randomness
        routing:
            App\Message\OrderCreatedEvent: async

auto_setup: "By default, the transport will automatically create any exchanges, queues and binding keys that are needed. That can be disabled." У production часто auto_setup: false + topology через migration.

Worker

# Запуск consumer'а (виконується infinitely до Ctrl+C / SIGTERM)
php bin/console messenger:consume async

# З verbose logging
php bin/console messenger:consume async -vv

# З time limit (для systemd / supervisord auto-restart)
php bin/console messenger:consume async --time-limit=3600

# Зупинити gracefully
php bin/console messenger:stop-workers
Messenger має Retry Strategy ВЛАСНИЙ механізм поверх RabbitMQ. Це Native Symfony retry, не RabbitMQ DLX. Failed повідомлення йдуть у retry queue, потім назад. Якщо хочеш RabbitMQ-native DLX - треба configure manually через transport options.
Джерело: symfony.com/doc messenger Версія: Symfony 7.x · symfony/amqp-messenger Перевірено: 2026-05-09
5.3

Laravel Queue з RabbitMQ driver

Community-стандарт: vladimir-yuldashev/laravel-queue-rabbitmq. Drop-in заміна Redis/SQS - вся Queue-фасадна логіка Laravel працює без змін.

Інсталяція

composer require vladimir-yuldashev/laravel-queue-rabbitmq
# Service provider реєструється автоматично через Laravel Package Discovery

Конфігурація

// config/queue.php
'default' => env('QUEUE_CONNECTION', 'rabbitmq'),

'connections' => [
    'rabbitmq' => [
        'driver' => 'rabbitmq',
        'queue'  => env('RABBITMQ_QUEUE', 'default'),
        'hosts' => [
            [
                'host'     => env('RABBITMQ_HOST', '127.0.0.1'),
                'port'     => env('RABBITMQ_PORT', 5672),
                'user'     => env('RABBITMQ_USER', 'guest'),
                'password' => env('RABBITMQ_PASSWORD', 'guest'),
                'vhost'    => env('RABBITMQ_VHOST', '/'),
            ],
        ],
        'options' => [
            'queue' => [
                'job'         => \VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob::class,
                'prefetch'    => 10,
                'declare'     => true,    // auto-declare queue
                'arguments'   => [
                    'x-queue-type' => 'quorum',
                ],
            ],
        ],
    ],
],

Використання (стандартне Laravel API)

// Job клас не змінюється - той самий що для Redis/SQS
class ProcessOrder implements ShouldQueue {
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(public Order $order) {}

    public function handle(): void {
        // ... обробка
    }
}

// Dispatch як зазвичай
ProcessOrder::dispatch($order)->onQueue('orders.processing');

Дві команди для consumer'а

КомандаЯк працюєШвидкість
php artisan queue:work rabbitmqСтандартна Laravel-команда. Polling через basic_get. Підтримує кілька queues одночасно.Базова.
php artisan rabbitmq:consumeNative через basic_consume (push-based, без polling).~2x швидше. Single queue only.
Horizon-сумісність: підтримує Laravel Horizon для моніторингу. Конфіг у config/horizon.php працює як для Redis: connection: 'rabbitmq'.
Quorum queues offically не задокументовані в README. На практиці працює через options.queue.arguments.x-queue-type: 'quorum' (як вище). Перевірити для production - issues на GitHub містять обговорення edge cases.
Джерело: github.com/vyuldashev/laravel-queue-rabbitmq Версія: compatible Laravel 10.x / 11.x Перевірено: 2026-05-09
5.4

Коли який інструмент

ІнструментКоли вибиратиAnti-cases
php-amqplib
  • Нестандартні patterns: RPC, custom routing logic, streams з offset.
  • Точний контроль над channel lifecycle, prefetch, ack semantics.
  • Бібліотечний код, що інтегрує з кількома frameworks.
  • CLI-tools і скрипти без framework.
Простий job dispatch у Laravel/Symfony - надмірний boilerplate.
Symfony Messenger
  • Production-стандарт для Symfony-проєктів.
  • Async commands і domain events.
  • Multi-transport (одні events → AMQP, інші → SQS, треті → in-memory).
  • Built-in retry, failure transport, middleware.
Stream-native features (server-side filter, super-streams). Низько-рівневі patterns.
Laravel Queue + RabbitMQ
  • Існуючий Laravel-проєкт мігрує з Redis/SQS на RabbitMQ.
  • Хочеться dispatch(new Job) API без зміни coda.
  • Horizon для UI моніторингу.
Складна topology (multiple exchanges, custom bindings). Stream-features. RPC.

Гібрид у production

У реальних проєктах часто комбінують:

Не намагатися "уніфікувати" - кожен інструмент під свою задачу. Spoiler: вони чудово співіснують у тому самому RabbitMQ broker'і.
Джерело: синтез з 5.1-5.3 Перевірено: 2026-05-09
РОЗДІЛ 06 · ≈ 2.0 ГОД

DevOps базовий

Як підняти RabbitMQ для production-команди. Без deep SRE - acceptable knowledge для backend-розробника, що деплоїть та підтримує сервіс.

6.1.1

Кластер на 3 ноди - peer discovery

To form a cluster, new ('blank') nodes need to be able to discover their peers. This can be done using a variety of mechanisms (backends).

П'ять discovery механізмів

МеханізмЯк працюєКоли
Classic config"read a list of nodes from the config file". Hardcoded у cluster_formation.classic_config.nodes.Бare metal, статичні IP, наш sandbox.
KubernetesPlugin використовує K8s API. "node with the lowest ordinal index (almost always the pod with the -0 suffix) to form a new cluster".K8s deployments, StatefulSet.
Consul"Nodes register with Consul on boot and unregister when they leave".HashiCorp stack.
etcdРеєструються через ключі в etcd directory.Kubernetes-aligned.
DNSQuery A-records seed hostname → reverse DNS lookup.AWS Cloud Map / kube-dns.

Чому 3 ноди (а не 2 чи 1)

Базується на правилі quorum = majority для Raft (Quorum Queues) і Khepri (metadata store).

Розмір кластераQuorumВитримує падіння
110 нод (no HA)
220 нод (split-brain ризик)
321 ноду
532 ноди
743 ноди
Khepri у 4.x - metadata store, замінив Mnesia (4.3 - вже єдиний). Khepri теж потребує majority для writes. "a majority of nodes must be online at all times" для оголошення queues, exchange, користувачів. Якщо 2 з 3 нод впали - топологію не можна змінити, але існуючі queues та messages працюють далі.
Парне число нод (4, 6) - анти-паттерн. Витримує стільки ж падінь, що й попереднє непарне (n-1), але споживає більше ресурсів.
Джерела: cluster-formation · v4.3.0 release notes (Khepri) Версія: 4.3.0 Перевірено: 2026-05-09
6.1.2

Sandbox: 3-нодний кластер у docker-compose

Створи окремий compose.cluster.yaml у sandbox/ для практики. Базовий каркас:

x-rabbit-base: &rabbit-base
  image: rabbitmq:4.3-management
  environment: &rabbit-env
    RABBITMQ_ERLANG_COOKIE: 'study-cluster-cookie'
    RABBITMQ_DEFAULT_USER: guest
    RABBITMQ_DEFAULT_PASS: guest
  healthcheck:
    test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
    interval: 15s
    timeout: 10s
    retries: 5

services:
  rabbit1:
    <<: *rabbit-base
    hostname: rabbit1
    container_name: rabbit1
    ports:
      - "5672:5672"
      - "15672:15672"
    volumes:
      - rabbit1-data:/var/lib/rabbitmq
      - ./cluster-config/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro

  rabbit2:
    <<: *rabbit-base
    hostname: rabbit2
    container_name: rabbit2
    volumes:
      - rabbit2-data:/var/lib/rabbitmq
      - ./cluster-config/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro

  rabbit3:
    <<: *rabbit-base
    hostname: rabbit3
    container_name: rabbit3
    volumes:
      - rabbit3-data:/var/lib/rabbitmq
      - ./cluster-config/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro

volumes:
  rabbit1-data:
  rabbit2-data:
  rabbit3-data:

cluster-config/rabbitmq.conf

cluster_formation.peer_discovery_backend = classic_config
cluster_formation.classic_config.nodes.1 = rabbit@rabbit1
cluster_formation.classic_config.nodes.2 = rabbit@rabbit2
cluster_formation.classic_config.nodes.3 = rabbit@rabbit3

# Khepri за замовчуванням у 4.3
# log і management plugin вже включені у -management образі

# Disk alarm
disk_free_limit.absolute = 2GB

# Memory alarm
vm_memory_high_watermark.relative = 0.6

Перевірка кластера

docker compose -f compose.cluster.yaml up -d

# Дочекатися healthy всіх 3 нод (60-90 с)
docker compose -f compose.cluster.yaml ps

# Перевірити cluster status з будь-якої ноди
docker exec rabbit1 rabbitmqctl cluster_status
# Має показати 3 nodes: [rabbit@rabbit1, rabbit@rabbit2, rabbit@rabbit3]

# Створити quorum queue з 3-way replication
docker exec rabbit1 rabbitmqadmin declare queue name=test \
    arguments='{"x-queue-type":"quorum"}'
Erlang Cookie - shared secret між нодами для inter-node communication. Має бути ОДНАКОВИЙ на всіх нодах кластера. Через env RABBITMQ_ERLANG_COOKIE або файл /var/lib/rabbitmq/.erlang.cookie.
Джерело: rabbitmq.com/docs/clustering Версія: 4.3.0 Перевірено: 2026-05-09
6.2.1

Моніторинг: Management HTTP API

Окрім UI, management plugin експонує REST API на тому ж порту 15672. Все що видно в UI, можна скачати JSON'ом.

Ключові endpoint'и

EndpointЩо повертає
GET /api/overviewCluster-wide: total connections, channels, queues, message rates.
GET /api/nodesСписок нод з memory, disk, FD usage, Erlang procs.
GET /api/nodes/{node}Детально по конкретній ноді.
GET /api/nodes/{node}/memoryMemory breakdown (queues, connections, plugins, ETS).
GET /api/queuesУсі queues - depth, consumers, message rates.
GET /api/queues/{vhost}/{name}Детально по конкретній queue.
GET /api/exchanges / /api/bindingsТопологія.

Приклад: швидкий health-check скрипт

#!/bin/bash
# health.sh
HOST="http://guest:guest@localhost:15672"

# Перевіряємо: всі ноди running?
unhealthy=$(curl -s "$HOST/api/nodes" | jq '[.[] | select(.running == false)] | length')
[[ "$unhealthy" -gt 0 ]] && echo "FAIL: $unhealthy node(s) not running" && exit 1

# Перевіряємо: queues з depth > 10000?
backed_up=$(curl -s "$HOST/api/queues" \
    | jq '[.[] | select(.messages > 10000)] | length')
[[ "$backed_up" -gt 0 ]] && echo "WARN: $backed_up queue(s) backed up"

# Memory alarm?
mem_alarm=$(curl -s "$HOST/api/overview" | jq '.message_stats // {}')
echo "OK"

Ключові метрики для backend-команди

Документація рекомендує collection interval "30 to 60 second range" для production. 5 секунд - тільки для dev (генерує надмірний load на management plugin).
Джерело: rabbitmq.com/docs/monitoring Версія: 4.3.0 Перевірено: 2026-05-09
6.2.2

Prometheus + Grafana

Support for Prometheus metric collector ships in the rabbitmq_prometheus plugin.

Включений за замовчуванням у -management tag

На наших sandbox-нодах він уже працює - це той самий порт 15692, який ми експонуємо у compose.yaml.

# Перевірити
curl -s localhost:15692/metrics | head -n 20

# rabbitmq_build_info{erlang_version="26.2.5",rabbitmq_version="4.3.0"} 1
# rabbitmq_identity_info{...} 1
# rabbitmq_alarms_memory_used_watermark 0
# rabbitmq_alarms_disk_limit_exceeded 0
# rabbitmq_queue_messages 0
# ...

Якщо вимкнено - як включити

rabbitmq-plugins enable rabbitmq_prometheus

Два режими metrics

# Per-object endpoint
curl -s localhost:15692/metrics/per-object

Топ-7 алертів (PromQL)

# 1. Нода не відповідає 2 хв
up{job="rabbitmq"} == 0  for 2m

# 2. Memory alarm активний
rabbitmq_alarms_memory_used_watermark == 1

# 3. Disk alarm активний
rabbitmq_alarms_disk_limit_exceeded == 1

# 4. Queue depth перевищив поріг
rabbitmq_queue_messages > 10000

# 5. Queue без consumers
rabbitmq_queue_consumers == 0  and  rabbitmq_queue_messages > 100

# 6. Connection churn (rate of new connections / sec)
rate(rabbitmq_connections_opened_total[5m]) > 10

# 7. FD usage > 80%
rabbitmq_process_open_fds / rabbitmq_process_max_fds > 0.8
У офіційному репо є готові Grafana dashboards (RabbitMQ-Overview, RabbitMQ-Erlang-VM, RabbitMQ-Quorum-Queues-Raft) - імпортуй за ID, не пиши з нуля.
Джерело: rabbitmq.com/docs/prometheus Версія: 4.3.0 Перевірено: 2026-05-09
6.3

Production checklist

Скорочена вибірка з офіційного production-checklist, з акцентом на пункти, які напряму зачіпають backend-команду.

Operating system

Resource alarms

Security

Erlang OTP

Cluster topology

Джерело: rabbitmq.com/docs/production-checklist Версія: 4.3.0 Перевірено: 2026-05-09

Курс пройдено ✓

RabbitMQ 4.3.0 · ~12 годин · 6 розділів
Що пройшли:
  • Архітектура AMQP 0-9-1: producer / exchange / binding / queue / consumer.
  • 4 типи exchange (direct, fanout, topic, headers) і коли який вибирати.
  • Connection vs Channel - multiplexing і чому це матеріально для production.
  • Reliability patterns: publisher confirms, consumer ack timeout у 4.3, DLX і retry.
  • Quorum queues (Raft) та Streams (append-only) - сучасні replicated primitives.
  • Інтеграція з PHP: php-amqplib, Symfony Messenger, Laravel Queue.
  • DevOps: 3-нодний кластер, Prometheus monitoring, production checklist.
Свідомо не торкались:
  • Federation, Shovel - cross-cluster replication (advanced).
  • STOMP, MQTT - інші протоколи (поза PHP-екосистемою).
  • AMQP 1.0 deep dive - тільки згадка про core у 4.0+.
  • Classic Mirrored Queues - deprecated у 4.x, видалено у 4.0.
Куди далі:
Натисни Home щоб повернутись на cover, або T щоб відкрити зміст.