WebSockety – jak na real-time aplikace v PHP

WebSockety – jak na real-time aplikace v PHP

Aktualizováno: 17. 4. 2014

95a298d2bffa1b46fd023a286a8a7e86?s=128

Ondřej Mirtes

June 29, 2013
Tweet

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