Message when a task is done, next worker listens to message
AMQP 0.9 libraries
php-amqlib: PHP only implementation
ext-amqp: Needs a compiled PHP module
BunnyPHP: PHP only, also works with ReactPHP
STOMP libraries
php-stomp: PHP only implementation
ext-stomp: Needs a compiled PHP module
queue-interop
queue-interop tries to identify and standardize a common way for PHP programs to create, send, receive and read MQ messages to achieve interoperability.
use M6Web\Bundle\AmqpBundle\Amqp\Producer;
use App\Message\ImageUploaded;
class ImageUploadedProducer
{
/** @var Producer */
private $producer;
public function publish(ImageUploaded $message)
{
return $this->producer->publishMessage(
json_encode($message),
AMQP_NOPARAM,
$message->getMessageProperties()
);
}
}
namespace App\Message;
class ImageUploaded implements \JsonSerializable
{
private $path;
private $mimeType;
public function __construct($path, ...
public function jsonSerialize() {
$message = ['path' => $this->path];
if ($this->mimeType) {
$message['mimeType'] = $this->mimeType;
}
return $message;
}
}
class ImageConvertProcessor
{
public function process(ImageUpdated $msg)
{
if (!$msg->getMimeType()) {
return;
}
if ($msg->getMimeType() === 'png') {
... $msg->getPath() ...
Patterns and Tools
Pattern: Delay slow task
Fast return, delay slow task
Cheap solution: Write to DB and cronjob
If result is needed by client: Poll for status of task that was triggered
e.g. Rokka.io on AWS
Respond with lower quality image transformation
Low cache lifetime
Send notification message
AWS lambda to do expensive transformation
Serve that image with long cache lifetime
Pattern: Decouple Event Listeners
Do not handle events inline
But send them to queue
Filter which events are relevant
Process queue and reproduce events
class WorkflowListener {
public function eventHappened(
string $type,
string $itemId
) {
foreach ($this->handlers as $handler) {
if ($handler->canHandle($type)) {
$this->messageProducer->publish(
$type,
$itemId
);
return;
}
}
}
}
class WorkflowListener {
...
public function handleEvent(
string $type,
string $itemId
) {
$item = $this->repo->getEntity($itemId);
foreach ($this->handlers as $handler) {
if ($handler->canHandle($type)) {
$handler->handle($type, $item);
}
}
}
}
Pattern: Processing lots of data
Split into subtasks
Each step has a persistent outcome & new message(s)
Scale individually
Different entry points
Priorities
e.g. Compile data into Elasticsearch
e.g. Compile data into Elasticsearch
SAP sends data - need to handle push as fast as possible, data delivery process is blocking in SAP
Dump data into message
Consumer to store data locally
New message that product needs reindexing
Price changes: higher priority
Changes from other data sources: Also message to reindex product