Drivers overview
A driver is the only part of Hermes that talks to a queue. Emitter and Dispatcher know nothing about Redis, RabbitMQ, or SQS. They call a DriverInterface, and the driver turns that into commands for its broker.
What a driver does
DriverInterface has three methods:
public function send(MessageInterface $message, int $priority = Dispatcher::DEFAULT_PRIORITY): bool;
public function setupPriorityQueue(string $name, int $priority): void;
public function wait(Closure $callback, array $priorities): void;send()runs in the web process, insideEmitter::emit(). It serializes the message and writes it to the broker. It has to be fast, because the request waits for it.wait()runs in the worker, insideDispatcher::handle(). It loops, reads one message at a time, unserializes it, and calls$callback($message, $priority). The dispatcher's callback finds the handlers for the message type and runs them.setupPriorityQueue()registers an extra queue for a priority. The emitter and every worker must register the same ones.
Use the same driver class, with the same queue names, on both sides. They share nothing else.
Serialization
Every driver turns a message into a string with MessageSerializer, which writes JSON: id, type, payload, created time, executeAt, and the retry count. The payload must survive json_encode(). Call setSerializer() on the driver to use another format, and set the same serializer on both sides. See A serializer.
The priority is not part of the JSON. It only decides which queue send() writes to.
Priority, delay, and shutdown
The drivers differ in what they do with these:
- Priority.
RedisSetDriverandPredisSetDriverkeep one Redis set per priority.LazyRabbitMqDriverandAmazonSqsDriverthrowNotSupportedExceptionfromsetupPriorityQueue(). See Priority. - Delay. The Redis drivers put a message with a future
executeAtin a sorted set and move it to the queue when it is due. On 5.x onlyPredisSetDriverdoes that move correctly (#95). RabbitMQ and SQS deliver at once. See Delayed execution. - Shutdown and max items. All four drivers use
ShutdownTraitandMaxItemsTrait.wait()checks the shutdown signal and the processed count, and stops the worker cleanly. See Shutdown and max items.
How to choose
- You already run Redis: use
PredisSetDriver. It needs no PHP extension and works with delayed messages and retries on every version. - You need priorities: use
PredisSetDriverorRedisSetDriver. The other two have one queue. - You already run RabbitMQ: use
LazyRabbitMqDriver. Delay and retry need your own design. - You are on AWS and want managed storage: use
AmazonSqsDriver. It deletes the message only after the handler finished, so a crash leaves it for redelivery. - Something else: write a driver. It is three methods and three traits.
Driver pages
- ComparisonPriority, delay, persistence, and ack for all four drivers in one table.
- Redis (ext-redis)RedisSetDriver on the phpredis extension. Priorities and delayed messages.
- PredisPredisSetDriver, plain PHP. Priorities, delayed messages, and retries.
- RabbitMQLazyRabbitMqDriver on php-amqplib. One queue, no priorities, no delay.
- Amazon SQSAmazonSqsDriver on the AWS SDK. Managed storage, no priorities, no delay.
- Write your ownImplement DriverInterface for another broker or database.
In a framework
To pick the driver from config in an application, see Framework integration. The Nette, Laravel and Symfony pages use one DriverFactory that builds any of the four drivers.
FAQ
Can the emitter and the worker use different drivers?
No. They must use the same driver class with the same queue names and priority queues, because the queue is the only thing they share.
Which driver supports delayed messages and retries?
PredisSetDriver and, from 6.0, RedisSetDriver. In 5.x RedisSetDriver writes delayed messages to the sorted set but does not move them back to the queue. RabbitMQ and SQS deliver at once. See Delayed execution.
Does the driver acknowledge a message after the handler succeeds?
No. Every driver removes the message before the handler runs, so a worker that dies in handle() loses it. See the Ack row in the comparison.
Can I store messages in something other than JSON?
Yes. Implement SerializerInterface and call setSerializer() on the driver in the web process and in every worker. See A serializer.
What happens when I send to a priority that was never registered?
The Redis drivers throw UnknownPriorityException. RabbitMQ and SQS ignore the priority argument.