SlideShare a Scribd company logo
asynchronous
steve rhoades twitter+github @steverhoades
Asynchronous PHP and Real-time Messaging
Asynchronous PHP
page scraper 
$urls = ['www.amazon.com', ...]; 
! 
foreach($urls as $url) { 
$content = file_get_contents( 
"http://$url", false, $context 
); 
}
request time 
www.amazon.com: 0.80137 
www.reddit.com: 0.21584 
www.hackernews.com: 1.80921 
www.google.com: 0.29365 
www.yahoo.com: 1.39217 
! 
Total time in seconds: 4.51274
time 
call 2 
Network 
call 1 
Network 
call 3 
Network 
blocking i/o
I/O is slow. 
Send 1K bytes over Gbps network 0.01 ms 
Read 4K randomly from SSD* 0.15 ms 
Read 1 MB sequentially from memory 0.25 ms 
Round trip within same datacenter 0.5 ms 
Read 1 MB sequentially from SSD* 1 ms 
Disk seek 10 ms 
Read 1MB sequentially from disk 20 ms 
Send packet CA->Netherlands->CA 150 ms 
* Assuming ~1GB/sec SSD source: https://gist.github.com/jboner/2841832
page scraper 
var urls = ['http://www.amazon.com', ...]; 
! 
for(var i in urls) { 
http.get(urls[i], function(response) { 
var str = ''; 
response.on('data', function (chunk) { 
str += chunk; 
}); 
! 
response.on('end', function () { 
// do something with data 
}); 
}); 
}
request time 
www.amazon.com: 0.75559 
www.reddit.com: 0.33153 
www.hackernews.com: 0.57661 
www.google.com: 0.48226 
www.yahoo.com: 0.23333 
! 
Total time: 0.76421
call 1 call 2 call 3 
create 
create 
non-blocking i/o 
create 
time 
resp 
resp 
resp 
req 
req 
req
non-blocking i/o 
$urls = ['www.amazon.com',...]; 
! 
foreach($urls as $ip => $url) { 
// create a stream that returns immediately 
// STREAM_CLIENT_CONNECT _MUST_ be passed 
// STREAM_CLIENT_ASYNC_CONNECT says create connection 
// asynchronously 
$socket = stream_socket_client( 
"tcp://$ip:80", $errno, $errstr, 0, 
STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT 
); 
// set stream as non-blocking with 0, default is 1 
stream_set_blocking($socket, 0); 
! 
// sockets = read streams, requests = write streams 
$sockets[(int) $socket] = $socket; 
$requests[(int) $socket] = $socket; 
}
non-blocking i/o 
while(!empty($sockets)) { //run loop 
$read = $sockets; 
$write = $requests; 
$except = NULL; 
! 
// check the multiplexer for stream events 
$ready = stream_select($read, $write, $except, 0); 
foreach($read as $readFd) { 
// .. read 
} 
foreach($write as $writeFd) { 
// .. write 
} 
}
non-blocking i/o 
www.reddit.com: 0.18858 
www.hackernews.com: 0.37317 
www.google.com: 0.10562 
www.yahoo.com: 0.10172 
www.amazon.com: 0.68584 
! 
Total time: 0.69041 
(PHP Blocking Example: 4.51274)
event-driven 
non-blocking i/o 
Igor Wiedler 
Christopher Boden 
#reactphp
igorw/evenement EventEmitter 
emit($event, array $arguments = []) 
$this->emit('data', array($data, $this)); 
on($event, callable $listener) 
$this->on('pipe', array($this, 'handleEvent')); 
once($event, callable $listener) 
$this->once('init', array($this, 'configure'));
service request 
event loop 
dispatcher 
tick 
nextTick 
futureTick 
timers & streams 
demultiplexer 
event handler 
callback
event loop 
interval 
in seconds 
callback 
Timer object 
timers 
one off timers 
$loop->addTimer(1, function($timer) { 
echo "I'm a one off timer.” . PHP_EOL; 
}); 
periodic timers 
$loop->addPeriodicTimer(1, function($timer) { 
echo "Yes, I am annoying =)" . PHP_EOL; 
}); 
// cancel that annoying timer 
! 
$timer->cancel(); 
});
STREAMS 
Stream($stream, $loop) events: 
data, close, error, drain 
$readStream = new Stream(fopen($file,"r"),$loop); 
$readStream->on('data',function($data, $stream) { 
//do something with $data 
});
$readStream->pipe($writeStream); 
readableStream 
on->(‘data’) resume() 
writeableStream 
emit->(‘pipe’) 
STREAMS & PIPING 
on->(‘drain’) 
write($data) 
end() 
end() 
pause() 
if $data > $limit
Promises/A 
states 
“A promise represents the eventual value returned 
from the single completion of an operation.” 
pending, fulfilled, rejected 
“once fulfilled or rejected the promise’s 
value shall not be changed” 
http://wiki.commonjs.org/wiki/Promises/A
working with a Promise 
$loop = ReactEventLoopFactory::create(); 
$factory = new ReactDnsResolverFactory(); 
$dns = $factory->create('8.8.8.8', $loop); 
! 
$dns 
->resolve('github.com') 
->then(function ($ip) { 
echo "Host: $ipn"; 
} 
); 
$loop->run(); 
Deferred
working with Promiseall() 
$file1 = new file('test_file.txt', "r", $loop); 
$file2 = new file('test_file2.txt', "r", $loop); 
$promises = array( 
$file1->read(), 
$file2->read() 
); 
! 
Promiseall($promises)->then(function($v){}); 
Promiserace($promises)->then(function($v){}); 
Promisesome($promises, 4)->then(function($v){});
SOCKETS 
Server events: 
connection, error 
$loop = ReactEventLoopFactory::create(); 
$socket = new ReactSocketServer($loop); 
! 
$socket->on('connection', function($conn) { 
echo $conn->getRemoteAddress() . " connected" . PHP_EOL; 
$conn->on('data', function($data) { 
echo "Data received for connection" . PHP_EOL; 
Connection extends Stream 
events: data, close, error, drain 
}); 
}); 
! 
$socket->listen(4000); 
$loop->run();
page scraper 
$factory = new ReactDnsResolverFactory(); 
$dns = $factory->create('8.8.8.8', $loop); 
$urls = ['www.amazon.com',...]; 
$msg = "GET / HTTP/1.0rnAccept: */*rnrn"; 
! 
foreach($urls as $url) { 
$connector = new ReactSocketClientConnector($loop, $dns); 
$connector->create($url, 80)->then( 
function (ReactStreamStream $stream) use ($msg){ 
$stream->write($msg); 
$stream->on('data', function($data, $stream) { 
// buffer data 
} 
); 
$stream->on('end', function($stream) { 
// do something with the data 
$stream->close(); 
}); 
} 
); 
} 
$loop->run();
request time 
www.reddit.com: 0.47400 
www.hackernews.com: 0.41715 
www.google.com: 0.16216 
www.yahoo.com: 0.15773 
www.amazon.com: 0.65287 
! 
Total time: 0.69455 
(PHP Blocking Example: 4.51274)
Messaging
Messaging Techniques 
polling long polling 
(hanging GET) 
pre-
Messaging Techniques 
long polling 
(hanging GET) 
streaming 
pre-
Server Sent Events 
supported by all major browsers 
var source = new EventSource('stream.php'); 
• automatically re-connects 
• uni-directional 
• send arbitrary events 
source.addEventListener('message', function(e) { 
console.log(e.data); 
}, false);
demo server sent events
WebSockets 
supported by all major browsers 
• new URI schemes ws and wss 
• bi-directional, full-duplex 
• send messages independently
WebSockets 
supported by all major browsers 
• multiplayer games 
• chat applications 
• social streams 
• auctions
WebSockets API 
supported by all major browsers 
var ws = new WebSocket("ws://www.websockets.org"); 
ws.onopen = function(e) { 
console.log("Connection open ..."); 
}; 
ws.onmessage = function(e) { 
console.log( "Received Message: " + e.data); 
}; 
ws.onclose = function(e) { 
console.log("Connection closed."); 
}; 
ws.send("Hello WebSockets!"); 
ws.close();
Ratchet 
WebSocket Support for 
• HTTP Server 
• handles WebSockets 
• supports the WAMP 1.0 protocol
Ratchet WebSocket Server 
$socketServer = new ReactSocketServer($loop); 
$socketServer->listen('8080', '0.0.0.0'); 
$websocketServer = new IoServer( 
new HttpServer( 
new WsServer( 
$myWsApp 
) 
), 
$socketServer, 
$loop 
); 
onOpen($conn) 
onClose($conn) 
onError($from, $error) 
onMessage($from, $msg)
demo web sockets
WAMP 
Web Application Messaging Protocol 
Remote Procedure Calls 
Publish & Subscribe 
autobahn.js
Ratchet 
$socketServer = new ReactSocketServer($loop); 
$socketServer->listen(8080, '0.0.0.0'); 
$wampServer = new IoServer( 
new HttpServer( 
new WsServer( 
new WampServer( 
$myWampApp 
) 
) 
), 
$socketServer, 
$loop 
); 
onCall($conn, $id, $topic, $params) 
onPublish($conn, $topic, $event) 
onSubscribe($conn, $topic) 
onUnsubscribe($conn, $topic)
Ratchet 
wamp connection 
$conn->callResult($id,$this->playerData); 
$conn->callError($id, $topic, 'You are not allowed to make calls'); 
topic 
$topic->broadcast($event, array($conn->WAMP->sessionId));
demo WAMP
results based on: http://philsturgeon.uk/blog/2013/11/benchmarking-codswallop-nodejs-v-php
OTHER INTERESTING LIBRARIES: 
AMP: dispatch blocking calls to worker threads. 
https://github.com/rdlowrey/Amp 
INTERESTING READS: 
COOPERATIVE MULTI-TASKING USING COROUTINES IN PHP 
http://bit.ly/1nTAV4e - nikic
Q & A 
Code Examples: 
https://github.com/steverhoades/drupalcampla 
http://socketo.me http://reactphp.org
steve rhoades twitter+github @steverhoades

More Related Content

Asynchronous PHP and Real-time Messaging

  • 5. page scraper $urls = ['www.amazon.com', ...]; ! foreach($urls as $url) { $content = file_get_contents( "http://$url", false, $context ); }
  • 6. request time www.amazon.com: 0.80137 www.reddit.com: 0.21584 www.hackernews.com: 1.80921 www.google.com: 0.29365 www.yahoo.com: 1.39217 ! Total time in seconds: 4.51274
  • 7. time call 2 Network call 1 Network call 3 Network blocking i/o
  • 8. I/O is slow. Send 1K bytes over Gbps network 0.01 ms Read 4K randomly from SSD* 0.15 ms Read 1 MB sequentially from memory 0.25 ms Round trip within same datacenter 0.5 ms Read 1 MB sequentially from SSD* 1 ms Disk seek 10 ms Read 1MB sequentially from disk 20 ms Send packet CA->Netherlands->CA 150 ms * Assuming ~1GB/sec SSD source: https://gist.github.com/jboner/2841832
  • 9. page scraper var urls = ['http://www.amazon.com', ...]; ! for(var i in urls) { http.get(urls[i], function(response) { var str = ''; response.on('data', function (chunk) { str += chunk; }); ! response.on('end', function () { // do something with data }); }); }
  • 10. request time www.amazon.com: 0.75559 www.reddit.com: 0.33153 www.hackernews.com: 0.57661 www.google.com: 0.48226 www.yahoo.com: 0.23333 ! Total time: 0.76421
  • 11. call 1 call 2 call 3 create create non-blocking i/o create time resp resp resp req req req
  • 12. non-blocking i/o $urls = ['www.amazon.com',...]; ! foreach($urls as $ip => $url) { // create a stream that returns immediately // STREAM_CLIENT_CONNECT _MUST_ be passed // STREAM_CLIENT_ASYNC_CONNECT says create connection // asynchronously $socket = stream_socket_client( "tcp://$ip:80", $errno, $errstr, 0, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT ); // set stream as non-blocking with 0, default is 1 stream_set_blocking($socket, 0); ! // sockets = read streams, requests = write streams $sockets[(int) $socket] = $socket; $requests[(int) $socket] = $socket; }
  • 13. non-blocking i/o while(!empty($sockets)) { //run loop $read = $sockets; $write = $requests; $except = NULL; ! // check the multiplexer for stream events $ready = stream_select($read, $write, $except, 0); foreach($read as $readFd) { // .. read } foreach($write as $writeFd) { // .. write } }
  • 14. non-blocking i/o www.reddit.com: 0.18858 www.hackernews.com: 0.37317 www.google.com: 0.10562 www.yahoo.com: 0.10172 www.amazon.com: 0.68584 ! Total time: 0.69041 (PHP Blocking Example: 4.51274)
  • 15. event-driven non-blocking i/o Igor Wiedler Christopher Boden #reactphp
  • 16. igorw/evenement EventEmitter emit($event, array $arguments = []) $this->emit('data', array($data, $this)); on($event, callable $listener) $this->on('pipe', array($this, 'handleEvent')); once($event, callable $listener) $this->once('init', array($this, 'configure'));
  • 17. service request event loop dispatcher tick nextTick futureTick timers & streams demultiplexer event handler callback
  • 18. event loop interval in seconds callback Timer object timers one off timers $loop->addTimer(1, function($timer) { echo "I'm a one off timer.” . PHP_EOL; }); periodic timers $loop->addPeriodicTimer(1, function($timer) { echo "Yes, I am annoying =)" . PHP_EOL; }); // cancel that annoying timer ! $timer->cancel(); });
  • 19. STREAMS Stream($stream, $loop) events: data, close, error, drain $readStream = new Stream(fopen($file,"r"),$loop); $readStream->on('data',function($data, $stream) { //do something with $data });
  • 20. $readStream->pipe($writeStream); readableStream on->(‘data’) resume() writeableStream emit->(‘pipe’) STREAMS & PIPING on->(‘drain’) write($data) end() end() pause() if $data > $limit
  • 21. Promises/A states “A promise represents the eventual value returned from the single completion of an operation.” pending, fulfilled, rejected “once fulfilled or rejected the promise’s value shall not be changed” http://wiki.commonjs.org/wiki/Promises/A
  • 22. working with a Promise $loop = ReactEventLoopFactory::create(); $factory = new ReactDnsResolverFactory(); $dns = $factory->create('8.8.8.8', $loop); ! $dns ->resolve('github.com') ->then(function ($ip) { echo "Host: $ipn"; } ); $loop->run(); Deferred
  • 23. working with Promiseall() $file1 = new file('test_file.txt', "r", $loop); $file2 = new file('test_file2.txt', "r", $loop); $promises = array( $file1->read(), $file2->read() ); ! Promiseall($promises)->then(function($v){}); Promiserace($promises)->then(function($v){}); Promisesome($promises, 4)->then(function($v){});
  • 24. SOCKETS Server events: connection, error $loop = ReactEventLoopFactory::create(); $socket = new ReactSocketServer($loop); ! $socket->on('connection', function($conn) { echo $conn->getRemoteAddress() . " connected" . PHP_EOL; $conn->on('data', function($data) { echo "Data received for connection" . PHP_EOL; Connection extends Stream events: data, close, error, drain }); }); ! $socket->listen(4000); $loop->run();
  • 25. page scraper $factory = new ReactDnsResolverFactory(); $dns = $factory->create('8.8.8.8', $loop); $urls = ['www.amazon.com',...]; $msg = "GET / HTTP/1.0rnAccept: */*rnrn"; ! foreach($urls as $url) { $connector = new ReactSocketClientConnector($loop, $dns); $connector->create($url, 80)->then( function (ReactStreamStream $stream) use ($msg){ $stream->write($msg); $stream->on('data', function($data, $stream) { // buffer data } ); $stream->on('end', function($stream) { // do something with the data $stream->close(); }); } ); } $loop->run();
  • 26. request time www.reddit.com: 0.47400 www.hackernews.com: 0.41715 www.google.com: 0.16216 www.yahoo.com: 0.15773 www.amazon.com: 0.65287 ! Total time: 0.69455 (PHP Blocking Example: 4.51274)
  • 28. Messaging Techniques polling long polling (hanging GET) pre-
  • 29. Messaging Techniques long polling (hanging GET) streaming pre-
  • 30. Server Sent Events supported by all major browsers var source = new EventSource('stream.php'); • automatically re-connects • uni-directional • send arbitrary events source.addEventListener('message', function(e) { console.log(e.data); }, false);
  • 32. WebSockets supported by all major browsers • new URI schemes ws and wss • bi-directional, full-duplex • send messages independently
  • 33. WebSockets supported by all major browsers • multiplayer games • chat applications • social streams • auctions
  • 34. WebSockets API supported by all major browsers var ws = new WebSocket("ws://www.websockets.org"); ws.onopen = function(e) { console.log("Connection open ..."); }; ws.onmessage = function(e) { console.log( "Received Message: " + e.data); }; ws.onclose = function(e) { console.log("Connection closed."); }; ws.send("Hello WebSockets!"); ws.close();
  • 35. Ratchet WebSocket Support for • HTTP Server • handles WebSockets • supports the WAMP 1.0 protocol
  • 36. Ratchet WebSocket Server $socketServer = new ReactSocketServer($loop); $socketServer->listen('8080', '0.0.0.0'); $websocketServer = new IoServer( new HttpServer( new WsServer( $myWsApp ) ), $socketServer, $loop ); onOpen($conn) onClose($conn) onError($from, $error) onMessage($from, $msg)
  • 38. WAMP Web Application Messaging Protocol Remote Procedure Calls Publish & Subscribe autobahn.js
  • 39. Ratchet $socketServer = new ReactSocketServer($loop); $socketServer->listen(8080, '0.0.0.0'); $wampServer = new IoServer( new HttpServer( new WsServer( new WampServer( $myWampApp ) ) ), $socketServer, $loop ); onCall($conn, $id, $topic, $params) onPublish($conn, $topic, $event) onSubscribe($conn, $topic) onUnsubscribe($conn, $topic)
  • 40. Ratchet wamp connection $conn->callResult($id,$this->playerData); $conn->callError($id, $topic, 'You are not allowed to make calls'); topic $topic->broadcast($event, array($conn->WAMP->sessionId));
  • 42. results based on: http://philsturgeon.uk/blog/2013/11/benchmarking-codswallop-nodejs-v-php
  • 43. OTHER INTERESTING LIBRARIES: AMP: dispatch blocking calls to worker threads. https://github.com/rdlowrey/Amp INTERESTING READS: COOPERATIVE MULTI-TASKING USING COROUTINES IN PHP http://bit.ly/1nTAV4e - nikic
  • 44. Q & A Code Examples: https://github.com/steverhoades/drupalcampla http://socketo.me http://reactphp.org