asynchronous
steve rhoadestwitter+github @steverhoades
Asynchronous PHP
page scraper$urls = ['www.amazon.com', ...]; !foreach($urls as $url) { $content = file_get_contents( "http://$url", false, $context );}
request timewww.amazon.com: 0.80137www.reddit.com: 0.21584www.hackernews.com: 1.80921www.google.com: 0.29365www.yahoo.com: 1.39217!
Total time in seconds: 4.51274
time
call 2
Netw
ork
call 1Ne
twork
call 3
Netw
ork
blocking i/o
* Assuming ~1GB/sec SSD source: https://gist.github.com/jboner/2841832
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
I/O is slow.
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 }); });}
page scraper
www.amazon.com: 0.75559www.reddit.com: 0.33153www.hackernews.com: 0.57661www.google.com: 0.48226www.yahoo.com: 0.23333!
Total time: 0.76421
request time
call 2call 1 call 3create
create
non-blocking i/o
create
time
resp
resp
resp
reqreq
req
$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 } }
$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
non-blocking i/owww.reddit.com: 0.18858www.hackernews.com: 0.37317www.google.com: 0.10562www.yahoo.com: 0.10172www.amazon.com: 0.68584!
Total time: 0.69041(PHP Blocking Example: 4.51274)
event-drivennon-blocking i/o
Igor Wiedler
Christopher Boden#reactphp
igorw/evenementEventEmitter
on($event, callable $listener)$this->on('pipe', array($this, 'handleEvent'));
emit($event, array $arguments = [])$this->emit('data', array($data, $this));
once($event, callable $listener)$this->once('init', array($this, 'configure'));
event loop
service request
dispatcher
ticknextTickfutureTick
demultiplexertimers & streams
event handlercallback
even
t loop
periodic timers$loop->addPeriodicTimer(1, function($timer) { echo "Yes, I am annoying =)" . PHP_EOL;});!
one off timers$loop->addTimer(1, function($timer) { echo "I'm a one off timer.” . PHP_EOL;});
interval in seconds
callback Timer object
// cancel that annoying timer $timer->cancel(); });
timers
STREAMSStream($stream, $loop) events:
data, close, error, drain
$readStream = new Stream(fopen($file,"r"),$loop);$readStream->on('data',function($data, $stream) { //do something with $data});
readableStream
writeableStreamemit->(‘pipe’)
STREAMS & PIPING
on->(‘drain’)resume()on->(‘data’)
write($data)end()
end()pause()
if $data > $limit
$readStream->pipe($writeStream);
Promises/A
states
“A promise represents the eventual value returned from the single completion of an operation.”
“once fulfilled or rejected the promise’s value shall not be changed”
pending, fulfilled, rejected
http://wiki.commonjs.org/wiki/Promises/A
working with a Promise $loop = React\EventLoop\Factory::create(); $factory = new React\Dns\Resolver\Factory(); $dns = $factory->create('8.8.8.8', $loop);! $dns ->resolve('github.com') ->then(function ($ip) { echo "Host: $ip\n"; } ); $loop->run();
Deferred
working with Promise\all()
$promises = array( $file1->read(), $file2->read());!Promise\all($promises)->then(function($v){});Promise\race($promises)->then(function($v){});Promise\some($promises, 4)->then(function($v){});
$file1 = new file('test_file.txt', "r", $loop);$file2 = new file('test_file2.txt', "r", $loop);
Server events:connection, error
SOCKETS
Connectionextends Stream
events: data, close, error, drain
$loop = React\EventLoop\Factory::create(); $socket = new React\Socket\Server($loop); !$socket->on('connection', function($conn) { echo $conn->getRemoteAddress() . " connected" . PHP_EOL; $conn->on('data', function($data) { echo "Data received for connection" . PHP_EOL; }); }); !$socket->listen(4000); $loop->run();
page scraper$factory = new React\Dns\Resolver\Factory();$dns = $factory->create('8.8.8.8', $loop);$urls = ['www.amazon.com',...];$msg = "GET / HTTP/1.0\r\nAccept: */*\r\n\r\n";!foreach($urls as $url) { $connector = new React\SocketClient\Connector($loop, $dns); $connector->create($url, 80)->then( function (React\Stream\Stream $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 timewww.reddit.com: 0.47400www.hackernews.com: 0.41715www.google.com: 0.16216www.yahoo.com: 0.15773www.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 Eventssupported by all major browsers
• automatically re-connects • uni-directional • send arbitrary events
var source = new EventSource('stream.php');
source.addEventListener('message', function(e) { console.log(e.data);}, false);
demoserver sent events
WebSocketssupported by all major browsers
• new URI schemes ws and wss • bi-directional, full-duplex • send messages independently
WebSocketssupported by all major browsers
• multiplayer games • chat applications • social streams • auctions
WebSockets APIsupported 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();
RatchetWebSocket Support for • HTTP Server • handles WebSockets • supports the WAMP 1.0 protocol
$socketServer = new React\Socket\Server($loop);$socketServer->listen('8080', '0.0.0.0');$websocketServer = new IoServer( new HttpServer( new WsServer( $myWsApp ) ), $socketServer, $loop);
RatchetWebSocket Server
onOpen($conn)
onClose($conn)
onError($from, $error)
onMessage($from, $msg)
demoweb sockets
WAMPWeb Application Messaging Protocol
autobahn.js
Remote Procedure Calls Publish & Subscribe
$socketServer = new React\Socket\Server($loop);$socketServer->listen(8080, '0.0.0.0');$wampServer = new IoServer( new HttpServer( new WsServer( new WampServer( $myWampApp ) ) ), $socketServer, $loop);
Ratchet
onCall($conn, $id, $topic, $params)
onPublish($conn, $topic, $event)
onSubscribe($conn, $topic)
onUnsubscribe($conn, $topic)
Ratchet$conn->callResult($id,$this->playerData);wamp connection
$topic->broadcast($event, array($conn->WAMP->sessionId));topic$conn->callError($id, $topic, 'You are not allowed to make calls');
demoWAMP
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 PHPhttp://bit.ly/1nTAV4e - nikic
Q & A
http://socketo.me http://reactphp.org
Code Examples:https://github.com/steverhoades/drupalcampla
steve rhoadestwitter+github @steverhoades
Top Related