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.

Implementing Comet using PHP

40,016 views

Published on

This talk was given at the Dutch PHP Conference 2011 and details the use of Comet (aka reverse ajax or ajax push) technologies and the importance of websockets and server-sent events. More information is available at http://joind.in/3237.

Published in: Technology

Implementing Comet using PHP

  1. 1. Implement Comet using PHP Dutch PHP Conference 2011 Jonas Mariën – King Foo www.king-foo.be
  2. 2. <ul>Jonas Mariën, King Foo www.king-foo.be Author “Zend Framework Web Services” (php|architect) Online since 93 (yep). PHP for fun since 96, professional since 2001. Master in Biological and Genetic Engineering, University of Leuven. </ul>
  3. 3. Comet = data push from server to client
  4. 4. Comet aka Reverse Ajax HTTP server Push Ajax Push HTTP streaming ... abused/umbrella term
  5. 5. Use case Example updates being pushed: <ul><li>ticker info
  6. 6. news items
  7. 7. chat messages
  8. 8. status updates
  9. 9. e-mail notifications
  10. 10. in-app messaging
  11. 11. monitoring feedback
  12. 12. feedback on processes that take time to complete
  13. 13. ... </li></ul>
  14. 14. Push? But, but ... a browser pulls and the server responds, no? <ul><li>Fake push using polling trickery
  15. 15. Connection is kept open as long as possible
  16. 16. A giant hack
  17. 17. Does the trick for now </li></ul>
  18. 18. The Basics
  19. 19. Short polling <ul><li>Client tries to get something new from the server and polls at regular and short intervals.
  20. 20. Responsiveness at the cost of performance (on the server side). Wasteful in terms of resources.
  21. 21. Easy, but not really what we are looking for.
  22. 22. Not considered 'comet'. </li></ul>
  23. 23. Short polling function getUpdate() { $.ajax({ type: &quot;POST&quot;, url: &quot;server.php&quot;, data: &quot;type=news&category=PHP&quot;, success : function(msg){ $('#feed').append(msg);/** we do something **/ setTimeout(&quot;getUpdate()&quot;, 2000); } }); } $(document).ready(function() { getUpdate(); }
  24. 24. Short polling demo
  25. 25. The forever frame <ul><li>An iframe keeps loading, never stops (or reloads occasionally)
  26. 26. Also called “streaming”
  27. 27. Dirty, e.g IE doesn't like this </li></ul>
  28. 28. The forever frame In the page: <script type=&quot;text/javascript&quot;>function updateResult(/** do something **/)</script>) <iframe style='width: 0px; height: 0px' src='./serverside.php'></iframe> On the server: <?php set_time_limit(0); header(&quot;Cache-Control: no-cache, must-revalidate&quot;); header(&quot;Expires: Mon, 24 Jul 1974 05:00:00 GMT&quot;); flush(); ob_flush();//if ob_start (implicitly) called before while(true) { $result = date('Y-m-d H:i:s'); echo '<script type=&quot;text/javascript&quot;>parent.updateResult(''.$result.'');</script>'; flush(); ob_flush(); $sleeptimer = rand(1, 15); sleep($sleeptimer); }
  29. 29. The forever frame demo
  30. 30. XHR <ul><li>“XHR streaming”
  31. 31. Output 'busy' && sleep() on the server side untill finishing
  32. 32. We need to get the status before oncomplete/onsuccess </li></ul>Note: there is also multipart/x-mixed-replace as part of XHR
  33. 33. XHR “ The XMLHttpRequest object can be in several states. The readyState attribute must return the current state, which must be one of the following values” * http://www.w3.org/TR/XMLHttpRequest/#states UNSENT (0) The object has been constructed. OPENED (1) The open() method has been successfully invoked. During this state request headers can be set using setRequestHeader() and the request can be made using the send() method. HEADERS_RECEIVED (2) All redirects (if any) have been followed and all HTTP headers of the final response have been received. Several response members of the object are now available. LOADING (3) The response entity body is being received. DONE (4) The data transfer has been completed or something went wrong during the transfer (e.g. infinite redirects).
  34. 34. XHR <ul><li>Sine jQuery 1.5, no direct access to the underlying XHR object
  35. 35. No access to loading state
  36. 36. Can use a filter: </li><ul><li>http://bugs.jquery.com/ticket/8327
  37. 37. http://jsfiddle.net/d8ckU/1/ </li></ul><li>Plugin: http://plugins.jquery.com/project/ajax-http-stream
  38. 38. Mixed results using different browsers for direct XHR manipulation </li></ul>
  39. 39. XHR xhr.onreadystatechange = function() { if(http.readyState == 3 && http.status == 200) { //do something document.write(xhr.responseText); } }
  40. 40. Long polling <ul><li>Longer intervals
  41. 41. Each time something is returned from the server, a new request is started
  42. 42. Using XHR
  43. 43. Server: checks for updates for a few cycles and keeps a counter. If counter limit reached, 'false' or 'error' message goes back. If an update is available before that, exit the loop and return an 'ok' message with payload. </li></ul>
  44. 44. Long Polling function getUpdate(){ $.ajax({ type: &quot;GET&quot;, url: &quot;server.php&quot;, async: true, cache: false, timeout:50000, success: function(data) { /** do something **/ setTimeout ( 'getUpdate()', 1000 ); }, error: function(XMLHttpRequest, textStatus, errorThrown){ /**show error notice**/ setTimeout ( 'getUpdate()', &quot;20000&quot;); }, }); }; $(document).ready(function(){ getUpdate(); });
  45. 45. Long Polling <?php ... $twitterSearch = new Zend_Service_Twitter_Search('json'); $hashtag = 'dpc11'; if (isset($_GET['hashtag'])) { $hashtag = $_GET['hashtag']; } $result = false; while($result === false) { if (isset($_GET['since_id']) && $_GET['since_id'] > 0) { $since_id = (int) $_GET['since_id'] + 1; $searchResults = $twitterSearch->search($hashtag, array('lang' => 'en', 'rpp' => 30,'since_id' => $since_id)); } else { $searchResults = $twitterSearch->search($hashtag, array('lang' => 'en','rpp' => 30)); } if (isset($searchResults['results']) && count($searchResults['results'])) { $result = json_encode($searchResults['results'][0]); } } echo $result;
  46. 46. Long Polling demo
  47. 47. Thoughts & notes <ul><li>Piggybacking
  48. 48. Long polling issues: </li><ul><li>Requests are kept open for a while
  49. 49. Objects (other then the ones being pulled/pushed) on server side can be changed, need to have these changes fed to the client. Piggybacking to the rescue again?
  50. 50. One or more long polling requests open and then an AJAX call: might give you browser problems with simultaneous requests
  51. 51. Server performance: we need event driven solutions (message queues and/or something like node.js) to fix this
  52. 52. Proxies (chunking stuff) and firewalls (dropping long connections). Some Comet libs (next slides) try to detect intermediate proxies </li></ul><li>Comet is not mature. Uses HTTP against most of the basic assumptions and no real standards available. Not a real standard. </li></ul>
  53. 53. Thoughts & Notes Performance is the biggest problem A Comet server should be able to handle as much connections as fast as possible, with a high troughput and low latency. Long polling requests stay idle for a while, so claiming a thread is not ok.
  54. 54. Frameworks/Toolkits
  55. 55. DWR <ul><li>Direct Web Remoting (http://directwebremoting.org)
  56. 56. Nice, but Java only
  57. 57. Fallback different polling mechanisms
  58. 58. Dynamic Javascript generation
  59. 59. Attempts at PHP implementations, one could use something like json-rpc to mimic this </li></ul>Could use a Java bridge from PHP perhaps?
  60. 60. Cometd & Bayeux <ul><li>Dojo Foundation
  61. 61. Cometd: reference implementation (in Java, Perl and Python). Java implementation uses Jetty.
  62. 62. Bayeux: protocol, JSON for request/response
  63. 63. Handshake + Publish/Subscribe
  64. 64. Channels (/segment/channel-name) </li><ul><li>/chat/publicroom
  65. 65. /chat/develepersonly
  66. 66. /chat/** </li></ul><li>Now has some WS support too (haven't tested that, see http://cometdproject.dojotoolkit.org/node/124 and https://www.dojotoolkit.org/reference-guide/_static/js/dojox/socket/)
  67. 67. http://cometd.org/documentation/installation http://cometd.org/documentation/howtos/primer </li></ul>Also, keep an eye on: http://opencoweb.org/
  68. 68. Bayeux Handshake [ { &quot;channel&quot;: &quot;/meta/handshake&quot;, &quot;version&quot;: &quot;1.0&quot;, &quot;minimumVersion&quot;: &quot;1.0beta&quot;, &quot;supportedConnectionTypes&quot;: [&quot;long-polling&quot;, &quot;callback-polling&quot;, &quot;iframe&quot;] } ] [ { &quot;channel&quot;: &quot;/meta/handshake&quot;, &quot;version&quot;: &quot;1.0&quot;, &quot;minimumVersion&quot;: &quot;1.0beta&quot;, &quot;supportedConnectionTypes&quot;: [&quot;long-polling&quot;,&quot;callback-polling&quot;], &quot;clientId&quot;: &quot;Un1q31d3nt1f13r&quot;, &quot;successful&quot;: true, &quot;authSuccessful&quot;: true, &quot;advice&quot;: { &quot;reconnect&quot;: &quot;retry&quot; } } ] http://cometdproject.dojotoolkit.org/documentation/bayeux
  69. 69. Bayeux <ul><li>Connect/disconnect
  70. 70. Subscribe </li></ul>[ { &quot;channel&quot;: &quot;/meta/subscribe&quot;, &quot;clientId&quot;: &quot;Un1q31d3nt1f13r&quot;, &quot;subscription&quot;: &quot;/chat/**&quot; } ] [ { &quot;channel&quot;: &quot;/meta/subscribe&quot;, &quot;clientId&quot;: &quot;Un1q31d3nt1f13r&quot;, &quot;subscription&quot;: &quot;/chat/**&quot;, &quot;successful&quot;: true, &quot;error&quot;: &quot;&quot; } ]
  71. 71. Bayeux dojo.require(&quot;dojox.cometd&quot;); dojox.cometd.init(&quot;http://example.com/cometd&quot;); dojox.cometd.subscribe(&quot;/slideshow/change&quot;, function(comet) { $('#slideimg').attr('src', comet.data.src); }); }); .... dojox.cometd.publish(&quot;/slideshow/change&quot;, { 'src': src }); http://simonwillison.net/2007/Dec/5/comet/
  72. 72. Cometd/Bayeux Demo Download distribution from: http://downloads.dojotoolkit.org/cometd/ $ tar zxvf cometd-1.1.4.tgz $ cd cometd-1.1.4/ $ cd cometd-1.1.4/ $ cd cometd-demo $ mvn jetty:deploy-war http://localhost:8080/
  73. 73. Cometd/Bayeux
  74. 74. Phomet/Bayeux client <ul><li>Use phomet to talk Bayeux to Cometd </li></ul>$phomet = new Bayeux(’http://example.com/cometd’); $phomet->publish(’/chat/developersonly’, array( 'user' => 'jonas', 'message' => ‘bayeux is ... well ... euhm’)); http://morglog.alleycatracing.com/bayeux.class.inc.phps
  75. 75. WebSync <ul><li>Commercial solution based on ASP.NET/IIS, also On-Demand
  76. 76. Comes with an OnDemand SDK for other languages, has some PHP sample code </li></ul>
  77. 77. APE <ul><li>APE - Ajax Push Engine
  78. 78. Comet but they claim WebSockets support
  79. 79. Fallback
  80. 80. Written in C, has SpiderMonkey (js) bindings allowing for serverside modules in js
  81. 81. GPL
  82. 82. http://www.ape-project.org/
  83. 83. http://www.ape-project.org/wiki/index.php/How_to_write_an_application_with_APE </li></ul>
  84. 84. APE
  85. 85. APE
  86. 86. APE
  87. 87. Kaazing <ul><li>Kaazing.com
  88. 88. Gateway, handles Comet and WebSockets
  89. 89. Some background: </li><ul><li>http://websocket.org/quantum.html
  90. 90. http://tech.kaazing.com/documentation/ </li></ul><li>Commercial, free dev license for testing </li></ul>
  91. 91. Kaazing
  92. 92. Kaazing demo
  93. 93. node.js
  94. 94. node.js
  95. 95. node.js git clone https://github.com/joyent/node.git cd node git tag -l git checkout v0.4.7 ./configure make make install
  96. 96. Socket.io
  97. 97. Socket.io
  98. 98. Socket.io curl http://npmjs.org/install.sh | sh npm install socket.io
  99. 99. Demo time
  100. 100. Want more? <ul><li>socket.io server side: https://github.com/LearnBoost/Socket.IO-node
  101. 101. socket.io client side: https://github.com/LearnBoost/Socket.IO
  102. 102. http://fzysqr.com/2011/02/28/nodechat-js-using-node-js-backbone-js-socket-io-and-redis-to-make-a-real-time-chat-app/ -> the demo
  103. 103. http://thecoffman.com/2011/02/21/getting-your-feet-wet-with-node.js-and-socket.io/ </li></ul>
  104. 104. NPM http://search.npmjs.org
  105. 105. Other solutions <ul><li>nginx push module (http://pushmodule.slact.net/)
  106. 106. Meteor (http://meteorserver.org)
  107. 107. Freeliberator (http://www.freeliberator.com)
  108. 108. Tornado (http://www.tornadoweb.org)
  109. 109. Hookbox (https://github.com/mcarter/hookbox)
  110. 110. Jetty 6 / Grizzly / Tomcat 6
  111. 111. Rails: Juggernaut plugin
  112. 112. Atmosphere (http://atmosphere.java.net/)
  113. 113. Orbited (https://bitbucket.org/desmaj/orbited)
  114. 114. Noloh (has some push support)
  115. 115. LightStreamer
  116. 116. Pushlets
  117. 117. ... </li></ul>
  118. 118. Alternatives <ul><li>Webhooks </li><ul><li>http://www.webhooks.org/
  119. 119. “HTTP callbacks using POST, for notification” </li></ul><li>PubSubHubbub </li><ul><li>http://code.google.com/p/pubsubhubbub/
  120. 120. “Server-to-server publish/subscribe using webhooks” </li></ul><li>ReverseHTTP </li><ul><li>http://reversehttp.net/
  121. 121. “Server in the browser” </li></ul><li>WebSockets & Server-Sent Events </li></ul>
  122. 122. Message formats <ul><li>JSON
  123. 123. STOMP </li><ul><li>http://en.wikipedia.org/wiki/Streaming_Text_Orientated_Messaging_Protocol
  124. 124. http://framework.zend.com/manual/en/zend.queue.stomp.html
  125. 125. http://pecl.php.net/package/stomp </li></ul><li>AMQP </li><ul><li>http://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol
  126. 126. http://pecl.php.net/package/amqp
  127. 127. http://riteshsblog.blogspot.com/2011/03/rabbitmq-adapter-for-zend-queue-using.html (uses AMQP and AMQP extension) </li></ul><li>XMPP </li></ul>
  128. 128. any PHP based solutions?
  129. 129. PHP? <ul><li>DIY - have a look at existing attempts </li><ul><li>Phico (http://www.3site.eu/phico.php)
  130. 130. Simplecomet (http://mandor.net/2008/12/23/12-simplecomet-http-streaming-and-toilet-scrubbing)
  131. 131. Comep (http://sourceforge.net/projects/comet/files/)
  132. 132. Other? </li></ul><li>No Bayeux/Cometd replacement in PHP, but doable if you really want to
  133. 133. Alternatives are available </li></ul>
  134. 134. PHP? <ul><li>Write a daemon </li><ul><li>Using nanoweb ?
  135. 135. Using Appserver-in-PHP ? (https://github.com/indeyets/appserver-in-php)
  136. 136. Use Phet (https://github.com/Tim-Smart/phet) </li></ul><li>Use existing comet server solutions and add/read data using PHP
  137. 137. Use something like node.js + socket.io and think in terms of upcoming websocket support in all browsers
  138. 138. Try to implement a node.js alternative using http://php.net/libevent (which could be a really interesting solution) </li></ul>
  139. 139. Real-time web Real push <ul><ul><li>WebSockets
  140. 140. Server-Sent Events (SSE) </li></ul></ul>
  141. 141. SSE Client: <!DOCTYPE html> <html> <head> <meta charset=&quot;utf-8&quot; /> </head> <body> <script> var source = new EventSource('./server.php'); source.onmessage = function (event) { document.getElementById('output').innerHTML += event.data + '<br>'; }; </script> <div id=&quot;output&quot;></div> </body> </html> Server: <?php header(&quot;Content-Type: text/event-streamnn&quot;); echo 'data: ' . date('Y-m-d H:i:s') . &quot;n&quot;; http://googlecodesamples.com/html5/sse/sse.html
  142. 142. SSE
  143. 143. WebSockets http://code.google.com/p/phpwebsocket/ Another option: www.waterspout.com
  144. 144. WebSockets
  145. 145. Conclusions Push! Go for a solution that bridges the gap while waiting for websockets and SSE Do keep an eye on the real-time web evolution. It will be here very (very) soon and will impact your job.
  146. 146. Credits <ul><li>“ Comet and Reverse Ajax: The next Generation Ajax 2.0” by Dave Crane and Phil McCarthy
  147. 147. www.html5rocks.com
  148. 148. www.cometdaily.com
  149. 149. Wikipedia of course
  150. 150. Plus: various links showed in the slides
  151. 151. Arrows: signsanddisplays.wordpress.com/page/2/
  152. 152. Comet image: http://www.igvita.com/2009/10/21/nginx-comet-low-latency-server-push/ </li></ul>
  153. 153. Questions? Want us to work for you? [email_address] www.king-foo.be www.king-foo.net/blog @jonasmarien

×