Upgrade to Pro — share decks privately, control downloads, hide ads and more …

WebSockety – jak na real-time aplikace v PHP

WebSockety – jak na real-time aplikace v PHP

Aktualizováno: 17. 4. 2014

Ondřej Mirtes

June 29, 2013
Tweet

More Decks by Ondřej Mirtes

Other Decks in Technology

Transcript

  1. WebSockety Ondřej Mirtes

  2. Využitelné všude tam, kde teď máte periodické AJAX požadavky$ chat,

    notifikace, hry, realtime updates…
  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. caniuse.com

  5. Detekce podpory return  'WebSocket'  in  window; Nekontrolujte typ a verzi

    prohlížeče, ale vždy výskyt konkrétní funkcionality.
  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. 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. Zakažte uživateli provádět akce při uzavřeném spojení Hrozí ztráta dat.

  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. None
  11. $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("Hello  World\n");   });   $ $socket-­‐>listen(1337);   $loop-­‐>run(); http://u.k47.cz/2Bw
  12. 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ě.
  13. while  (true)  {     ...   } Event loop

    http://u.k47.cz/2Bw Response 1 Response 2 Response 3 sleep(15); Request 2 Request 3 Request 1
  14. http://socketo.me/

  15. composer.json {          "require":  {    

                 "cboden/Ratchet":  "~0.3"          },          "suggest":  {                  "ext-­‐libevent":  ""          }   } S libevent rozšířením se použije efektivnější implementace event loop.
  16. use  Ratchet\Http\HttpServer;   use  Ratchet\Server\IoServer;   use  Ratchet\WebSocket\WsServer;   use

     React\EventLoop\Factory;   use  React\Socket\Server;   $ $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.
  17. use  Ratchet\ConnectionInterface  as  Client;   $ class  App  implements  \Ratchet\MessageComponentInterface

      {   $   public  function  onOpen(Client  $client)  {   $   }   $   public  function  onMessage(Client  $client,  $message)  {   $   }   $   public  function  onClose(Client  $client)  {         }   $   public  function  onError(Client  $client,  \Exception  $e)  {   $   }   $ }
  18. public  function  onMessage(Client  $client,  $message)   {      

       foreach  ($this-­‐>clients  as  $c)  {                  if  ($c  !==  $client)  {                          $c-­‐>send($message);                  }          }   } Rozeslání zprávy
 na ostatní klienty
  19. 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.
  20. Č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.
  21. Flash polyfill https://github.com/gimite/web-socket-js

  22. use  Ratchet\Server\FlashPolicy;   use  Ratchet\Server\IoServer;   use  React\Socket\Server;   $

    $server  =  new  Server($loop);   $server-­‐>listen(843,  '0.0.0.0');   $policy  =  new  FlashPolicy();   $policy-­‐>addAllowedAccess('*',  8080);   $ new  IoServer($policy,  $server);   $loop-­‐>run(); FlashPolicy
  23. 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
  24. Ratchet Long polling server WS WS HTTP https://gist.github.com/10895929 Long polling

    server = HTTP server
 v PHP + WebSocket klient v PHP :)
  25. 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
  26. 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.
  27. http://zeromq.org/

  28. 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.
  29. ZeroMQ - receiver use  React\ZMQ\Context;   $ $context  =  new

     Context($loop);   $socket  =  $context-­‐>getSocket(ZMQ::SOCKET_PULL);   $socket-­‐>bind('tcp://127.0.0.1:5555');   $socket-­‐>on('message',  [$app,  'onZmqMessage']);
  30. ZeroMQ - sender $context  =  new  ZMQContext();   $socket  =

     $context-­‐>getSocket(     ZMQ::SOCKET_PUSH,     'id'   );   $socket-­‐>connect('tcp://127.0.0.1:5555');   $socket-­‐>send('ahoj!');
  31. @OndrejMirtes