RabbitMQ Fundamentals
Also Known As
TL;DR
Explanation
RabbitMQ's core model has four components: producers publish messages to exchanges; exchanges route messages to queues based on routing rules (direct, topic, fanout, or headers); queues store messages durably until consumed; consumers pull messages from queues and send acknowledgements. This separation of routing logic (exchange) from storage (queue) enables powerful patterns: fanout exchanges broadcast to all bound queues; topic exchanges route by wildcard patterns; dead letter exchanges catch failed messages for inspection. RabbitMQ supports message priorities, consumer prefetch limits (preventing one slow consumer from starving others), and publisher confirms (delivery guarantees from the broker). The standard PHP client is php-amqplib; Laravel's queue system supports RabbitMQ via the vladimir-yuldashev/laravel-queue-rabbitmq package.
Common Misconception
Why It Matters
Common Mistakes
- Not declaring queues as durable — messages are lost on RabbitMQ restart without durable queues and persistent message delivery mode.
- Not setting a prefetch count — without prefetch, RabbitMQ pushes all available messages to a consumer immediately; a slow consumer accumulates a large unacknowledged backlog.
- Forgetting to nack (negative acknowledge) on processing failure — unacknowledged messages block the queue until the consumer disconnects.
- Using the default exchange for routing — the default exchange only supports direct routing by queue name; define explicit exchanges for routing flexibility.
Code Examples
// Non-durable queue — lost on restart
$channel->queue_declare('emails', false, false, false, false);
// durable=false ^ lost on restart
// No ack — message hangs as unacknowledged if handler throws
$channel->basic_consume('emails', '', false, true, false, false,
function($msg) { sendEmail($msg->body); }
// no_ack=true ^ never acknowledged
// Durable queue + persistent messages
$channel->queue_declare('emails', false, true, false, false);
// durable=true ^
// Fair dispatch — one message at a time per worker
$channel->basic_qos(null, 1, null);
// Manual ack with error handling
$channel->basic_consume('emails', '', false, false, false, false,
function($msg) {
try {
sendEmail(json_decode($msg->body, true));
$msg->ack();
} catch (Throwable $e) {
$msg->nack(requeue: false); // send to dead letter exchange
logger()->error('Email job failed', ['error' => $e->getMessage()]);
}
}
);