Working with RabbitMQ in PHP
Publish and consume messages with RabbitMQ.
Working with RabbitMQ in PHP is a free PHP Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the PHP Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
RabbitMQ in PHP
RabbitMQ is a broker that speaks AMQP 0-9-1. In PHP the canonical client is php-amqplib/php-amqplib (pure PHP) or the C-backed ext-amqp. This lesson uses php-amqplib because it runs anywhere Composer does.
The core AMQP model has three actors: exchanges receive messages, bindings route them by key, and queues hold them for consumers. Master this and the rest is detail.
Installing the Client
Add the library with Composer. It needs the sockets and bcmath extensions, both common in CLI PHP builds.
composer require php-amqplib/php-amqplib
# Connection target, e.g. amqp://guest:guest@localhost:5672/The Exchange/Queue/Binding Model
Producers publish to an exchange, never directly to a queue. The exchange type decides routing:
direct— exact routing-key match.topic— wildcard patterns likeorder.*.eu.fanout— broadcast to all bound queues.headers— match on header attributes.
A binding connects a queue to an exchange with a routing pattern. Decoupling producers from queue topology is the whole point of the exchange layer.
Connecting & Declaring
Open a connection, get a channel, and declare your topology. durable: true makes the exchange/queue survive a broker restart. Declarations are idempotent — declaring an existing entity with matching args is a no-op.
<?php
require 'vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
$conn = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$ch = $conn->channel();
$ch->exchange_declare('orders', 'topic', false, true, false);
$ch->queue_declare('orders.email', false, true, false, false);
$ch->queue_bind('orders.email', 'orders', 'order.created');
echo "Topology ready\n";
$ch->close();
$conn->close();Publishing a Message
Wrap the body in an AMQPMessage. Set delivery_mode = 2 to make the message persistent — durable queue + persistent message is what survives a restart (one without the other does not).
<?php
require 'vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
$conn = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$ch = $conn->channel();
$payload = json_encode(['orderId' => 42, 'total' => 19.90]);
$msg = new AMQPMessage($payload, [
'content_type' => 'application/json',
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
'message_id' => bin2hex(random_bytes(8)),
]);
$ch->basic_publish($msg, 'orders', 'order.created');
echo "Published\n";
$ch->close();
$conn->close();Consuming with Manual Ack
Register a callback with basic_consume. Pass no_ack = false so you control acknowledgement. Ack only after the work succeeds; on failure, basic_nack with requeue lets RabbitMQ redeliver.
<?php
require 'vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
$conn = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$ch = $conn->channel();
$ch->basic_qos(null, 10, null); // prefetch 10
$callback = function ($msg) {
$data = json_decode($msg->getBody(), true);
try {
// ... do work ...
$msg->ack();
} catch (\Throwable $e) {
$msg->nack(true); // requeue
}
};
$ch->basic_consume('orders.email', '', false, false, false, false, $callback);
while ($ch->is_consuming()) {
$ch->wait();
}Prefetch & Fair Dispatch
By default RabbitMQ round-robins messages to consumers without regard to how busy each is — a slow consumer piles up work. basic_qos(null, prefetch, null) caps the number of unacknowledged messages a consumer may hold.
Set prefetch to a small number (e.g. 1–10) for heavy, uneven tasks so the broker sends new work only to consumers that have free capacity. This is fair dispatch.
Publisher Confirms
basic_publish returns immediately and does not tell you the broker accepted the message. For guaranteed publishing, enable publisher confirms: the broker sends an ack once the message is safely persisted/routed.
<?php
require 'vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
$conn = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$ch = $conn->channel();
$ch->confirm_select(); // enable confirms on this channel
$ch->set_ack_handler(fn($m) => print("confirmed\n"));
$ch->set_nack_handler(fn($m) => print("REJECTED\n"));
$ch->basic_publish(new AMQPMessage('hi'), 'orders', 'order.created');
$ch->wait_for_pending_acks(5.0); // block until confirmed or timeoutDead Lettering
Configure a queue's x-dead-letter-exchange argument so rejected (nack without requeue) or expired messages route to a DLX. Combine with x-delivery-limit on quorum queues to cap retries automatically.
<?php
use PhpAmqpLib\Wire\AMQPTable;
$args = new AMQPTable([
'x-dead-letter-exchange' => 'orders.dlx',
'x-dead-letter-routing-key' => 'order.failed',
'x-message-ttl' => 60000, // ms before expiry
]);
// false=passive, true=durable, false=exclusive, false=autodelete, args
$ch->queue_declare('orders.email', false, true, false, false, false, $args);
$ch->queue_declare('orders.dead', false, true, false, false);
$ch->queue_bind('orders.dead', 'orders.dlx', 'order.failed');Running Workers in Production
A few hard-won operational rules for PHP RabbitMQ workers:
- Run consumers as long-lived CLI processes under a supervisor (systemd / Supervisor) that restarts them on exit.
- PHP leaks memory over time — restart the worker after N messages or a memory threshold.
- Send AMQP heartbeats and handle
SIGTERMfor graceful shutdown (finish the in-flight message, then stop). - Use quorum queues for HA instead of legacy mirrored queues.
Graceful Shutdown
Deploys send SIGTERM. A naive worker dies mid-message, forcing a redelivery. Install a signal handler that flips a flag; finish the current message, ack it, then break the consume loop cleanly. pcntl_async_signals(true) lets PHP deliver the signal between AMQP waits.
<?php
pcntl_async_signals(true);
$running = true;
pcntl_signal(SIGTERM, function () use (&$running) {
$running = false; // stop after the current message
echo "SIGTERM: draining...\n";
});
while ($running && $ch->is_consuming()) {
try {
$ch->wait(null, false, 5); // wakes for signals
} catch (\PhpAmqpLib\Exception\AMQPTimeoutException $e) {
// idle tick - loop and re-check $running
}
}
$ch->close();
echo "Stopped cleanly\n";Quick Check
Surviving a broker restart.
Recap
You can now build a real RabbitMQ pipeline in PHP:
- Publish to an exchange; route via bindings to queues.
- Durable queue + persistent message survives restarts.
- Consume with manual ack and tune basic_qos prefetch for fair dispatch.
- Use publisher confirms for guaranteed sends and a DLX for poison messages.
- Run workers under a supervisor with heartbeats and graceful shutdown.
Next: Kafka, when you need high-throughput streaming instead of task queuing.
Frequently asked questions
Is the “Working with RabbitMQ in PHP” lesson free?
Yes — the full text of “Working with RabbitMQ in PHP” is free to read here on the web, and the PHP Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the PHP Academy course, upgrade to CoddyKit PRO.
What will I learn in “Working with RabbitMQ in PHP”?
Publish and consume messages with RabbitMQ. You practise PHP Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start PHP Academy?
No prior experience is required. PHP Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Working with RabbitMQ in PHP” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this PHP Academy lesson?
Yes. Every PHP Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Why Asynchronous Messaging
- Working with RabbitMQ in PHP
- Apache Kafka with PHP
- Building Event-Driven Workflows