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

Real-Time HTML5 and Ruby

Real-Time HTML5 and Ruby

Presented at RubyNation on March 24, 2012.

Luigi Ray-Montanez

March 24, 2012
Tweet

More Decks by Luigi Ray-Montanez

Other Decks in Programming

Transcript

  1. Overview Part 1 - Real-time HTML5 Technologies Part 2 -

    EventMachine Part 3 - Ruby frameworks
  2. In the Browser var connection = new WebSocket('ws://example.com/echo'); connection.onopen =

    function () { connection.send('Ping'); }; connection.onerror = function (error) { // handle error }; connection.onmessage = function (e) { console.log('Server said: ' + e.data); }; connection.onclose = function () { // ensure you expected a close };
  3. Just TCP A lower level than HTTP Developer de"nes protocol

    Support XMPP, IRC, AMQP, VNC Some proxy servers not compatible
  4. Server-Sent Events Forgotten little brother of WebSockets Downstream, server to

    browser push Just HTTP Browser handles reconnections Pure JS Poly"ll by Remy Sharp
  5. In the Browser var source = new EventSource('/stream'); source.addEventListener('message', function(e)

    { console.log(e.data); }); source.addEventListener('open', function(e) { ... }); source.addEventListener('error', function(e) { ... });
  6. Data data: first line\n data: second line\n\n --- data: {\n

    data: "msg": "hello world",\n data: "id": 12345\n data: }\n\n source.addEventListener('message', function(e) { var data = JSON.parse(e.data); console.log(data.id, data.msg); });
  7. Data IDs and Events id: 12345\n data: AAPL\n data: 572.44\n\n

    --- data: {"msg": "First message"}\n\n event: userlogon\n data: {"username": "John123"}\n\n event: update\n data: {"username": "John123", "emotion": "happy"}\n\n source.addEventListener('userlogon', function(e) { ... source.addEventListener('update', function(e) { ...
  8. Why EM? Many concurrent, long-held connections Addresses the C10K Problem

    Can’t use Rails or Rack to support WebSockets and Server-Sent Events
  9. Reactor Pattern Single-threaded event loop listens to many sources Dispatches

    synchronous work to handlers Handlers report when done
  10. Timebomb require 'eventmachine' EM.run do EM.add_timer(5) do puts "BOOM" EM.stop_event_loop

    end EM.add_periodic_timer(1) do puts "Tick" end end $ ruby timer.rb Tick Tick Tick Tick BOOM
  11. Tip of the Pyramid EventMachine.run { page = EventMachine::HttpRequest.new('http://example.com/').get page.errback

    { p "Google is down! terminate?" } page.callback { about = EventMachine::HttpRequest.new('http://example2.com').get about.callback { # callback nesting, ad infinitum } about.errback { # error-handling code } } }
  12. EM::Synchrony Ruby 1.9 Fibers Abstracts away callbacks and errbacks Code

    looks synchronous but is actually asynchronous
  13. EM + Fiber def http_get(url) f = Fiber.current http =

    EM::HttpRequest.new(url).get # resume fiber once http call is done http.callback { f.resume(http) } http.errback { f.resume(http) } return Fiber.yield end EM.run do Fiber.new{ page = http_get('http://www.google.com/') puts "Fetched page: #{page.response_header.status}" if page page = http_get('http://www.google.com/search?q=eventmachine') puts "Fetched page 2: #{page.response_header.status}" end }.resume end
  14. EM::Synchrony::Multi EventMachine.synchrony do multi = EventMachine::Synchrony::Multi.new multi.add :a, EventMachine::HttpRequest.new(uri1).aget multi.add

    :b, EventMachine::HttpRequest.new(uri2).apost multi.add :c, EventMachine::HttpRequest.new(uri3).aget res = multi.perform p "Look ma, no callbacks, and parallel HTTP requests!" p res EventMachine.stop end
  15. Server-Sent Events class TimeAction < Cramp::Action self.transport = :sse on_start

    :send_latest_time periodic_timer :send_latest_time, :every => 2 def send_latest_time data = {'time' => Time.now.to_i}.to_json render data end end
  16. WebSocket Endpoint class WebsocketEndPoint < Goliath::WebSocket def on_open(env) env.logger.info("WS OPEN")

    env['subscription'] = env.channel.subscribe { |m| env.stream_send(m) } end def on_message(env, msg) env.logger.info("WS MESSAGE: #{msg}") env.channel << msg end def on_close(env) env.logger.info("WS CLOSED") env.channel.unsubscribe(env['subscription']) end def on_error(env, error) env.logger.error error end end