asynchronous php and real-time messaging

Post on 22-Apr-2015

3.165 Views

Category:

Engineering

12 Downloads

Preview:

Click to see full reader

DESCRIPTION

With every major browser supporting WebSockets, HTML 5 has changed how we handle client to server communications. The high demand for real time client and server messaging has developers flocking away from PHP to languages such as Node.js. In this session we'll explore the libraries and extensions that make Asynchronous PHP possible and analyze the performance differences with Node.js. In addition we'll identify use cases and walk through examples of how Asynchronous PHP can handle everything from WebSockets and Message Queues to MySQL.

TRANSCRIPT

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