Decoupling Applications

With Message Queues


Symfony Con, Cluj
November 16th, 2017

© David Buchmann

TOC




An Introduction to Message Queues

Message Queues

Working with Messages: AMQP


=> tryrabbitmq.com/

For Example

            beanstalkd

      


    SQS    Azure    IBM MQ



Leverage Message Queues

Start using message queues

Producer - Consumer

AMQP 0.9 libraries

STOMP libraries

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

e.g. Rokka.io on AWS

Pattern: Decouple Event Listeners

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

e.g. Compile data into Elasticsearch

e.g. Compile data into Elasticsearch

Workers in PHP?

Task Runners

$ bin/console app:messages:image-worker
$ rabbitmq-cli-consumer \
  -e "bin/console app:messages:image-worker" \
  -c config.ini
            
# supervisor/imageworker.conf
[program:imageworker]
command=/usr/local/bin/rabbitmq-cli-consumer ...
process_name=imageworker_%(process_num)s
numprocs=4
autorestart=true
            

Worker scheduler

We built an autoscaler (in GO) that monitors queue sizes in RabbitMQ and adjusts the number of running worker instances to queue size

What can go wrong?

Everything

Log, Monitor, Alert

Missing Aknowledgement

e.g. Blowing up the queues

The Redelivery Trap: Retry




Conclusions

Benefits

Microservices

Serverless



Questions / Input / Feedback ?


https://joind.in/talk/6b11a


Twitter: @dbu