53
Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

  • Upload
    polcode

  • View
    1.034

  • Download
    1

Embed Size (px)

Citation preview

Page 1: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Asynchroniczny PHP& komunikacja czasu

rzeczywistego z wykorzystaniemwebsocketów

Page 2: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

who am iNazywam się Łukasz AdamczewskiPracuje w firmie Polcode jako Starszy Programista PHP

Szukaj mnie na @lukeadamczewski

Page 3: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

- czyli wasz język tego nie potrafi

Page 4: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

CPU IO CPU

czas

IO

Model typowej aplikacji

Page 5: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

W czym problem?

Model typowej aplikacji

CPU IO CPU

czas

IO

Page 6: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

asynchroniczna instrukcja

CPU

Model non-blocking IO

IO

IO

CPU

IO

CPU

CZA

S

różny czas wykonyw

ania

IO

CPU

oczekiwanie na koniec poprzedniego zadania

Page 7: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Słów kilka o czasach dostępu czyli IO vs CPU

http://norvig.com/21-days.html#answers

fetch from L1 cache memory 0.5 nanosec

fetch from L2 cache memory 7 nanosec

fetch from main memory 100 nanosec

send 2K bytes over 1Gbps network 20,000 nanosec

read 1MB sequentially from memory 0,25 ms = 250,000 ns

Read 1 MB sequentially from SSD* 1 ms

Disk seek 10 ms

Read 1 MB sequentially from disk 20 ms

send packet US to Europe and back 150 ms

* Assuming ~1GB/sec SSD

Page 8: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

getUser(432, function (user) { console.log(user.name);}); console.log('Done');

Typowy node.js

Page 9: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

getUser(432, function (user) { console.log(user.name);}); console.log('Done');1

Typowy node.js

Page 10: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

getUser(432, function (user) { console.log(user.name);}); console.log('Done');1

2

Typowy node.js

Page 11: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Node.JS - webserver “hello world”

var http = require('http');

var server = new http.Server();

server.on('request', function (req, res) {

res.writeHead(

200,

{'Content-Type':'text/plain'}

);

res.end('Hello World');

});

server.listen(8000, '127.0.0.1');

Page 12: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Node.JS - webserver “hello world”

var http = require('http');

var server = new http.Server();

server.on('request', function (req, res) {

res.writeHead(

200,

{'Content-Type':'text/plain'}

);

res.end('Hello World');

});

server.listen(8000, '127.0.0.1');

oczekiwanie na zdarzenie request

kod obsługi zdarzenia wysyłający odpowiednie nagłówki do klienta HTTP

oraz wysyłający odpowiednią wiadomość

Page 13: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Node.JS - webserver “hello world”

var http = require('http');

var server = new http.Server();

server.on('request', function (req, res) {

res.writeHead(

200,

{'Content-Type':'text/plain'}

);

res.end('Hello World');

});

server.listen(8000, '127.0.0.1');

oczekiwanie na zdarzenie request

kod obsługi zdarzenia wysyłający odpowiednie nagłówki do klienta HTTP

oraz wysyłający odpowiednią wiadomość C10K problem

10k połączeń na jednym rdzeniu

Page 14: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Node.JS - webserver “hello world”

var http = require('http');

var server = new http.Server();

server.on('request', function (req, res) {

res.writeHead(

200,

{'Content-Type':'text/plain'}

);

res.end('Hello World');

});

server.listen(8000, '127.0.0.1');

oczekiwanie na zdarzenie request

kod obsługi zdarzenia wysyłający odpowiednie nagłówki do klienta HTTP

oraz wysyłający odpowiednią wiadomość C10K problem

10k połączeń na jednym rdzeniu

Page 15: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Zróbmy to w PHP!

bo w czym problem?

Page 16: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

<?php

$server = stream_socket_server('tcp://127.0.0.1:8000');

while ($conn = stream_socket_accept($server, -1)) {

fwrite($conn, "HTTP/1.1 200 OK\r\n");

fwrite($conn, "Content-Length: 3\r\n\r\n");

fwrite($conn, "Hi PHPers\n");

fclose($conn);

}

$ curl 127.0.0.1:8000 -v

> GET / HTTP/1.1

> Host: 127.0.0.1:8000

< HTTP/1.1 200 OK

Hi PHPers

Page 17: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

<?php

$server = stream_socket_server('tcp://127.0.0.1:8000');

while ($conn = stream_socket_accept($server, -1)) {

fwrite($conn, "HTTP/1.1 200 OK\r\n");

fwrite($conn, "Content-Length: 3\r\n\r\n");

fwrite($conn, "Hi PHPers\n");

fclose($conn);

}

BLOCKING

IO

Page 18: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

<?php

$server = stream_socket_server('tcp://127.0.0.1:8000');

while ($conn = stream_socket_accept($server, -1)) {

fwrite($conn, "HTTP/1.1 200 OK\r\n");

fwrite($conn, "Content-Length: 3\r\n\r\n");

fwrite($conn, "Hi PHPers\n");

fclose($conn);

}

BLOCKING

IO

NON-BLOCKING

IO// If mode is 0, the given stream will be switched to non-blocking modebool stream_set_blocking ( resource $stream , int $mode )

int stream_select ( array &$read , array &$write , array &$except , int $tv_sec [, int $tv_usec = 0 ] )

Page 19: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

$readable = $read ?: null;

$writable = $write ?: null;

$except = null;

if (stream_select($readable, $writable, $except, 1)) {

if ($readable) {

foreach ($readable as $stream) { /* code */ }

}

if ($writable) {

foreach ($writable as $stream) { /* code */ }

}

}

Pooling IO

Page 20: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

tablica streamów do odczytu

tablica streamów do zapisu

streamy “faworyzowane”

Pooling IOPooling IO$readable = $read ?: null;

$writable = $write ?: null;

$except = null;

if (stream_select($readable, $writable, $except, 1)) {

if ($readable) {

foreach ($readable as $stream) { /* code */ }

}

if ($writable) {

foreach ($writable as $stream) { /* code */ }

}

}

Page 21: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

EVENT LOOP

CZYLI MOŻNA LEPIEJ

Page 22: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

EVENT LOOP➜ Zarządzanie streamami

➜ Ustawianie timerów jednorazowych i cyklicznych

➜ Ustawianie nextTicków i futureTicków

➜ Maksymalna czas zwłoki (timeout) może być zdefiniowany

➜ Jeżeli nie ma dalszych ticków, timerów lub streamów do obsłużenia -

event loop kończy pracę

Page 23: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów
Page 24: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów
Page 25: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Event Loop:Istnieje kilka implementacji z docelowymi backendami:

● StreamSelectLoop - stream_select● LibEventLoop - libevent pecl extension● LibEvLoop - libev pecl extension (najszybszy)● może kiedyś LibUV :)

Page 26: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Demultiplexer:oczekuje na eventy dla zasobów np. nowe połączenie i powiadamia dispatcher, po czym przechodzi do dalszego nasłuchiwania.

Page 27: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Dispatcher:Komponent służący do rejestracji obsługi zdarzeń. Odbiera synchronicznie event z demultiplexera i wybiera właściwy event handler, który następnie uruchamia.

Page 28: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Obsługa zdarzeń:Event Handler który obsługuje przekierowane do niego zdarzenie. Jest to po prostu jeden ze zdefiniowanych wcześniej callbacków.

Page 29: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Zasoby:Czyli tutaj mamy wszystkie streamy które chcemy obsługiwać. Mogą to być także procesy czy np. uchwyty do plików.

Page 30: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Hello World Again

Page 31: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Instalacja z wykorzystaniem composera

curl -sS https://getcomposer.org/installer | php

php composer.phar require react/react

Page 32: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

require 'vendor/autoload.php';

$loop = React\EventLoop\Factory::create();

$socket = new React\Socket\Server($loop);

$http = new React\Http\Server($socket, $loop);

$http->on('request', function ($request, $response) {

$response->writeHead(200, ['Content-Type' =>

'text/plain']);

$response->end("Hi Phpers\n");

});

$socket->listen(8000);

$loop->run();

Page 33: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Ekosystem

EVENT LOOP

STREAM

TICKS

SOCKET

HTTP

TIMER

CHILD PROCESSFILESYSTEM

PROMISES

Page 34: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

EVENT LOOP

STREAM

TICKS

SOCKET

HTTP

TIMER

CHILD PROCESSFILESYSTEM

PROMISES

Tickiumożliwiają wykonywanie określonych funkcji w ramach Event Loopa. Dzielą się na $loop-

>nextTick($callback) i $loop->futureTick($callback) .

Pierwszy jest wykonywany zawsze na początku każdej iteracji loopa, wykonywanie drugiego

jest zawsze oddelegowane jako ostatnia operacja w ramach iteracji.

Hint: zakolejkowane futureTicki nie będą wykonywane jeśli Event Loop nie ma więcej

zadań. NextTicki uzupełniają Event Loop nowymi zadaniami.

Page 35: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

EVENT LOOP

STREAM

TICKS

SOCKET

HTTP

TIMER

CHILD PROCESSFILESYSTEM

PROMISES

Funkcja odmierzania czasu wykonywana w ramach Event Loop w kolejności zaraz po nextTicku.

● $loop->addTimer - jednorazowe wykonanie funkcji po upływie czasu (jak setTimeout)● $loop->addPeriodicTimer - wykonuje funkcje cyklicznie (jak setInterval)● $loop->cancelTimer - zatrzymje timer● $loop->isTimerActive - sprawdza stan działania timera

Hint: nie polegaj na czasie odmierzanym przez timery w 100% ponieważ operacje przetwarzane w Event Loop mogą zablokować je na jakiś czas wynikający z bieżących działań.

Page 36: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

STREAM

SOCKET

HTTP

CHILD PROCESSFILESYSTEM

PROMISES

● Opakowuje natywny zasób stream. ● Rejestrowane w ramach Event Loop’a.● Stream do odczytu i zapisu (ReadableStream / WriteableStream)● Potkowość (ang. pipeline). WriteableStream może być ze sobą łączone, więc wyjście

jednego jest wejściem drugiego.● Dane wczytywane / zapisywane do streamów są buforowane

Klasy Funkcjonalne:

CompositeStream - łączy streamy do odczytu i zapisu łącząc obydwie funkcjonalnościThroughStream - umożliwia modyfikacje danch które stream zawiera - filtrowanie. BufferedSink - konwertuje WriteableStream do Promise

STREAM

Page 37: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

STREAM

SOCKET

HTTP

CHILD PROCESSFILESYSTEM

PROMISES

Dla funkcji asynchronicznych umożliwia natychmiastowy zwrot wartości, a raczej pewnej zaliczki tej wartości.

Przykład - zamiana hosta na ip:

$factory = new React\Dns\Resolver\Factory();

$dns = $factory->create('8.8.8.8', $loop);

$dns->resolve('igor.io')->then(function ($ip) {

echo "Host: $ip\n";

});

PROMISE

Page 38: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

STREAM

SOCKET

HTTP

CHILD PROCESSFILESYSTEM

PROMISES

Komponent sieciowy umożliwiający tworzenie serwerów nasłuchujących nowych połączeń

oraz przetwarzających dane połączonych klientów.

$socket = new React\Socket\Server($loop);

$socket->on('connection', function ($conn) {

$conn->on('data', function ($data, $conn) {

$conn->write($data);

});

});

$socket->listen(1337);

SOCKET

Page 39: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

WebsocketyKomunikacja w czasie rzeczywistym

Page 40: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Websockety - zalety

➜ Wsparcie we wszystkich wiodących przeglądarkach (> IE8)

➜ Dwukierunkowość komunikacji➜ Niezależnie wysyłanie wiadomości➜ Protokół oparty na HTTP➜ Niewielki rozmiar pojedynczego pakietu

danych

Page 41: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

WebsocketAPI - interfejs kliencki JavaScript

var websocket = new WebSocket('ws://localhost:8000' );

websocket.onopen = function(evt) {};

websocket.onclose = function(evt) {};

websocket.onmessage = function(evt) {};

websocket.onerror = function(evt) {};

Page 42: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Ratchet - WebSockets for PHP

Page 43: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

class WS implements MessageComponentInterface {

function onOpen(ConnectionInterface $conn) {}

function onClose(ConnectionInterface $conn) {}

function onError(ConnectionInterface $conn, \Exception $e) {}

function onMessage(ConnectionInterface $from, $msg) {

$from->send($msg);

}

}

$server = IoServer::factory(

new HttpServer(new WsServer(new WS())),

3000

);

$server->run();

Page 44: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

The Web Application Messaging Protocol

Page 45: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

The Bigger The Better

➜ Remote Procedure Calls (RPC)➜ Publish & Subscribe➜ Autobahn.JS➜ integracja ZeroMQ➜ integracja Redis

Page 46: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Autobahn.JS?

// dla wersji AUTOBAHNJS_VERSION="0.7.1"var session = new ab.Session( "ws://127.0.0.1:3000", function () { // zostaliśmy połączeni }, function () { // zostaliśmy rozłączeni }, { 'skipSubprotocolCheck': true, 'maxRetries': 5, 'retryDelay': 2000 });session.subscribe("http://phpers.pl/event/message", callback);

Page 47: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

class WAMP implements Ratchet\Wamp\WampServerInterface {

public function onPublish(ConnectionInterface $conn, $topic, $event, array

$exclude, array $eligible) {}

public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {}

public function onSubscribe(ConnectionInterface $conn, $topic) {}

public function onUnSubscribe(ConnectionInterface $conn, $topic) {}

public function onOpen(ConnectionInterface $conn) {}

public function onClose(ConnectionInterface $conn) {}

public function onError(ConnectionInterface $conn, \Exception $e) {}

}

$server = \Ratchet\Server\IoServer::factory(

new HttpServer(new WsServer(new WampServer(new WAMP()))),

3000

);

$server->run();

Ratchet + WAMP

Page 48: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

$loop = React\EventLoop\Factory::create();

$pusher = new WAMP;

$context = new React\ZMQ\Context($loop);

$pull = $context->getSocket(ZMQ::SOCKET_PULL);

$pull->bind('tcp://127.0.0.1:5555');

$pull->on('message', array($pusher, 'onQueueAdded'));

$socket = new React\Socket\Server($loop);

$socket->listen(3000, '0.0.0.0');

$webServer = new IoServer(

new HttpServer(new WsServer(new WampServer($pusher))),

$socket

);

$loop->run();

ZeroMQ + Ratchet

Page 49: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

ZeroMQ + Ratchet

$loop = React\EventLoop\Factory::create();

$pusher = new WAMP;

$context = new React\ZMQ\Context($loop);

$pull = $context->getSocket(ZMQ::SOCKET_PULL);

$pull->bind('tcp://127.0.0.1:5555');

$pull->on('message', array($pusher, 'onQueueAdded'));

$socket = new React\Socket\Server($loop);

$socket->listen(3000, '0.0.0.0');

$webServer = new IoServer(

new HttpServer(new WsServer(new WampServer($pusher))),

$socket

);

$loop->run();

$context = new \ZMQContext(); $socket = $context->getSocket( \ZMQ::SOCKET_PUSH, 'websocket');$socket->connect('tcp://127.0.0.1:5555');$socket->send($payloadAsJSON);

Page 50: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

ZeroMQ + Ratchet

$loop = React\EventLoop\Factory::create();

$pusher = new WAMP;

$context = new React\ZMQ\Context($loop);

$pull = $context->getSocket(ZMQ::SOCKET_PULL);

$pull->bind('tcp://127.0.0.1:5555');

$pull->on('message', array($pusher, 'onQueueAdded'));

$socket = new React\Socket\Server($loop);

$socket->listen(3000, '0.0.0.0');

$webServer = new IoServer(

new HttpServer(new WsServer(new WampServer($pusher))),

$socket

);

$loop->run();

$context = new \ZMQContext(); $socket = $context->getSocket( \ZMQ::SOCKET_PUSH, 'websocket');$socket->connect('tcp://127.0.0.1:5555');$socket->send($payloadAsJSON);

public function onQueueAdded($payload) {

$payloadData = json_decode($payload, true); // dalsze przetwarzanie }

Page 51: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

MAO?➜ WAMP2➜ voryx/Thruway (kompatybilny z nowym

Autobahn.JS)

➜ bixuehujin/reactphp-mysql

➜ DNode

➜ STOMP

➜ AR.Drone

➜ Whois

➜ Childprocess

Page 52: Asynchroniczny PHP & komunikacja czasu rzeczywistego z wykorzystaniem websocketów

Koniec!Macie pytania?