Home

Awesome

BunnyPHP

Build Status Downloads this Month Latest stable

Performant pure-PHP AMQP (RabbitMQ) sync/async (ReactPHP) library

Requirements

BunnyPHP requires PHP 7.1 and newer.

Installation

Add as Composer dependency:

$ composer require bunny/bunny:@dev

Comparison

You might ask if there isn't a library/extension to connect to AMQP broker (e.g. RabbitMQ) already. Yes, there are multiple options:

Why should you want to choose BunnyPHP instead?

Apart from that BunnyPHP is more performant than main competing library, php-amqplib. See benchmark/ directory and php-amqplib's benchmark/.

Benchmarks were run as:

$ php benchmark/producer.php N & php benchmark/consumer.php
LibraryN (# messages)Produce secProduce msg/secConsume secConsume msg/sec
php-amqplib1000.013176330.04462242
bunnyphp1000.012878120.04882049
bunnyphp +/-+2.3%-8.6%
php-amqplib10000.121882100.48012082
bunnyphp10000.104295960.29193425
bunnyphp +/-+17%+64%
php-amqplib100001.107590295.18241929
bunnyphp100000.9078110152.90583441
bunnyphp +/-+22%+78%
php-amqplib10000020.7005483069.03601448
bunnyphp1000009.78911021535.73052789
bunnyphp +/-+111%+92%

Tutorial

Connecting

When instantiating the BunnyPHP Client accepts an array with connection options:

$connection = [
    'host'      => 'HOSTNAME',
    'vhost'     => 'VHOST',    // The default vhost is /
    'user'      => 'USERNAME', // The default user is guest
    'password'  => 'PASSWORD', // The default password is guest
];

$bunny = new Client($connection);
$bunny->connect();

Connecting with SSL/TLS

Options for SSL-connections should be specified as array ssl:

$connection = [
    'host'      => 'HOSTNAME',
    'vhost'     => 'VHOST',    // The default vhost is /
    'user'      => 'USERNAME', // The default user is guest
    'password'  => 'PASSWORD', // The default password is guest
    'ssl'       => [
        'cafile'      => 'ca.pem',
        'local_cert'  => 'client.cert',
        'local_pk'    => 'client.key',
    ],
];

$bunny = new Client($connection);
$bunny->connect();

For options description - please see SSL context options.

Note: invalid SSL configuration will cause connection failure.

See also common configuration variants.

Providing client properties

Client Connections can present their capabilities to a server by presenting an optional client_properties table when establishing a connection.

For example, a connection name may be provided by setting the connection_name property:

$connection = [
    'host'              => 'HOSTNAME',
    'vhost'             => 'VHOST',    // The default vhost is /
    'user'              => 'USERNAME', // The default user is guest
    'password'          => 'PASSWORD', // The default password is guest
    'client_properties' => [
        'connection_name' => 'My connection',
    ],
];

$bunny = new Client($connection);
$bunny->connect();

Publish a message

Now that we have a connection with the server we need to create a channel and declare a queue to communicate over before we can publish a message, or subscribe to a queue for that matter.

$channel = $bunny->channel();
$channel->queueDeclare('queue_name'); // Queue name

Publishing a message on a virtual host with quorum queues as a default

From RabbitMQ 4 queues will be standard defined as Quorum queues, those are by default durable, in order to connect to them you should use the queue declare method as follows. In the current version of RabbitMQ 3.11.15 this is already supported, if the virtual host is configured to have a default type of Quorum.

$channel = $bunny->channel();
$channel->queueDeclare('queue_name', false, true); // Queue name

With a communication channel set up, we can now publish a message to the queue:

$channel->publish(
    $message,    // The message you're publishing as a string
    [],          // Any headers you want to add to the message
    '',          // Exchange name
    'queue_name' // Routing key, in this example the queue's name
);

Subscribing to a queue

Subscribing to a queue can be done in two ways. The first way will run indefinitely:

$channel->run(
    function (Message $message, Channel $channel, Client $bunny) {
        $success = handleMessage($message); // Handle your message here

        if ($success) {
            $channel->ack($message); // Acknowledge message
            return;
        }

        $channel->nack($message); // Mark message fail, message will be redelivered
    },
    'queue_name'
);

The other way lets you run the client for a specific amount of time consuming the queue before it stops:

$channel->consume(
    function (Message $message, Channel $channel, Client $client){
        $channel->ack($message); // Acknowledge message
    },
    'queue_name'
);
$bunny->run(12); // Client runs for 12 seconds and then stops

Pop a single message from a queue

$message = $channel->get('queue_name');

// Handle message

$channel->ack($message); // Acknowledge message

Prefetch count

A way to control how many messages are prefetched by BunnyPHP when consuming a queue is by using the channel's QOS method. In the example below only 5 messages will be prefetched. Combined with acknowledging messages this turns into an effective flow control for your applications, especially asynchronous applications. No new messages will be fetched unless one has been acknowledged.

$channel->qos(
    0, // Prefetch size
    5  // Prefetch count
);

Asynchronous usage

Bunny supports both synchronous and asynchronous usage utilizing ReactPHP. The following example shows setting up a client and consuming a queue indefinitely.

(new Async\Client($eventLoop, $options))->connect()->then(function (Async\Client $client) {
   return $client->channel();
})->then(function (Channel $channel) {
   return $channel->qos(0, 5)->then(function () use ($channel) {
       return $channel;
   });
})->then(function (Channel $channel) use ($event) {
   $channel->consume(
       function (Message $message, Channel $channel, Async\Client $client) use ($event) {
           // Handle message

           $channel->ack($message);
       },
       'queue_name'
   );
});

AMQP interop

There is amqp interop compatible wrapper(s) for the bunny library.

Testing

Create client/server SSL certificates by running:

$ cd test/ssl && make all && cd -

You need access to a RabbitMQ instance in order to run the test suite. The easiest way is to use the provided Docker Compose setup to create an isolated environment, including a RabbitMQ container, to run the test suite in.

Docker Compose

Contributing

Broker compatibility

Works well with RabbitMQ

Does not work with ActiveMQ because it requires AMQP 1.0 which is a completely different protocol (Bunny is implementing AMQP 0.9.1)

License

BunnyPHP is licensed under MIT license. See LICENSE file.