php artisan queen:consume reads a Queen queue and passes each message to a class of yours. Use it
for messages that other services push to Queen. Laravel jobs keep going through queue:work
(Laravel).
The command connects with the settings of config/queen.php, as the queue connection does
(QUEEN_URL, QUEEN_BEARER_TOKEN). The service provider registers it for the console only. This
page describes PHP client 2.3.0 and later; before 2.3.0 lists
what differs in earlier releases.
Try it
-
Write a handler. Any class with a
handle()method.<?php namespace App\Queen; use Illuminate\Support\Facades\Log; final class RecordPayment { public function handle(array $message): void { // $message['data'] is the payload the producer pushed. Log::info('payment received', $message['data']); } } -
Start the command. Quote the class name, or the shell removes its backslashes.
php artisan queen:consume payments \ 'App\Queen\RecordPayment' --group=ledger --auto-ackStarting Queen consumer on queue: payments Consumer group: ledger Consumer subscribed. Waiting for messages... (Ctrl+C to stop) -
Push a message, from
php artisan tinkeror from another application.Queen::queue('payments') ->push(['data' => ['paymentId' => 'p-1']]) ->execute();
Start the command before the first push. A new group starts with the messages pushed after its
first pop, unless you pass --subscription-mode=all
(where a new group starts).
The handler
- Built by the container. The command resolves the class with
app(), so constructor injection works. One instance handles every message of the run. - One message or a list.
handle()gets one message array. With--batchabove 1 it gets a list of up to that many messages. - The message.
$message['data']is the payload.transactionId,partitionId,leaseId,deliveryAttemptand the other keys are in the PHP client reference. - Exceptions. The command prints the exception’s message as an error line, nacks the message
(or the list) with
--auto-ackonly, then pops again. Laravel’s exception handler does not see it.
Ack and nack
handle() |
With --auto-ack |
Without --auto-ack |
|---|---|---|
| returns | the command acks the message | nothing is acked |
| throws | the command nacks it | nothing is nacked |
- A nack spends a retry. With
--auto-ack, a message that always throws reaches the dead-letter queue once the queue’s retries are spent (retry budget). The nack carries the exception’s message as its error. - Without
--auto-ack, the handler settles. A throw is printed, thenNot nacked without --auto-ack. The message comes back when its lease expires, which spends no retry. Nack it in the handler when a failure must count, as the SDK consumers expect. - Client-side. The command acks after
handle()returns, so delivery stays at-least-once. - One call per pop. With
--batch, one ack or nack covers the whole list. - Refusals are printed. A refused call prints one warning, for example
Ack refused for 1 of 1 message: invalid or expired lease.
Without --auto-ack, ack in the handler, with the group you passed:
<?php
namespace App\Queen;
use Queen\Laravel\QueenFacade as Queen;
final class RecordPayment
{
public function handle(array $message): void
{
// ... the work ...
Queen::ack($message, true, ['group' => 'ledger']);
}
}Without --group, leave group out of the context. With --auto-ack, leave the ack to the
command.
Lease
Every pop asks for a lease of --lease seconds, by default retry_after of config/queen.php
(90). Keep handle() well inside it, or turn on renewal.
- Renewal. With
lease_renewaltrue inconfig/queen.phpand--auto-ack, the command renews the lease whilehandle()runs, with the renewer ofqueue:work(lease renewal and fencing). - Timing. The
lease_renewal_*keys apply, measured against--lease. A timing that does not fit stops the command at start, with exit code 1 (lease renewal keys). - Fencing. When a renewal cannot finish in time, the renewer sends the command SIGTERM, then SIGKILL after the kill grace, before the lease ends.
- Before each ack or nack. The command checks the lease and stops its renewal. A lease that is
no longer safe is neither acked nor nacked: the command prints
Not acked:(orNot nacked:) and the reason, and the broker hands the message out again. - Without
--auto-ack. Renewal stays off, and the command prints one line to say so. A handler that acks by itself releases the lease, and renewing a released lease stops the process. - Long polls. The command counts the lease from the start of each pop. Keep
--timeoutwell below--lease: a message that arrives late in a long poll can leave too little lease to track, and the command then stops with exit code 1.
Options
| Option | Default | Effect |
|---|---|---|
--group= |
none | The consumer group. Unset: queue mode. |
--auto-ack |
off | Ack after handle() returns. |
--batch= |
1 |
Messages per pop. Above 1: a list. |
--partitions= |
the broker | Partitions per pop, under one lease. |
--no-autopilot |
off | Unset --partitions means 1. |
--lease= |
retry_after |
The lease of each pop, in seconds. |
--timeout= |
30000 |
Long poll of one pop, in ms. |
--subscription-mode= |
the broker | new or all, for a new group. |
--subscription-from= |
none | now or an ISO time, for a new group. |
--conflation |
off | The newest message per partition only. |
--limit= |
none | Stop after N messages handed out. |
--idle-timeout= |
none | Stop after N ms without a message. |
--partitions. The partitions of one pop share the--batchbudget.--no-autopilot. The broker stops choosing the partitions.QUEEN_SDK_POP_AUTOPILOT=offdoes the same for the whole process.--lease. An integer from 1 to 2147483647. Another value stops the command with exit code 1.--conflation. It needs--groupand broker 1.1.0 or later. It works with--auto-ack.--limit. It counts every message handed tohandle(), failed ones too. A pop asks for at most the messages the limit leaves, so the command never handles more than N.--idle-timeout. Counted from the start, then from the end of the lasthandle(). A long poll never waits past it. The command printsNo message for N ms (--idle-timeout): stopping.and exits with code 0.
Before production
--grouphas no default. The command does not readQUEEN_CONSUMER_GROUP, which is the group of the queue connection.- No restart on deploy. The command does not watch
queue:restart. Restart it with your process manager.
Stop and exit codes
SIGINT (Ctrl+C) and SIGTERM stop the command after the pop or the message in progress. It then
prints Consumer stopped. Processed N messages. This needs pcntl, and the handlers the command
installs replace yours for those two signals.
A pop that times out, or fails with a connection error, counts as an empty pop. After a connection
error the command waits 1 s before the next pop. It also prints Queen broker unreachable: and the
error, again at most every 30 s while it lasts, and Queen broker reachable again after N s. once
a pop gets an answer.
| Exit code | When |
|---|---|
0 |
stopped by a signal, --limit or --idle-timeout |
1 |
the handler class does not exist or has no handle() |
1 |
an invalid lease, or a renewal timing that does not fit it |
1 |
an error left the loop; Laravel reports it |
Errors that leave the loop include:
ConflationUnsupportedException.--conflationagainst a broker older than 1.1.0.ConflationPolicyMismatchException. The group conflates, and the command has no--conflation.HttpException. The broker refused the pop, for example with a 403.
Before PHP client 2.3.0
- The nack carried no error. With
--auto-ack, a throw was nacked without the exception’s message. - No
--leaseand no renewal. Every pop got the queue’s own lease time. --idle-timeouthad no effect. The command ran until a signal or--limit.--limitcounted only successes. With--batchit could handle up to--batchminus 1 messages more than N.- Nothing was printed for a refused ack or for a broker the pops could not reach.
The command is a loop around the client’s
KafkaConsumer-style consumer.
Write your own loop with Queen::queue('payments')->getConsumer() when you need another ack policy.