Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

WebSockets - how to do real-time applications in PHP

1,341 views

Published on

At Brno PHP April 2014 meetup by Ondřej Mirtes

Published in: Software
  • Be the first to comment

  • Be the first to like this

WebSockets - how to do real-time applications in PHP

  1. 1. WebSockety Ondřej Mirtes
  2. 2. Využitelné všude tam, kde teď máte periodické AJAX požadavky$ chat, notifikace, hry, realtime updates…
  3. 3. Počáteční HTTP handshake, poté stálé spojení přes TCP.$ V komunikaci se posílají jen změny (v AJAXu obvykle celý stav aplikace).
  4. 4. caniuse.com
  5. 5. Detekce podpory return  'WebSocket'  in  window; Nekontrolujte typ a verzi prohlížeče, ale vždy výskyt konkrétní funkcionality.
  6. 6. var  ws  =  new  WebSocket('ws://localhost:8080');   $ ws.onopen  =  function()  {   $ };   $ ws.onmessage  =  function(event)  {      console.log(event.data);   };   $ ws.onclose  =  function()  {   $ }; Pozor - připojuje už konstruktor!
  7. 7. var  ws  =  new  WebSocketWrapper(     'ws://localhost:8080'   );   ws.connect();   $ ws.on('postLiked',  function(data)  {       });   $ ws.on('chatMessage',  function(data)  {   $ });   $ ws.send({'action':'ping'}); https://gist.github.com/10647722 Můj vlastní wrapper, který řeší reconnecting
 a rozstřelování konkrétních akcí do jednotlivých callbacků (oproti jedinému "onmessage").
  8. 8. Zakažte uživateli provádět akce při uzavřeném spojení Hrozí ztráta dat.
  9. 9. Proč WebSockety v PHP a ne v něčem vhodnějším, třeba node.js?$ $ Pokud máte už běžící aplikaci, tak napsáním WS funkcionality v PHP budete těžit z jednotné codebase, využití znalostí týmu a stejných procesů na testování, continuous integration, build a deployment.$ $ Pokud stavíte na zelené louce a chcete se naučit něco nového, tak vás do PHP nutit nebudu :)
  10. 10. $loop  =  ReactEventLoopFactory::create();   $socket  =  new  ReactSocketServer($loop);   $http  =  new  ReactHttpServer($socket,  $loop);   $ $http-­‐>on('request',  function($request,  $response)  {     $response-­‐>writeHead(       200,       ['Content-­‐Type'  =>  'text/plain']     );     $response-­‐>end("Hello  Worldn");   });   $ $socket-­‐>listen(1337);   $loop-­‐>run(); http://u.k47.cz/2Bw
  11. 11. while  (true)  {     ...   } Event loop http://u.k47.cz/2Bw Request 1 Response 1 Request 2 Response 2 Request 3 Response 3 Request 4 Response 4 React je asynchronní, ale ne paralelní – běží stále v jednom vlákně.
  12. 12. while  (true)  {     ...   } Event loop http://u.k47.cz/2Bw Response 1 Response 2 Response 3 sleep(15); Request 2 Request 3Request 1
  13. 13. http://socketo.me/
  14. 14. composer.json {          "require":  {                  "cboden/Ratchet":  "~0.3"          },          "suggest":  {                  "ext-­‐libevent":  ""          }   } S libevent rozšířením se použije efektivnější implementace event loop.
  15. 15. use  RatchetHttpHttpServer;   use  RatchetServerIoServer;   use  RatchetWebSocketWsServer;   use  ReactEventLoopFactory;   use  ReactSocketServer;   $ $loop  =  Factory::create();   $server  =  new  Server($loop);   $server-­‐>listen(8080,  '0.0.0.0');   $ new  IoServer(     new  HttpServer(new  WsServer($app)),     $server   );   $ $loop-­‐>run(); Spuštěný proces spravujte např. pomocí supervisord.org, aby stále běžel.
  16. 16. use  RatchetConnectionInterface  as  Client;   $ class  App  implements  RatchetMessageComponentInterface   {   $   public  function  onOpen(Client  $client)  {   $   }   $   public  function  onMessage(Client  $client,  $message)  {   $   }   $   public  function  onClose(Client  $client)  {         }   $   public  function  onError(Client  $client,  Exception  $e)  {   $   }   $ }
  17. 17. public  function  onMessage(Client  $client,  $message)   {          foreach  ($this-­‐>clients  as  $c)  {                  if  ($c  !==  $client)  {                          $c-­‐>send($message);                  }          }   } Rozeslání zprávy
 na ostatní klienty
  18. 18. Při navázání spojení pošlete klientovi počáteční stav Např. posledních 10 zpráv v chatu. Pokud byste počáteční stav nepotřebovali, tak vlastně nepotřebujete ani žádné úložiště na data.
  19. 19. Časovače $loop-­‐>addTimer(5,  function()  {          //  za  pět  sekund   });   $ $loop-­‐>addPeriodicTimer(5,  function()  {          //  každých  pět  sekund   }); Na odpojení uživatele při neaktivitě, na zasílání pingu pro udržení připojení. Časovače jsou jen v paměti, po pádu a obnovení procesu
 je musíte zrekonstruovat.
  20. 20. Flash polyfill https://github.com/gimite/web-socket-js
  21. 21. use  RatchetServerFlashPolicy;   use  RatchetServerIoServer;   use  ReactSocketServer;   $ $server  =  new  Server($loop);   $server-­‐>listen(843,  '0.0.0.0');   $policy  =  new  FlashPolicy();   $policy-­‐>addAllowedAccess('*',  8080);   $ new  IoServer($policy,  $server);   $loop-­‐>run(); FlashPolicy
  22. 22. Long polling AJAX fallback, který funguje všude$ POST požadavky pro odchozí zprávy$ Stream příchozích zpráv přes dlouhodobý GET požadavek$ xhr.responseText & xhr.onreadystatechange
  23. 23. Ratchet Long polling server WS WS HTTP https://gist.github.com/10895929 Long polling server = HTTP server
 v PHP + WebSocket klient v PHP :)
  24. 24. Ratchet neumí SSL (wss://)$ Webserver může fungovat jako proxy,
 která zabezpečenou komunikaci zajistí$ Webserver může WebSockety poskytnout
 na klasických portech (80 a 443) –
 např. na subdoméně$ Apache – mod_proxy_wstunnel
  25. 25. Ratchet ws.foo.com wslp.foo.com www.foo.com Long polling Nginx jako proxy může všechny služby poskytnout na klasických portech – 80, 443.
  26. 26. http://zeromq.org/
  27. 27. Webserver CLI Ratchet WebSocket klienti Ratchet běží v odděleném procesu, pokud potřebujete reagovat na akci z webové aplikace nebo třeba cronu, pošlete ji do Ratchetu pomocí ZMQ.
  28. 28. ZeroMQ - receiver use  ReactZMQContext;   $ $context  =  new  Context($loop);   $socket  =  $context-­‐>getSocket(ZMQ::SOCKET_PULL);   $socket-­‐>bind('tcp://127.0.0.1:5555');   $socket-­‐>on('message',  [$app,  'onZmqMessage']);
  29. 29. ZeroMQ - sender $context  =  new  ZMQContext();   $socket  =  $context-­‐>getSocket(     ZMQ::SOCKET_PUSH,     'id'   );   $socket-­‐>connect('tcp://127.0.0.1:5555');   $socket-­‐>send('ahoj!');
  30. 30. @OndrejMirtes

×