• Stars
    star
    676
  • Rank 64,219 (Top 2 %)
  • Language
    PHP
  • License
    MIT License
  • Created about 9 years ago
  • Updated about 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

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?

  • You want nice idiomatic PHP API to work with (I'm looking at you, php-amqplib). BunnyPHP interface follows PHP's common coding standards and naming conventions. See tutorial.

  • You can't (don't want to) install PECL extension that has latest stable version in 2014. BunnyPHP isn't as such marked as stable yet. But it is already being used in production.

  • You have both classic CLI/FPM and ReactPHP applications and need to connect to RabbitMQ. BunnyPHP comes with both synchronous and asynchronous clients with same PHP-idiomatic interface. Async client uses react/promise.

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
Library N (# messages) Produce sec Produce msg/sec Consume sec Consume msg/sec
php-amqplib 100 0.0131 7633 0.0446 2242
bunnyphp 100 0.0128 7812 0.0488 2049
bunnyphp +/- +2.3% -8.6%
php-amqplib 1000 0.1218 8210 0.4801 2082
bunnyphp 1000 0.1042 9596 0.2919 3425
bunnyphp +/- +17% +64%
php-amqplib 10000 1.1075 9029 5.1824 1929
bunnyphp 10000 0.9078 11015 2.9058 3441
bunnyphp +/- +22% +78%
php-amqplib 100000 20.7005 4830 69.0360 1448
bunnyphp 100000 9.7891 10215 35.7305 2789
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.

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

  • Use Docker Compose to create a network with a RabbitMQ container and a PHP container to run the tests in. The project directory will be mounted into the PHP container.

    $ docker-compose up -d
    

    To test against different SSL configurations (as in CI builds), you can set environment variable CONFIG_NAME=rabbitmq.ssl.verify_none before running docker-compose up.

  • Optionally use docker ps to display the running containers.

    $ docker ps --filter name=bunny
    [...] bunny_rabbit_node_1_1
    [...] bunny_bunny_1
    
  • Enter the PHP container.

    $ docker exec -it bunny_bunny_1 bash
    
  • Within the container, run:

    $ vendor/bin/phpunit
    

Contributing

  • Large part of the PHP code (almost everything in Bunny\Protocol namespace) is generated from spec in file spec/amqp-rabbitmq-0.9.1.json. Look for DO NOT EDIT! in doc comments.

    To change generated files change spec/generate.php and run:

    $ php ./spec/generate.php

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.

More Repositories

1

chrome-devtools-protocol

Chrome Devtools Protocol client for PHP
PHP
166
star
2

ingress-merge

Merge Ingress Controller for Kubernetes
Go
140
star
3

btree

Append-only B+Tree implemented purely in PHP.
PHP
75
star
4

hit-server-bench

[ABANDONED] Comparison of many HTTP servers raw hit performance
Java
69
star
5

js2php

Javascript parser, compiler and interpreter written in PHP
PHP
49
star
6

couchdb-php

Simple PHP API to CouchDB
PHP
26
star
7

pacc

[OLD] use PHP7 rewrite https://github.com/axmachado/pacc (orig. description: pacc (PHP yACC), parser generator for PHP)
PHP
19
star
8

shopaholic

Shopaholic is an open-source e-shop based on PHP with Nette Framework and dibi under the hood.
PHP
18
star
9

reactphp-symfony

ReactPHP + Symfony example
PHP
14
star
10

phpeg

PEG for PHP
PHP
10
star
11

jeph

Application framework that runs Javascript on top of regular PHP engine
PHP
8
star
12

xo

Real-time multiplayer five-in-a-row Facebook game
JavaScript
7
star
13

fcgipass

Proxy HTTP requests to FastCGI server
Go
6
star
14

kube-jessie-ansible

[ABANDONED] Kubernetes set-up on Debian Jessie using Ansible
5
star
15

benchmarks

Some PHP benchmarks.
PHP
5
star
16

react-fcgi

[EXPERIMENTAL] Asynchronous FastCGI server built on ReactPHP
PHP
4
star
17

metatable

[ABANDONED] Simple way how to store and retrieve data, written natively in PHP.
PHP
3
star
18

bleve-additions

Some useful tools for Bleve
Go
3
star
19

nette-coffee

Nette Framework application where Presenters are generated from Coffeescript source
PHP
2
star
20

pssh

SSH server for any environment where PHP is
PHP
2
star
21

d4node

school project - simple UDP messaging with (sort of) auto-discovery
C
2
star
22

sally

Javascript library, which emulates some "advanced" CSS selectors (+, >, [attr]) for Microsoft Internet Explorer 6 using Sizzle selector library.
JavaScript
1
star
23

cc16-java-aop-multithreading

Codecamp 2016 AOP/multithreading lecture
Java
1
star
24

go-workshop

Go
1
star
25

guestbook

Guestbook using PHP and metatable
PHP
1
star
26

sandbox

Puts given program into sandbox.
C
1
star
27

cc16-java-elasticsearch

Codecamp 2016 Java Elasticsearch lecture
Java
1
star
28

nbh

neighbour discovery server
Erlang
1
star
29

xsession

xsession script that rewrites home on every X login
1
star
30

bayesian-spam-filter

Bayesian spam filter in Python
Python
1
star
31

dockerfiles

Custom Dockerfiles based on Debian Stretch.
Dockerfile
1
star