Slide 1

Slide 1 text

PHP Microservice Development by the Example of a Mail Service PHP UG Munich / 2016-09-28 / Max Kleucker / Scandio GmbH

Slide 2

Slide 2 text

SMTP SMTP SMTP Application 1 Application 2 Application X

Slide 3

Slide 3 text

SMTP CONFIGS SMTP CONFIGS EVERYWHERE

Slide 4

Slide 4 text

Ƙ SMTP Application 1 Application 2 Application X

Slide 5

Slide 5 text

Microservice Application 1 Ƙ Mailer Service

Slide 6

Slide 6 text

Application 1 Ƙ { Mailer Service REST
 Endpoint Business Logic Mail Delivery SMTP

Slide 7

Slide 7 text

Ƙ { Mailer Service REST
 Endpoint Business Logic Mail Delivery

Slide 8

Slide 8 text

Ƙ { Mailer Service REST
 Endpoint Business Logic Mail Delivery Queue

Slide 9

Slide 9 text

Why a Queue? • Substituting the Mail Delivery with a Mail Catcher • Additional Delivery mechanisms • Asynchronous if there are larger mail batches to be sent

Slide 10

Slide 10 text

Queues The focus of this talk.

Slide 11

Slide 11 text

Producer Consumer Queue creates a message fetches messages Queues Basics

Slide 12

Slide 12 text

"Vanilla" PHP • AMQP-Lib (https://github.com/php-amqplib/php-amqplib)

Slide 13

Slide 13 text

$connection = new AMQPStreamConnection('rabbitmq', 5672, 'guest', 'guest'); $channel = $connection->channel(); $channel->queue_declare('send_mail', false, false, false, false); $msg = new AMQPMessage('{"property": "value"}'); $channel->basic_publish($msg, '', 'send_mail'); $channel->close(); $connection->close(); PHP Enqueue a Message

Slide 14

Slide 14 text

{ "property": "value" } PHP Message Payload

Slide 15

Slide 15 text

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest'); $channel = $connection->channel(); $channel->queue_declare('send_mail', false, false, false, false); $callback = function($msg) { echo "Received ". $msg->body. "\n"; }; $channel->basic_consume('send_mail', '', false, true, false, false, $callback); while(count($channel->callbacks)) { $channel->wait(); } PHP Process a Message

Slide 16

Slide 16 text

Laravel • Using Laravel Queues and Jobs • vladimir-yuldashev/laravel-queue-rabbitmq

Slide 17

Slide 17 text

Laravel → RabbitMQ # .env QUEUE_DRIVER=rabbitmq RABBITMQ_HOST=rabbit1 RABBITMQ_PORT=5672 RABBITMQ_VHOST=/ RABBITMQ_LOGIN=guest RABBITMQ_PASSWORD=guest RABBITMQ_QUEUE=mailqueue Laravel

Slide 18

Slide 18 text

Job Definition namespace App\Jobs; class SendEmail implements ShouldQueue { use InteractsWithQueue, Queueable, SerializesModels; protected $mail; public function __construct(Mail $mail) { $this->mail = $mail; } } Laravel

Slide 19

Slide 19 text

$mail = new \App\Mail(); $mail->receiver = '[email protected]'; $mail->subject = 'This is an email!'; $mail->body = 'Moar awesome text.'; $job = new \App\Jobs\SendEmail($mail); dispatch($job->onQueue('mailqueue')); Laravel Enqueue a Message

Slide 20

Slide 20 text

Laravel Enqueue a Message $mail = new \App\Mail(); $mail->receiver = '[email protected]'; $mail->subject = 'This is an email!'; $mail->body = 'Moar awesome text.'; $job = new \App\Jobs\SendEmail($mail); dispatch($job->onQueue('mailqueue'));

Slide 21

Slide 21 text

Laravel Enqueue a Message $mail = new \App\Mail(); $mail->receiver = '[email protected]'; $mail->subject = 'This is an email!'; $mail->body = 'Moar awesome text.'; $job = new \App\Jobs\SendEmail($mail); dispatch($job->onQueue('mailqueue'));

Slide 22

Slide 22 text

Laravel Enqueue a Message $mail = new \App\Mail(); $mail->receiver = '[email protected]'; $mail->subject = 'This is an email!'; $mail->body = 'Moar awesome text.'; $job = new \App\Jobs\SendEmail($mail); dispatch($job->onQueue('mailqueue'));

Slide 23

Slide 23 text

{ "job": "Illuminate\\Queue\\CallQueuedHandler@call", "data": { "commandName": "App\\Jobs\\SendEmail", "command": "O:18:\"App\\Jobs\\SendEmail\":5:{s: 7:\"\u0000*\u0000mail\";O:45:\"Illuminate\\Contracts\\Database\ \ModelIdentifier\":2:{s:5:\"class\";s:8:\"App\\Mail\";s:2:\"id\";N;}s: 6:\"\u0000*\u0000job\";N;s:10:\"connection\";N;s:5:\"queue\";s: 9:\"mailqueue\";s:5:\"delay\";N;}" } } Laravel Message Payload

Slide 24

Slide 24 text

Laravel Message Payload { "job": "Illuminate\\Queue\\CallQueuedHandler@call", "data": { "commandName": "App\\Jobs\\SendEmail", "command": "O:18:\"App\\Jobs\\SendEmail\":5:{s: 7:\"\u0000*\u0000mail\";O:45:\"Illuminate\\Contracts\\Database\ \ModelIdentifier\":2:{s:5:\"class\";s:8:\"App\\Mail\";s:2:\"id\";N;}s: 6:\"\u0000*\u0000job\";N;s:10:\"connection\";N;s:5:\"queue\";s: 9:\"mailqueue\";s:5:\"delay\";N;}" } }

Slide 25

Slide 25 text

Laravel Process a Message class SendEmail implements ShouldQueue { use InteractsWithQueue, Queueable, SerializesModels; protected $mail; public function __construct(Mail $mail) { $this->mail = $mail; } public function handle() { $payload = $this→mail; } }

Slide 26

Slide 26 text

class SendEmail implements ShouldQueue { use InteractsWithQueue, Queueable, SerializesModels; protected $mail; public function __construct(Mail $mail) { $this->mail = $mail; } public function handle() { $payload = $this->mail; } } $ php artisan queue:work Laravel Process a Job

Slide 27

Slide 27 text

• Job-Object is used for creating and processing messages • Difficult if you want to use it between different applications • Data is persisted in the database, not available in the message • Low configuration overhead, easy to use Laravel Summary

Slide 28

Slide 28 text

Symfony • Using Base Symfony 3 Install • php-amqplib/rabbitmq-bundle

Slide 29

Slide 29 text

Symfony Symfony → RabbitMQ # app/config/config.yml old_sound_rabbit_mq: connections: default: host: 'rabbitmq' port: 5672 user: 'guest' password: 'guest' vhost: '/' lazy: false connection_timeout: 3 read_write_timeout: 3 producers: send_mail: connection: default exchange_options: {name: 'send-mail', type: direct} queue_options: {name: 'send-mail'} consumers: send_mail: connection: default exchange_options: {name: 'send-mail', type: direct} queue_options: {name: 'send-mail'} callback: send_mail_service

Slide 30

Slide 30 text

Symfony Enqueue a Message $mail = new Mail(); $mail->receiver = '[email protected]'; $mail->subject = 'This is an email!'; $mail->body = 'Moar awesome text.'; $this->get('old_sound_rabbit_mq.send_mail_producer') ->publish(serialize($mail));

Slide 31

Slide 31 text

Symfony Enqueue a Message $mail = new Mail(); $mail->receiver = '[email protected]'; $mail->subject = 'This is an email!'; $mail->body = 'Moar awesome text.'; $this->get('old_sound_rabbit_mq.send_mail_producer') ->publish(serialize($mail));

Slide 32

Slide 32 text

O:21:"AppBundle\Entity\Mail":3:{s:8:"receiver";s: 17:"[email protected]";s:7:"subject";s:17:"This is an email!";s:4:"body";s:18:"Moar awesome text.";} Symfony Message Payload

Slide 33

Slide 33 text

namespace AppBundle\Consumer; class SendMailConsumer implements ConsumerInterface { public function execute(AMQPMessage $msg) { $payload = $msg->getBody(); } } Symfony Process a Message • Must be registered as Service in Symfony

Slide 34

Slide 34 text

namespace AppBundle\Consumer; class SendMailConsumer implements ConsumerInterface { public function execute(AMQPMessage $msg) { $payload = $msg->getBody(); } } Symfony Process a Message $ bin/console rabbitmq:consumer -w send_mail

Slide 35

Slide 35 text

• More configuration necessary • Able to create and handle "bare" messages • Better suited when talking with other software Symfony Summary

Slide 36

Slide 36 text

Queues Summary • As always: Differentiation between comfort (Laravel) and power (Vanilla PHP, Symfony) • Personal Opinion: Better to stay close to independent messages

Slide 37

Slide 37 text

So, how is our Mailer-Service going?

Slide 38

Slide 38 text

Thank you. !