Swoole
RabbitMQ
PHP
Message Queue
Server Programming

Swoole with RabbitMQ

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Swoole and RabbitMQ solve different parts of the same backend problem. Swoole gives PHP a long-running, event-driven execution model, while RabbitMQ gives you durable queues and reliable message delivery. Putting them together works well, but only if you respect the fact that most RabbitMQ clients are blocking by default.

Decide What Runs in the Server and What Runs in the Consumer

The most important design choice is not the connection syntax. It is process layout. A Swoole HTTP server should usually publish messages quickly and return the response. Long-running consume() loops are better placed in a worker process or a separate command, because a blocking consumer inside the main server process can stall request handling.

That separation keeps responsibilities clear:

  • Swoole handles network requests and in-memory concurrency.
  • RabbitMQ buffers work between services.
  • A dedicated consumer process pulls jobs and acknowledges them only after success.

Install the RabbitMQ Pieces

bash
1sudo apt-get update
2sudo apt-get install rabbitmq-server
3sudo systemctl enable rabbitmq-server
4sudo systemctl start rabbitmq-server
5sudo rabbitmq-plugins enable rabbitmq_management
6pecl install amqp

After installing the amqp extension, enable it in php.ini and verify it is loaded with php -m | grep amqp.

Publish Jobs from a Swoole HTTP Handler

Publishing is the simple side of the integration. Open a connection, declare the exchange and queue, then publish a small message payload.

php
1<?php
2use Swoole\Http\Server;
3
4function rabbitConnection(): AMQPConnection
5{
6    $connection = new AMQPConnection([
7        'host' => '127.0.0.1',
8        'port' => 5672,
9        'vhost' => '/',
10        'login' => 'guest',
11        'password' => 'guest',
12    ]);
13    $connection->connect();
14    return $connection;
15}
16
17function publishJob(array $payload): void
18{
19    $connection = rabbitConnection();
20    $channel = new AMQPChannel($connection);
21
22    $exchange = new AMQPExchange($channel);
23    $exchange->setName('jobs');
24    $exchange->setType(AMQP_EX_TYPE_DIRECT);
25    $exchange->declareExchange();
26
27    $queue = new AMQPQueue($channel);
28    $queue->setName('thumbnail_jobs');
29    $queue->declareQueue();
30    $queue->bind('jobs', 'thumbnails.create');
31
32    $exchange->publish(
33        json_encode($payload, JSON_THROW_ON_ERROR),
34        'thumbnails.create',
35        AMQP_NOPARAM,
36        ['delivery_mode' => 2]
37    );
38
39    $connection->disconnect();
40}
41
42$server = new Server('127.0.0.1', 9501);
43
44$server->on('request', function ($request, $response) {
45    $file = $request->get['file'] ?? 'image.jpg';
46    publishJob(['file' => $file]);
47    $response->end("queued\n");
48});
49
50$server->start();

This gives you a fast request path. The HTTP layer does not perform the slow work itself. It just hands off a durable job.

Run the Consumer in a Separate Process

The common mistake is trying to call consume() directly inside the Swoole reactor. Because the AMQP extension is blocking, it is safer to isolate the consumer in a Swoole\Process or a separate worker command.

php
1<?php
2use Swoole\Process;
3
4$worker = new Process(function () {
5    $connection = rabbitConnection();
6    $channel = new AMQPChannel($connection);
7
8    $queue = new AMQPQueue($channel);
9    $queue->setName('thumbnail_jobs');
10    $queue->setFlags(AMQP_DURABLE);
11    $queue->declareQueue();
12
13    $queue->consume(function (AMQPEnvelope $message, AMQPQueue $queue) {
14        $payload = json_decode($message->getBody(), true, 512, JSON_THROW_ON_ERROR);
15
16        try {
17            echo "processing {$payload['file']}\n";
18            sleep(1);
19            $queue->ack($message->getDeliveryTag());
20        } catch (Throwable $e) {
21            $queue->nack($message->getDeliveryTag(), AMQP_REQUEUE);
22        }
23    });
24});
25
26$worker->start();

That layout preserves Swoole's responsiveness while still letting your PHP stack own message publication and consumption.

Use Acknowledgements and Durability Correctly

RabbitMQ is only reliable if you use its reliability features. Mark important queues durable, publish persistent messages, and acknowledge messages only after the work is actually complete. If you acknowledge too early, a crash can lose work. If you never acknowledge, the queue will keep redelivering the same message.

You should also keep payloads small. A message should usually contain identifiers, paths, or job parameters rather than a huge binary blob. Put large file content in object storage or a database and send only a reference through RabbitMQ.

Where Swoole Adds Value

Swoole is most useful here when you need high-throughput API endpoints, websockets, or internal services that queue work constantly. It is less about making RabbitMQ itself faster and more about making the producer side efficient:

  • accept requests without starting a fresh PHP process per request
  • keep connections and warm application state in memory
  • fan work out to queues instead of blocking on slow background tasks

That is a strong fit for image processing, notifications, indexing, and webhook ingestion.

Common Pitfalls

  • Running a blocking RabbitMQ consumer inside the main Swoole request loop.
  • Acknowledging messages before the task really finishes.
  • Sending large payloads through the queue instead of sending references.
  • Recreating topology inconsistently, such as using different exchange or routing key names in publishers and consumers.
  • Treating RabbitMQ as a substitute for application-level retry and error handling.

Summary

  • Use Swoole for the long-running network server and RabbitMQ for durable background work.
  • Keep the HTTP request path short by publishing a job and returning quickly.
  • Run blocking consumers in a separate process instead of inside the reactor.
  • Use durable queues, persistent messages, and manual acknowledgements for reliability.
  • Pass lightweight job data through RabbitMQ and keep heavy work in the consumer.

Course illustration
Course illustration

All Rights Reserved.