The PHP client is a plain PSR-4 library, Queen\ mapped onto src/, with an optional Laravel
layer on top. It needs PHP 8.3 and it is synchronous: every builder chain ends in an explicit
execute(), pop(), get() or commit().
composer require smartpricing/queen-mquse Queen\Queen;
$client = new Queen([
'urls' => ['http://broker-a:6632', 'http://broker-b:6632'],
'bearerToken' => getenv('QUEEN_TOKEN'),
'timeoutMillis' => 30000,
'loadBalancingStrategy' => 'affinity',
]);The constructor also takes a bare URL string or a list of URLs. One URL is a direct client, more
than one builds a load balancer that fails over on 5xx and network errors. The whole option
table is in Reference.
Push
$results = $client->queue('orders')
->partition('acct-42')
->push([['transactionId' => 'order-1-created', 'data' => ['id' => 1]]])
->execute();The queue and the partition are created by that call. execute() returns the broker’s array,
one entry per item, each carrying a status of queued, duplicate, buffered or failed.
Pass your own transactionId and a retry inside the dedup window writes nothing a second time.
Consume
$client->queue('jobs')
->group('workers')
->batch(10)
->each()
->consume(function (array $message) {
process($message['data']);
})
->execute();consume() returns a builder and execute() runs the worker loop, blocking until a signal, a
limit or an idle timeout stops it. With concurrency(1) it is one synchronous loop; above 1,
Guzzle’s async pool overlaps the long polls. A bare pop() makes the same claim without the
loop, long-polling for 30 seconds unless you pass ->wait(false).
Acknowledge
autoAck is client-side: the loop acks completed when your closure returns, failed when it
throws. Turn it off and settle the batch yourself.
$messages = $client->queue('orders')->group('billing')->batch(10)->pop();
$client->ack($messages, true, ['group' => 'billing']);ack() takes one message array, a list, or a bare transactionId, and partitionId is
mandatory. An ack is an offset commit, so a nack clamps the group’s cursor at the failed message
and everything after it in that batch comes back. A rejected ack still arrives as HTTP 200: read
success off each item.
Laravel
- Auto-discovery binds
Queen\Queenas a singleton and aliases theQueenfacade, so inject the class or callQueen::queue('orders')->push(...)->execute();. php artisan vendor:publish --tag=queen-configpublishesconfig/queen.php, which readsQUEEN_URL,QUEEN_BEARER_TOKENand the retry and load-balancing variables.php artisan queen:consume orders "App\\Queen\\OrderHandler" --group=processors --auto-ackruns a handler class as a worker; without--auto-ackit acknowledges nothing and every message is redelivered.
What differs here
- A chain that never reaches
execute()sends no request at all, and says nothing about it. - Without
each()the closure receives the array of popped messages, even whenbatchis1. pop()propagates exceptions instead of swallowing them into an empty array, and returns the result ofarray_filter, which keeps the original keys: usearray_values()for a list.- An
each()handler keeps running after a nack, so make it idempotent or throw out of a batch handler instead. - Signal handling needs the
pcntlextension; without it onlylimitoridleMillisstops the consume loop.
Client options with their defaults, the rdkafka-shaped high-level consumer, transactions,
buffering, the admin surface and the 429/403 error codes are in
the PHP reference.
Four tutorials follow, each one a program that runs and checks its own result, from a first push to a replay of everything already consumed. Start at Hello world.