SlideShare une entreprise Scribd logo
1  sur  76
Télécharger pour lire hors ligne
PHP, RabbitMQ, and You
#mwphp14 #rabbitmq @jasonlotito
MidwestPHP 2014 - RabbitMQ
What will we be covering?
1. What is RabbitMQ
2. Technology Overview
3. Publishers
4. Consumers
5. Exchanges
6. Queues
7. Bindings
8. Carrot to make things easy
9. Publish events from the web
10.Multiple consumers
11.Management UI Publishing
12.Consumers Publishing
PHP, RabbitMQ, and You
#mwphp14 #rabbitmq @jasonlotito
Jason Lotito
Senior Architect @ MeetMe
@jasonlotito.com
github.com/jasonlotito@gmail.com
!
Senior Architect means people can blame
me when things don’t work as expected.
When things work, it’s because they
worked around my code.
Who has worked with
RabbitMQ in production?
Raise your hands. The only audience participation part, I promise.
Part 1
Crash Course In RabbitMQ
1. What is RabbitMQ
2. Technology Overview
3. Publishers
4. Consumers
5. Exchanges
6. Queues
7. Bindings
– RabbitMQ In Action*, Manning
“RabbitMQ is an open source message broker
and queueing server that can be used to let
disparate applications share data via a common
protocol, or to simply queue jobs for processing
by distributed workers.

”
Where RabbitMQ Sits
(P) Producer/Publisher - (X) Exchange - (C) Consumer
Event Occurs in Application
(P) Producer/Publisher - (X) Exchange - (C) Consumer
Message is Sent to Exchange
(P) Producer/Publisher - (X) Exchange - (C) Consumer
Message is Sent to Queue
(P) Producer/Publisher - (X) Exchange - (C) Consumer
Exchanges connect to Queues through Bindings
Message is Sent to Consumer
(P) Producer/Publisher - (X) Exchange - (C) Consumer
– Me, Now
“Where as a database handles your data,
a message queue handles your events.”
A database handles nouns.
A message queue handles verbs.
But enough talk
Let’s see some code!
composer.json
"require": {

"videlalvaro/php-amqplib": "2.2.*"

}
We are starting with the publisher
Publisher: send.php
<?php

// Setup, $ php send.php whatever you want to send

require_once 'vendor/autoload.php';

$config = require('config.php');

use PhpAmqpLibConnectionAMQPConnection;

use PhpAmqpLibMessageAMQPMessage;



// Message Prep

$connection = new AMQPConnection($config['mq']['host'],

$config['mq']['port'],

$config['mq']['user'],

$config['mq']['pass']);



$channel = $connection->channel();

$message = join(' ', array_splice($argv, 1));

$message = empty($message) ? 'Hello world!' : $message;



// Publish Message

$channel->basic_publish(new AMQPMessage( $message ), '', 'hello');

echo " [x] Sent '$message'n";

$channel->close();

$connection->close();

Publisher: send.php
<?php

// Setup, $ php send.php whatever you want to send

require_once 'vendor/autoload.php';

$config = require('config.php');

use PhpAmqpLibConnectionAMQPConnection;

use PhpAmqpLibMessageAMQPMessage;



// Message Prep

$connection = new AMQPConnection($config['mq']['host'],

$config['mq']['port'],

$config['mq']['user'],

$config['mq']['pass']);



$channel = $connection->channel();

$message = join(' ', array_splice($argv, 1));

$message = empty($message) ? 'Hello world!' : $message;



// Publish Message

$channel->basic_publish(new AMQPMessage( $message ), '', 'hello');

echo " [x] Sent '$message'n";

$channel->close();

$connection->close();

Publisher: send.php
<?php

// Setup, $ php send.php whatever you want to send

require_once 'vendor/autoload.php';

$config = require('config.php');

use PhpAmqpLibConnectionAMQPConnection;

use PhpAmqpLibMessageAMQPMessage;



// Message Prep

$connection = new AMQPConnection($config['mq']['host'],

$config['mq']['port'],

$config['mq']['user'],

$config['mq']['pass']);



$channel = $connection->channel();

$message = join(' ', array_splice($argv, 1));

$message = empty($message) ? 'Hello world!' : $message;



// Publish Message

$channel->basic_publish(new AMQPMessage( $message ), '', 'hello');

echo " [x] Sent '$message'n";

$channel->close();

$connection->close();

Publisher: send.php
<?php

// Setup, $ php send.php whatever you want to send

require_once 'vendor/autoload.php';

$config = require('config.php');

use PhpAmqpLibConnectionAMQPConnection;

use PhpAmqpLibMessageAMQPMessage;



// Message Prep

$connection = new AMQPConnection($config['mq']['host'],

$config['mq']['port'],

$config['mq']['user'],

$config['mq']['pass']);



$channel = $connection->channel();

$message = join(' ', array_splice($argv, 1));

$message = empty($message) ? 'Hello world!' : $message;



// Publish Message

$channel->basic_publish(new AMQPMessage( $message ), '', 'hello');

echo " [x] Sent '$message'n";

$channel->close();

$connection->close();

Publisher: send.php
<?php

// Setup, $ php send.php whatever you want to send

require_once 'vendor/autoload.php';

$config = require('config.php');

use PhpAmqpLibConnectionAMQPConnection;

use PhpAmqpLibMessageAMQPMessage;



// Message Prep

$connection = new AMQPConnection($config['mq']['host'],

$config['mq']['port'],

$config['mq']['user'],

$config['mq']['pass']);



$channel = $connection->channel();

$message = join(' ', array_splice($argv, 1));

$message = empty($message) ? 'Hello world!' : $message;



// Publish Message

$channel->basic_publish(new AMQPMessage( $message ), '', 'hello');

echo " [x] Sent '$message'n";

$channel->close();

$connection->close();

Now we create a consumer
Consumer: receive.php
<?php



require_once 'vendor/autoload.php';

$config = require('config.php');

use PhpAmqpLibConnectionAMQPConnection;



$connection = new AMQPConnection($config['mq']['host'],

$config['mq']['port'],

$config['mq']['user'],

$config['mq']['pass']);

$channel = $connection->channel();

$channel->queue_declare('hello', false, false, false, true);



echo ' [*] Waiting for messages. To exit press CTRL+C', "n";



$handler = function($message) use($channel){

echo sprintf('Message: %s' . PHP_EOL, $message->body);

};



$channel->basic_consume('hello', false, true, true, false, false,
$handler);

$channel->wait();
Consumer: receive.php
<?php



require_once 'vendor/autoload.php';

$config = require('config.php');

use PhpAmqpLibConnectionAMQPConnection;



$connection = new AMQPConnection($config['mq']['host'],

$config['mq']['port'],

$config['mq']['user'],

$config['mq']['pass']);

$channel = $connection->channel();

$channel->queue_declare('hello', false, false, false, true);



echo ' [*] Waiting for messages. To exit press CTRL+C', "n";



$handler = function($message) use($channel){

echo sprintf('Message: %s' . PHP_EOL, $message->body);

};



$channel->basic_consume('hello', false, true, true, false, false,
$handler);

$channel->wait();
Consumer: receive.php
<?php



require_once 'vendor/autoload.php';

$config = require('config.php');

use PhpAmqpLibConnectionAMQPConnection;



$connection = new AMQPConnection($config['mq']['host'],

$config['mq']['port'],

$config['mq']['user'],

$config['mq']['pass']);

$channel = $connection->channel();

$channel->queue_declare('hello', false, false, false, true);



echo ' [*] Waiting for messages. To exit press CTRL+C', "n";



$handler = function($message) use($channel){

echo sprintf('Message: %s' . PHP_EOL, $message->body);

};



$channel->basic_consume('hello', false, true, true, false, false,
$handler);

$channel->wait();
Consumer: receive.php
<?php



require_once 'vendor/autoload.php';

$config = require('config.php');

use PhpAmqpLibConnectionAMQPConnection;



$connection = new AMQPConnection($config['mq']['host'],

$config['mq']['port'],

$config['mq']['user'],

$config['mq']['pass']);

$channel = $connection->channel();

$channel->queue_declare('hello', false, false, false, true);



echo ' [*] Waiting for messages. To exit press CTRL+C', "n";



$handler = function($message) use($channel){

echo sprintf('Message: %s' . PHP_EOL, $message->body);

};



$channel->basic_consume('hello', false, true, true, false, false,
$handler);

$channel->wait();
Message is Sent to Exchange
(P) Producer/Publisher - (X) Exchange - (C) Consumer
Exchange Types
Direct, Fanout, and Topic
Queue bound to many exchanges
$msg = new AMQPMessage( $message );

$channel->basic_publish($msg, '', ‘messages.new');
* matches one word
# matches zero or more words
A word is delineated by .
*, #, and .
*.new
messages.*
NOT *.messages.*
messages.new matches
NOT spam.*
spam.*.*
spam.#
spam.message.new matches
Now Let’s Create an
Exchange and a Queue
Using the Management UI
rabbitmq-plugins enable rabbitmq_management
http://localhost:15672
We’ve Created Everything
Publishers, Exchanges, Bindings, Queues, and Consumers
So what can we do with this?
Part 2
PHP & RabbitMQ Together
1. Carrot to make things easy
2. Publish events from the web
3. Multiple consumers
4. Management UI Publishing
5. Consumers Publishing
Carrot
github.com/jasonlotito/Carrot
Carrot Consumer Code
<?php

require_once 'vendor/autoload.php';



use CarrotConsumer;
$queue = 'new_messages';

$handler = function($msg){

echo $msg, PHP_EOL;

return true;

};


(new Consumer())->listenTo($queue, $handler)

->listenAndWait();!
Carrot Publisher Code
<?php

require 'vendor/autoload.php';



use CarrotPublisher;



$msg = implode(' ', array_splice($argv, 1));

(new Publisher('messages'))

->publish('message.new', $msg);
Make publishing easy
Make consuming easier
github.com/jasonlotito/
midwest-rabbitmq/tree/carrot
Adding the Web
Publisher
$publisher = new Publisher('messages');

$sendCount = (int) (isset($_POST['simulatedMessageCount']) ?
$_POST['simulatedMessageCount'] : 1);



for($x = 0; $x<$sendCount; $x++){

if(isset($_POST['simulateWork'])) {

usleep(500000);

$msg = ['comment' => $_POST['comment'] . " $x"];

$publisher->eventuallyPublish('message.new', $msg);

} else {

$msg = ['comment' => $_POST['comment'] . " $x"];

$publisher->publish('message.new', $msg);

}

}
Let’s use the written
Carrot Consumer Code
batch_basic_publish
public function eventuallyPublish($routingKey, $message)

{

$msg = $this->buildMessage($message);

$channel = $this->getChannel();

$channel->batch_basic_publish($msg, $this->exchange,
$routingKey);

$this->registerShutdownHandler();

}



public function finallyPublish()

{

if ($this->doBatchPublish) {

$this->doBatchPublish = false;

$this->getChannel()->publish_batch();

}

}
!
// register finallyPublish
private function registerShutdownHandler();

Publish from the web
Let’s add another consumer
Without changing existing code
send text messages
$queueName = 'messages_for_nexmo';



(new Consumer())

->bind($queueName, 'messages', 'message.new')

->listenTo($queueName, function($msg) use($key, $secret, $phoneNumber) {

$msg = json_decode($msg);

$urlString = 'https://rest.nexmo.com/sms/json?api_key=%s&api_secret=%s' .

'&from=17088568489&to=%s&text=%s';

$preparedMessage = urlencode($msg->comment);

$url = sprintf($urlString, $key, $secret, $phoneNumber, $preparedMessage);

$res = file_get_contents($url);

$result = json_decode($res);

$messageResult = $result->messages[0];

echo "Message Result: " .

($messageResult->status === '0' ? 'Message Sent' : 'Message not sent')

. PHP_EOL;

return $messageResult->status === '0';

})->listenAndWait();

send text messages
$queueName = 'messages_for_nexmo';



(new Consumer())

->bind($queueName, 'messages', 'message.new')

->listenTo($queueName, function($msg) use($key, $secret, $phoneNumber) {

$msg = json_decode($msg);

$urlString = 'https://rest.nexmo.com/sms/json?api_key=%s&api_secret=%s' .

'&from=17088568489&to=%s&text=%s';

$preparedMessage = urlencode($msg->comment);

$url = sprintf($urlString, $key, $secret, $phoneNumber, $preparedMessage);

$res = file_get_contents($url);

$result = json_decode($res);

$messageResult = $result->messages[0];

echo "Message Result: " .

($messageResult->status === '0' ? 'Message Sent' : 'Message not sent')

. PHP_EOL;

return $messageResult->status === '0';

})->listenAndWait();

send text messages
$queueName = 'messages_for_nexmo';



(new Consumer())

->bind($queueName, 'messages', 'message.new')

->listenTo($queueName, function($msg) use($key, $secret, $phoneNumber) {

$msg = json_decode($msg);

$urlString = 'https://rest.nexmo.com/sms/json?api_key=%s&api_secret=%s' .

'&from=17088568489&to=%s&text=%s';

$preparedMessage = urlencode($msg->comment);

$url = sprintf($urlString, $key, $secret, $phoneNumber, $preparedMessage);

$res = file_get_contents($url);

$result = json_decode($res);

$messageResult = $result->messages[0];

echo "Message Result: " .

($messageResult->status === '0' ? 'Message Sent' : 'Message not sent')

. PHP_EOL;

return $messageResult->status === '0';

})->listenAndWait();

Both queues get the message
Let’s add another layer
Yo dawg, let’s have a
consumer publish
Send an email
After the text message
Create a new Exchange
Update text message consumer
$publisher = new Publisher('sms');



(new Consumer())

->bind($queueName, 'messages', 'message.new')

->listenTo($queueName, function($msg) use($key, $secret, $phoneNumber, $publisher) {

// existing code

$successful = $messageResult->status === '0';



if ($successful) {

$publisher->publish(‘sms.sent', ['message' => $msg->comment]);

}



return $successful;

})->listenAndWait();

Let’s write the email consumer
And I’ll also show you how to easily test them
Email Consumer
(new Consumer())

->bind('send_email', 'emails', '*.send')

->bind('send_email', 'sms', '*.sent')

->listenTo('send_email', function($message){

$msg = json_decode($message);

mail('jasonlotito@gmail.com', 'MidwestPHP RabbitMQ Talk',
$msg->message);

echo 'Message sent: ' . $msg->message . PHP_EOL;

return true;

})->listenAndWait();
Email consumer
(new Consumer())

->bind('send_email', 'emails', '*.send')

->bind('send_email', 'sms', '*.sent')

->listenTo('send_email', function($message){

$msg = json_decode($message);

mail('jasonlotito@gmail.com', 'MidwestPHP RabbitMQ Talk',
$msg->message);

echo 'Message sent: ' . $msg->message . PHP_EOL;

return true;

})->listenAndWait();
Now, let’s see it
from the beginning
rabbitmq.org
Thank you. Review: joind.in/10558
#midwestphp #rabbitmq
Questions? Feel free to stop and ask me, email, tweet, @jasonlotito@gmail.com

Contenu connexe

Tendances

Working with web_services
Working with web_servicesWorking with web_services
Working with web_services
Lorna Mitchell
 
Operation Oriented Web Applications / Yokohama pm7
Operation Oriented Web Applications / Yokohama pm7Operation Oriented Web Applications / Yokohama pm7
Operation Oriented Web Applications / Yokohama pm7
Masahiro Nagano
 
Perl web frameworks
Perl web frameworksPerl web frameworks
Perl web frameworks
diego_k
 
AnyMQ, Hippie, and the real-time web
AnyMQ, Hippie, and the real-time webAnyMQ, Hippie, and the real-time web
AnyMQ, Hippie, and the real-time web
clkao
 

Tendances (20)

Working with web_services
Working with web_servicesWorking with web_services
Working with web_services
 
Operation Oriented Web Applications / Yokohama pm7
Operation Oriented Web Applications / Yokohama pm7Operation Oriented Web Applications / Yokohama pm7
Operation Oriented Web Applications / Yokohama pm7
 
Perl web frameworks
Perl web frameworksPerl web frameworks
Perl web frameworks
 
Redis for your boss
Redis for your bossRedis for your boss
Redis for your boss
 
AnyMQ, Hippie, and the real-time web
AnyMQ, Hippie, and the real-time webAnyMQ, Hippie, and the real-time web
AnyMQ, Hippie, and the real-time web
 
Redis for your boss 2.0
Redis for your boss 2.0Redis for your boss 2.0
Redis for your boss 2.0
 
Asynchronous programming patterns in Perl
Asynchronous programming patterns in PerlAsynchronous programming patterns in Perl
Asynchronous programming patterns in Perl
 
Mojo as a_client
Mojo as a_clientMojo as a_client
Mojo as a_client
 
Webrtc mojo
Webrtc mojoWebrtc mojo
Webrtc mojo
 
Cli the other SAPI confoo11
Cli the other SAPI confoo11Cli the other SAPI confoo11
Cli the other SAPI confoo11
 
Zephir - A Wind of Change for writing PHP extensions
Zephir - A Wind of Change for writing PHP extensionsZephir - A Wind of Change for writing PHP extensions
Zephir - A Wind of Change for writing PHP extensions
 
ReactPHP
ReactPHPReactPHP
ReactPHP
 
Php on the Web and Desktop
Php on the Web and DesktopPhp on the Web and Desktop
Php on the Web and Desktop
 
CLI, the other SAPI phpnw11
CLI, the other SAPI phpnw11CLI, the other SAPI phpnw11
CLI, the other SAPI phpnw11
 
Developing apps using Perl
Developing apps using PerlDeveloping apps using Perl
Developing apps using Perl
 
Let's Talk Scope
Let's Talk ScopeLet's Talk Scope
Let's Talk Scope
 
Inside Bokete: Web Application with Mojolicious and others
Inside Bokete:  Web Application with Mojolicious and othersInside Bokete:  Web Application with Mojolicious and others
Inside Bokete: Web Application with Mojolicious and others
 
PHP
PHPPHP
PHP
 
RESTful web services
RESTful web servicesRESTful web services
RESTful web services
 
PECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life betterPECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life better
 

En vedette

Компания мечты своими руками, Уфа,
Компания мечты своими руками, Уфа, Компания мечты своими руками, Уфа,
Компания мечты своими руками, Уфа,
Alexander Byndyu
 
Карьера в IT, 27-02-2013
Карьера в IT, 27-02-2013Карьера в IT, 27-02-2013
Карьера в IT, 27-02-2013
Alexander Byndyu
 

En vedette (8)

Integrating RabbitMQ with PHP
Integrating RabbitMQ with PHPIntegrating RabbitMQ with PHP
Integrating RabbitMQ with PHP
 
ByndyuSoft 1 год глазами программиста
ByndyuSoft 1 год глазами программистаByndyuSoft 1 год глазами программиста
ByndyuSoft 1 год глазами программиста
 
Компания мечты своими руками, Уфа,
Компания мечты своими руками, Уфа, Компания мечты своими руками, Уфа,
Компания мечты своими руками, Уфа,
 
Карьера в IT, 27-02-2013
Карьера в IT, 27-02-2013Карьера в IT, 27-02-2013
Карьера в IT, 27-02-2013
 
Антон Довгоброд: Highload и очереди задач на примере PHP + Gearman + Yii2
Антон Довгоброд: Highload и очереди задач на примере PHP + Gearman + Yii2Антон Довгоброд: Highload и очереди задач на примере PHP + Gearman + Yii2
Антон Довгоброд: Highload и очереди задач на примере PHP + Gearman + Yii2
 
What RabbitMQ Can Do For You (Nomad PHP May 2014)
What RabbitMQ Can Do For You (Nomad PHP May 2014)What RabbitMQ Can Do For You (Nomad PHP May 2014)
What RabbitMQ Can Do For You (Nomad PHP May 2014)
 
Theres a rabbit on my symfony
Theres a rabbit on my symfonyTheres a rabbit on my symfony
Theres a rabbit on my symfony
 
Introduction to AMQP Messaging with RabbitMQ
Introduction to AMQP Messaging with RabbitMQIntroduction to AMQP Messaging with RabbitMQ
Introduction to AMQP Messaging with RabbitMQ
 

Similaire à PHP, RabbitMQ, and You

Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统
yiditushe
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook
guoqing75
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Tatsuhiko Miyagawa
 
The symfony platform: Create your very own framework (PHP Quebec 2008)
The symfony platform: Create your very own framework (PHP Quebec 2008)The symfony platform: Create your very own framework (PHP Quebec 2008)
The symfony platform: Create your very own framework (PHP Quebec 2008)
Fabien Potencier
 

Similaire à PHP, RabbitMQ, and You (20)

Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)
 
Tatsumaki
TatsumakiTatsumaki
Tatsumaki
 
How to build a High Performance PSGI/Plack Server
How to build a High Performance PSGI/Plack Server How to build a High Performance PSGI/Plack Server
How to build a High Performance PSGI/Plack Server
 
Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统
 
13 PHPUnit #burningkeyboards
13 PHPUnit #burningkeyboards13 PHPUnit #burningkeyboards
13 PHPUnit #burningkeyboards
 
Adding 1.21 Gigawatts to Applications with RabbitMQ (DPC 2015)
Adding 1.21 Gigawatts to Applications with RabbitMQ (DPC 2015)Adding 1.21 Gigawatts to Applications with RabbitMQ (DPC 2015)
Adding 1.21 Gigawatts to Applications with RabbitMQ (DPC 2015)
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook
 
PSGI and Plack from first principles
PSGI and Plack from first principlesPSGI and Plack from first principles
PSGI and Plack from first principles
 
PHP And Web Services: Perfect Partners
PHP And Web Services: Perfect PartnersPHP And Web Services: Perfect Partners
PHP And Web Services: Perfect Partners
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
 
Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!
 
Php hacku
Php hackuPhp hacku
Php hacku
 
Giới thiệu PHP 7
Giới thiệu PHP 7Giới thiệu PHP 7
Giới thiệu PHP 7
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
 
Simple callcenter platform with PHP
Simple callcenter platform with PHPSimple callcenter platform with PHP
Simple callcenter platform with PHP
 
Ch ch-changes cake php2
Ch ch-changes cake php2Ch ch-changes cake php2
Ch ch-changes cake php2
 
PhpBB meets Symfony2
PhpBB meets Symfony2PhpBB meets Symfony2
PhpBB meets Symfony2
 
Mojolicious
MojoliciousMojolicious
Mojolicious
 
Building and Incredible Machine with Pipelines and Generators in PHP (IPC Ber...
Building and Incredible Machine with Pipelines and Generators in PHP (IPC Ber...Building and Incredible Machine with Pipelines and Generators in PHP (IPC Ber...
Building and Incredible Machine with Pipelines and Generators in PHP (IPC Ber...
 
The symfony platform: Create your very own framework (PHP Quebec 2008)
The symfony platform: Create your very own framework (PHP Quebec 2008)The symfony platform: Create your very own framework (PHP Quebec 2008)
The symfony platform: Create your very own framework (PHP Quebec 2008)
 

Plus de Jason Lotito

Twitter Bootstrap, or why being a PHP Developer is a bad idea
Twitter Bootstrap, or why being a PHP Developer is a bad ideaTwitter Bootstrap, or why being a PHP Developer is a bad idea
Twitter Bootstrap, or why being a PHP Developer is a bad idea
Jason Lotito
 

Plus de Jason Lotito (9)

A Presentation on Presenting
A Presentation on PresentingA Presentation on Presenting
A Presentation on Presenting
 
Load Testing with PHP and RedLine13
Load Testing with PHP and RedLine13Load Testing with PHP and RedLine13
Load Testing with PHP and RedLine13
 
Load Testing with RedLine13: Or getting paid to DoS your own systems
Load Testing with RedLine13: Or getting paid to DoS your own systemsLoad Testing with RedLine13: Or getting paid to DoS your own systems
Load Testing with RedLine13: Or getting paid to DoS your own systems
 
tmux
tmuxtmux
tmux
 
Getting Things Done - Tips from someone with ADHD and OCD
Getting Things Done - Tips from someone with ADHD and OCDGetting Things Done - Tips from someone with ADHD and OCD
Getting Things Done - Tips from someone with ADHD and OCD
 
How we killed our process, technology stack, and assumptions – and survived
How we killed our process, technology stack, and assumptions – and survivedHow we killed our process, technology stack, and assumptions – and survived
How we killed our process, technology stack, and assumptions – and survived
 
Twitter Bootstrap, or why being a PHP Developer is a bad idea
Twitter Bootstrap, or why being a PHP Developer is a bad ideaTwitter Bootstrap, or why being a PHP Developer is a bad idea
Twitter Bootstrap, or why being a PHP Developer is a bad idea
 
Requirejs
RequirejsRequirejs
Requirejs
 
Tmux
TmuxTmux
Tmux
 

Dernier

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Victor Rentea
 

Dernier (20)

"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 

PHP, RabbitMQ, and You

  • 1. PHP, RabbitMQ, and You #mwphp14 #rabbitmq @jasonlotito MidwestPHP 2014 - RabbitMQ What will we be covering? 1. What is RabbitMQ 2. Technology Overview 3. Publishers 4. Consumers 5. Exchanges 6. Queues 7. Bindings 8. Carrot to make things easy 9. Publish events from the web 10.Multiple consumers 11.Management UI Publishing 12.Consumers Publishing
  • 2. PHP, RabbitMQ, and You #mwphp14 #rabbitmq @jasonlotito
  • 3. Jason Lotito Senior Architect @ MeetMe @jasonlotito.com github.com/jasonlotito@gmail.com ! Senior Architect means people can blame me when things don’t work as expected. When things work, it’s because they worked around my code.
  • 4. Who has worked with RabbitMQ in production? Raise your hands. The only audience participation part, I promise.
  • 5. Part 1 Crash Course In RabbitMQ 1. What is RabbitMQ 2. Technology Overview 3. Publishers 4. Consumers 5. Exchanges 6. Queues 7. Bindings
  • 6. – RabbitMQ In Action*, Manning “RabbitMQ is an open source message broker and queueing server that can be used to let disparate applications share data via a common protocol, or to simply queue jobs for processing by distributed workers. ”
  • 7. Where RabbitMQ Sits (P) Producer/Publisher - (X) Exchange - (C) Consumer
  • 8. Event Occurs in Application (P) Producer/Publisher - (X) Exchange - (C) Consumer
  • 9. Message is Sent to Exchange (P) Producer/Publisher - (X) Exchange - (C) Consumer
  • 10. Message is Sent to Queue (P) Producer/Publisher - (X) Exchange - (C) Consumer Exchanges connect to Queues through Bindings
  • 11. Message is Sent to Consumer (P) Producer/Publisher - (X) Exchange - (C) Consumer
  • 12. – Me, Now “Where as a database handles your data, a message queue handles your events.”
  • 13. A database handles nouns. A message queue handles verbs.
  • 14. But enough talk Let’s see some code!
  • 16. We are starting with the publisher
  • 17. Publisher: send.php <?php
 // Setup, $ php send.php whatever you want to send
 require_once 'vendor/autoload.php';
 $config = require('config.php');
 use PhpAmqpLibConnectionAMQPConnection;
 use PhpAmqpLibMessageAMQPMessage;
 
 // Message Prep
 $connection = new AMQPConnection($config['mq']['host'],
 $config['mq']['port'],
 $config['mq']['user'],
 $config['mq']['pass']);
 
 $channel = $connection->channel();
 $message = join(' ', array_splice($argv, 1));
 $message = empty($message) ? 'Hello world!' : $message;
 
 // Publish Message
 $channel->basic_publish(new AMQPMessage( $message ), '', 'hello');
 echo " [x] Sent '$message'n";
 $channel->close();
 $connection->close();

  • 18. Publisher: send.php <?php
 // Setup, $ php send.php whatever you want to send
 require_once 'vendor/autoload.php';
 $config = require('config.php');
 use PhpAmqpLibConnectionAMQPConnection;
 use PhpAmqpLibMessageAMQPMessage;
 
 // Message Prep
 $connection = new AMQPConnection($config['mq']['host'],
 $config['mq']['port'],
 $config['mq']['user'],
 $config['mq']['pass']);
 
 $channel = $connection->channel();
 $message = join(' ', array_splice($argv, 1));
 $message = empty($message) ? 'Hello world!' : $message;
 
 // Publish Message
 $channel->basic_publish(new AMQPMessage( $message ), '', 'hello');
 echo " [x] Sent '$message'n";
 $channel->close();
 $connection->close();

  • 19. Publisher: send.php <?php
 // Setup, $ php send.php whatever you want to send
 require_once 'vendor/autoload.php';
 $config = require('config.php');
 use PhpAmqpLibConnectionAMQPConnection;
 use PhpAmqpLibMessageAMQPMessage;
 
 // Message Prep
 $connection = new AMQPConnection($config['mq']['host'],
 $config['mq']['port'],
 $config['mq']['user'],
 $config['mq']['pass']);
 
 $channel = $connection->channel();
 $message = join(' ', array_splice($argv, 1));
 $message = empty($message) ? 'Hello world!' : $message;
 
 // Publish Message
 $channel->basic_publish(new AMQPMessage( $message ), '', 'hello');
 echo " [x] Sent '$message'n";
 $channel->close();
 $connection->close();

  • 20. Publisher: send.php <?php
 // Setup, $ php send.php whatever you want to send
 require_once 'vendor/autoload.php';
 $config = require('config.php');
 use PhpAmqpLibConnectionAMQPConnection;
 use PhpAmqpLibMessageAMQPMessage;
 
 // Message Prep
 $connection = new AMQPConnection($config['mq']['host'],
 $config['mq']['port'],
 $config['mq']['user'],
 $config['mq']['pass']);
 
 $channel = $connection->channel();
 $message = join(' ', array_splice($argv, 1));
 $message = empty($message) ? 'Hello world!' : $message;
 
 // Publish Message
 $channel->basic_publish(new AMQPMessage( $message ), '', 'hello');
 echo " [x] Sent '$message'n";
 $channel->close();
 $connection->close();

  • 21. Publisher: send.php <?php
 // Setup, $ php send.php whatever you want to send
 require_once 'vendor/autoload.php';
 $config = require('config.php');
 use PhpAmqpLibConnectionAMQPConnection;
 use PhpAmqpLibMessageAMQPMessage;
 
 // Message Prep
 $connection = new AMQPConnection($config['mq']['host'],
 $config['mq']['port'],
 $config['mq']['user'],
 $config['mq']['pass']);
 
 $channel = $connection->channel();
 $message = join(' ', array_splice($argv, 1));
 $message = empty($message) ? 'Hello world!' : $message;
 
 // Publish Message
 $channel->basic_publish(new AMQPMessage( $message ), '', 'hello');
 echo " [x] Sent '$message'n";
 $channel->close();
 $connection->close();

  • 22. Now we create a consumer
  • 23. Consumer: receive.php <?php
 
 require_once 'vendor/autoload.php';
 $config = require('config.php');
 use PhpAmqpLibConnectionAMQPConnection;
 
 $connection = new AMQPConnection($config['mq']['host'],
 $config['mq']['port'],
 $config['mq']['user'],
 $config['mq']['pass']);
 $channel = $connection->channel();
 $channel->queue_declare('hello', false, false, false, true);
 
 echo ' [*] Waiting for messages. To exit press CTRL+C', "n";
 
 $handler = function($message) use($channel){
 echo sprintf('Message: %s' . PHP_EOL, $message->body);
 };
 
 $channel->basic_consume('hello', false, true, true, false, false, $handler);
 $channel->wait();
  • 24. Consumer: receive.php <?php
 
 require_once 'vendor/autoload.php';
 $config = require('config.php');
 use PhpAmqpLibConnectionAMQPConnection;
 
 $connection = new AMQPConnection($config['mq']['host'],
 $config['mq']['port'],
 $config['mq']['user'],
 $config['mq']['pass']);
 $channel = $connection->channel();
 $channel->queue_declare('hello', false, false, false, true);
 
 echo ' [*] Waiting for messages. To exit press CTRL+C', "n";
 
 $handler = function($message) use($channel){
 echo sprintf('Message: %s' . PHP_EOL, $message->body);
 };
 
 $channel->basic_consume('hello', false, true, true, false, false, $handler);
 $channel->wait();
  • 25. Consumer: receive.php <?php
 
 require_once 'vendor/autoload.php';
 $config = require('config.php');
 use PhpAmqpLibConnectionAMQPConnection;
 
 $connection = new AMQPConnection($config['mq']['host'],
 $config['mq']['port'],
 $config['mq']['user'],
 $config['mq']['pass']);
 $channel = $connection->channel();
 $channel->queue_declare('hello', false, false, false, true);
 
 echo ' [*] Waiting for messages. To exit press CTRL+C', "n";
 
 $handler = function($message) use($channel){
 echo sprintf('Message: %s' . PHP_EOL, $message->body);
 };
 
 $channel->basic_consume('hello', false, true, true, false, false, $handler);
 $channel->wait();
  • 26. Consumer: receive.php <?php
 
 require_once 'vendor/autoload.php';
 $config = require('config.php');
 use PhpAmqpLibConnectionAMQPConnection;
 
 $connection = new AMQPConnection($config['mq']['host'],
 $config['mq']['port'],
 $config['mq']['user'],
 $config['mq']['pass']);
 $channel = $connection->channel();
 $channel->queue_declare('hello', false, false, false, true);
 
 echo ' [*] Waiting for messages. To exit press CTRL+C', "n";
 
 $handler = function($message) use($channel){
 echo sprintf('Message: %s' . PHP_EOL, $message->body);
 };
 
 $channel->basic_consume('hello', false, true, true, false, false, $handler);
 $channel->wait();
  • 27.
  • 28. Message is Sent to Exchange (P) Producer/Publisher - (X) Exchange - (C) Consumer
  • 30.
  • 31.
  • 32.
  • 33. Queue bound to many exchanges
  • 34. $msg = new AMQPMessage( $message );
 $channel->basic_publish($msg, '', ‘messages.new');
  • 35. * matches one word # matches zero or more words A word is delineated by . *, #, and .
  • 38. Now Let’s Create an Exchange and a Queue
  • 39. Using the Management UI rabbitmq-plugins enable rabbitmq_management http://localhost:15672
  • 40.
  • 41.
  • 42. We’ve Created Everything Publishers, Exchanges, Bindings, Queues, and Consumers
  • 43. So what can we do with this?
  • 44. Part 2 PHP & RabbitMQ Together 1. Carrot to make things easy 2. Publish events from the web 3. Multiple consumers 4. Management UI Publishing 5. Consumers Publishing
  • 46. Carrot Consumer Code <?php
 require_once 'vendor/autoload.php';
 
 use CarrotConsumer; $queue = 'new_messages';
 $handler = function($msg){
 echo $msg, PHP_EOL;
 return true;
 }; 
 (new Consumer())->listenTo($queue, $handler)
 ->listenAndWait();!
  • 47. Carrot Publisher Code <?php
 require 'vendor/autoload.php';
 
 use CarrotPublisher;
 
 $msg = implode(' ', array_splice($argv, 1));
 (new Publisher('messages'))
 ->publish('message.new', $msg);
  • 48.
  • 53. Publisher $publisher = new Publisher('messages');
 $sendCount = (int) (isset($_POST['simulatedMessageCount']) ? $_POST['simulatedMessageCount'] : 1);
 
 for($x = 0; $x<$sendCount; $x++){
 if(isset($_POST['simulateWork'])) {
 usleep(500000);
 $msg = ['comment' => $_POST['comment'] . " $x"];
 $publisher->eventuallyPublish('message.new', $msg);
 } else {
 $msg = ['comment' => $_POST['comment'] . " $x"];
 $publisher->publish('message.new', $msg);
 }
 }
  • 54. Let’s use the written Carrot Consumer Code
  • 55.
  • 56. batch_basic_publish public function eventuallyPublish($routingKey, $message)
 {
 $msg = $this->buildMessage($message);
 $channel = $this->getChannel();
 $channel->batch_basic_publish($msg, $this->exchange, $routingKey);
 $this->registerShutdownHandler();
 }
 
 public function finallyPublish()
 {
 if ($this->doBatchPublish) {
 $this->doBatchPublish = false;
 $this->getChannel()->publish_batch();
 }
 } ! // register finallyPublish private function registerShutdownHandler();

  • 57. Publish from the web Let’s add another consumer Without changing existing code
  • 58. send text messages $queueName = 'messages_for_nexmo';
 
 (new Consumer())
 ->bind($queueName, 'messages', 'message.new')
 ->listenTo($queueName, function($msg) use($key, $secret, $phoneNumber) {
 $msg = json_decode($msg);
 $urlString = 'https://rest.nexmo.com/sms/json?api_key=%s&api_secret=%s' .
 '&from=17088568489&to=%s&text=%s';
 $preparedMessage = urlencode($msg->comment);
 $url = sprintf($urlString, $key, $secret, $phoneNumber, $preparedMessage);
 $res = file_get_contents($url);
 $result = json_decode($res);
 $messageResult = $result->messages[0];
 echo "Message Result: " .
 ($messageResult->status === '0' ? 'Message Sent' : 'Message not sent')
 . PHP_EOL;
 return $messageResult->status === '0';
 })->listenAndWait();

  • 59. send text messages $queueName = 'messages_for_nexmo';
 
 (new Consumer())
 ->bind($queueName, 'messages', 'message.new')
 ->listenTo($queueName, function($msg) use($key, $secret, $phoneNumber) {
 $msg = json_decode($msg);
 $urlString = 'https://rest.nexmo.com/sms/json?api_key=%s&api_secret=%s' .
 '&from=17088568489&to=%s&text=%s';
 $preparedMessage = urlencode($msg->comment);
 $url = sprintf($urlString, $key, $secret, $phoneNumber, $preparedMessage);
 $res = file_get_contents($url);
 $result = json_decode($res);
 $messageResult = $result->messages[0];
 echo "Message Result: " .
 ($messageResult->status === '0' ? 'Message Sent' : 'Message not sent')
 . PHP_EOL;
 return $messageResult->status === '0';
 })->listenAndWait();

  • 60. send text messages $queueName = 'messages_for_nexmo';
 
 (new Consumer())
 ->bind($queueName, 'messages', 'message.new')
 ->listenTo($queueName, function($msg) use($key, $secret, $phoneNumber) {
 $msg = json_decode($msg);
 $urlString = 'https://rest.nexmo.com/sms/json?api_key=%s&api_secret=%s' .
 '&from=17088568489&to=%s&text=%s';
 $preparedMessage = urlencode($msg->comment);
 $url = sprintf($urlString, $key, $secret, $phoneNumber, $preparedMessage);
 $res = file_get_contents($url);
 $result = json_decode($res);
 $messageResult = $result->messages[0];
 echo "Message Result: " .
 ($messageResult->status === '0' ? 'Message Sent' : 'Message not sent')
 . PHP_EOL;
 return $messageResult->status === '0';
 })->listenAndWait();

  • 61. Both queues get the message
  • 62.
  • 64. Yo dawg, let’s have a consumer publish
  • 65. Send an email After the text message
  • 66. Create a new Exchange
  • 67. Update text message consumer $publisher = new Publisher('sms');
 
 (new Consumer())
 ->bind($queueName, 'messages', 'message.new')
 ->listenTo($queueName, function($msg) use($key, $secret, $phoneNumber, $publisher) {
 // existing code
 $successful = $messageResult->status === '0';
 
 if ($successful) {
 $publisher->publish(‘sms.sent', ['message' => $msg->comment]);
 }
 
 return $successful;
 })->listenAndWait();

  • 68. Let’s write the email consumer And I’ll also show you how to easily test them
  • 69. Email Consumer (new Consumer())
 ->bind('send_email', 'emails', '*.send')
 ->bind('send_email', 'sms', '*.sent')
 ->listenTo('send_email', function($message){
 $msg = json_decode($message);
 mail('jasonlotito@gmail.com', 'MidwestPHP RabbitMQ Talk', $msg->message);
 echo 'Message sent: ' . $msg->message . PHP_EOL;
 return true;
 })->listenAndWait();
  • 70. Email consumer (new Consumer())
 ->bind('send_email', 'emails', '*.send')
 ->bind('send_email', 'sms', '*.sent')
 ->listenTo('send_email', function($message){
 $msg = json_decode($message);
 mail('jasonlotito@gmail.com', 'MidwestPHP RabbitMQ Talk', $msg->message);
 echo 'Message sent: ' . $msg->message . PHP_EOL;
 return true;
 })->listenAndWait();
  • 71.
  • 72. Now, let’s see it from the beginning
  • 73.
  • 75.
  • 76. Thank you. Review: joind.in/10558 #midwestphp #rabbitmq Questions? Feel free to stop and ask me, email, tweet, @jasonlotito@gmail.com