SlideShare ist ein Scribd-Unternehmen logo
1 von 36
Downloaden Sie, um offline zu lesen
Live Streaming
        &
Server Sent Events
    Tomáš Kramár
      @tkramar
When?
●   Server needs to stream data to client
    –   Server decides when and what to send
    –   Client waits and listens
    –   Client does not need to send messages
    –   Uni-directional communication
    –   Asynchronously
How? / Terminology
●   AJAX polling
●   Comet
●   WebSockets
●   Server-Sent Events
AJAX polling
                   Any news?




Browser/Client                  Server
AJAX polling
                   Any news?

                     No




Browser/Client                  Server
AJAX polling
                   Any news?

                     No

                   Any news?

                     No



Browser/Client                  Server
AJAX polling
                   Any news?

                     No

                   Any news?

                     No

                   Any news?

Browser/Client       Yes!       Server
AJAX polling
                   Any news?

                     No

                   Any news?

                     No

                   Any news?

Browser/Client       Yes!       Server

                   Any news?

                     No
AJAX polling
●   Overhead
    –   Establishing new connections, TCP handshakes
    –   Sending HTTP headers
    –   Multiply by number of clients
●   Not really realtime
    –   Poll each 2 seconds
Comet
●   set of technology principles/communication
    patterns
●   mostly hacks
    –   forever-iframe
    –   htmlfile ActiveX object
    –   XHR multipart/streaming/long-polling
    –   Flash
    –   ..
WebSockets
●   bi-directional, full-duplex communication
    channels over a single TCP connection
●   HTML5
●   being standardized
Server-Sent Events
●   HTML5
●   Traditional HTTP
    –   No special protocol or server implementation
●   Browser establishes single connection and
    waits
●   Server generates events
SSE
                 Request w parameters




                 id: 1
                 event: display
                 data: { foo: 'moo' }

Browser/Client                           Server
SSE
                 Request w parameters




                 id: 1
                 event: display
                 data: { foo: 'moo' }

Browser/Client   id: 2                   Server
                 event: redraw
                 data: { boo: 'hoo' }
Case study
●   Live search in trademark databases
●   query
    –   search in register #1
        ●   Search (~15s), parse search result list, fetch each result
            (~3s each), go to next page in search result list (~10s),
            fetch each result, ...
    –   search in register #2
        ●   ...
    –   …
●   Don't let the user wait, display results when
    they are available
Demo
Client

this.source = new EventSource('marks/search');

self.source.addEventListener('results', function(e) {
  self.marks.appendMarks($.parseJSON(e.data));
});

self.source.addEventListener('failure', function(e) {
  self.errors.showError();
});

self.source.addEventListener('status', function(e) {
  self.paging.update($.parseJSON(e.data));
});
Client gotchas
 ●    Special events:
      –   open
      –   error
 ●    Don't forget to close the request

self.source.addEventListener('finished', function(e) {
 self.status.searchFinished();
 self.source.close();
});
Server
●   Must support
    –   long-running request
    –   Live-streaming (i.e., no output buffering)
●   Rainbows!, Puma or Thin
●   Rails 4 (beta) supports live streaming
Rails 4 Live Streaming
class MarksController < ApplicationController
  include ActionController::Live
  def results
    response.headers['Content-Type'] = 'text/event-stream'
    sse = SSE.new(response.stream)
    Tort.search(params[:query]) do |on|
      on.results do |hits|
        sse.write(hits, event: 'result')
      end
      on.status_change do |status|
        sse.write(status, event: 'status')
      end
      on.error do
        sse.write({}, event: 'failure')
      end
    end
  end
end
require 'json'


class SSE
 def initialize io
                                               event: displayn
  @io = io                                     data: { foo: 'moo' }nn
 end


 def write object, options = {}
  options.each do |k,v|
    @io.write "#{k}: #{v}n"
  end
  @io.write "data: #{JSON.dump(object)}nn"
 end


 def close
  @io.close
 end
end
Timeouts, lost connections, internet
  explorers and other bad things
●   EventSource request can be interrupted
●   EventSource will reconnect automatically
●   What happens with the data during the time
    connection was not available?
Handling reconnections
●   When EventSource reconnects, we need to
    continue sending the data from the point the
    connection was lost
    –   Do the work in the background and store events
        somewhere
    –   In the controller, load events from the storage
●   EventSource sends Last-Event-Id in HTTP
    header
    –   But we don't need it if we remove the processed
        events
marks/search?q=eset                      GirlFriday
                                                      Search
                                                     3342345
           HTTP 202 Accepted
           marks/results?job_id=3342345




Browser                                   Server       Redis



          marks/results?job_id=3342345


                                                   MarksController

             event: results
             data: {foo: 'boo'}

             event: status
             data: {moo: 'hoo'}
class MarksController < ApplicationController
 include ActionController::Live

 def search!
  uuid = UUID.new.generate(:compact)
  TORT_QUEUE << { phrase: params[:q], job_id: uuid }

  render status: 202, text: marks_results_path(job: uuid)
 end

 def results
  response.headers['Content-Type'] = 'text/event-stream'
  sse = SSE.new(response.stream)
  queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis)
  finished = false

  begin
   begin
     queue.next_message do |json_message|
      message = JSON.parse(json_message)
      case message["type"]
      when "results" then
       sse.write(message["data"], event: 'results')
      when "failure" then
       sse.write({}, event: 'failure')
      when "fatal" then
       sse.write({}, event: 'fatal')
       finished = true
      when "status" then
       sse.write(message["data"], event: 'status')
      when "finished" then
       sse.write({}, event: 'finished')
       finished = true
      end
     end
   end while !finished
  rescue IOError
    # when clients disconnects
  ensure
   sse.close
  end
 end
end
class MarksController < ApplicationController
 include ActionController::Live

 def search!
  uuid = UUID.new.generate(:compact)
  TORT_QUEUE << { phrase: params[:q], job_id: uuid }
                                                                       generate job_id

  render status: 202, text: marks_results_path(job: uuid)
 end

 def results
  response.headers['Content-Type'] = 'text/event-stream'
  sse = SSE.new(response.stream)
  queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis)
  finished = false

  begin
   begin
     queue.next_message do |json_message|
      message = JSON.parse(json_message)
      case message["type"]
      when "results" then
       sse.write(message["data"], event: 'results')
      when "failure" then
       sse.write({}, event: 'failure')
      when "fatal" then
       sse.write({}, event: 'fatal')
       finished = true
      when "status" then
       sse.write(message["data"], event: 'status')
      when "finished" then
       sse.write({}, event: 'finished')
       finished = true
      end
     end
   end while !finished
  rescue IOError
    # when clients disconnects
  ensure
   sse.close
  end
 end
end
class MarksController < ApplicationController
 include ActionController::Live

 def search!
  uuid = UUID.new.generate(:compact)
  TORT_QUEUE << { phrase: params[:q], job_id: uuid }
                                                                       start async job (GirlFriday)
  render status: 202, text: marks_results_path(job: uuid)
 end

 def results
  response.headers['Content-Type'] = 'text/event-stream'
  sse = SSE.new(response.stream)
  queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis)
  finished = false

  begin
   begin
     queue.next_message do |json_message|
      message = JSON.parse(json_message)
      case message["type"]
      when "results" then
       sse.write(message["data"], event: 'results')
      when "failure" then
       sse.write({}, event: 'failure')
      when "fatal" then
       sse.write({}, event: 'fatal')
       finished = true
      when "status" then
       sse.write(message["data"], event: 'status')
      when "finished" then
       sse.write({}, event: 'finished')
       finished = true
      end
     end
   end while !finished
  rescue IOError
    # when clients disconnects
  ensure
   sse.close
  end
 end
end
class MarksController < ApplicationController
 include ActionController::Live

 def search!
  uuid = UUID.new.generate(:compact)
  TORT_QUEUE << { phrase: params[:q], job_id: uuid }

  render status: 202, text: marks_results_path(job: uuid)
 end
                                                                       send results URL

 def results
  response.headers['Content-Type'] = 'text/event-stream'
  sse = SSE.new(response.stream)
  queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis)
  finished = false

  begin
   begin
     queue.next_message do |json_message|
      message = JSON.parse(json_message)
      case message["type"]
      when "results" then
       sse.write(message["data"], event: 'results')
      when "failure" then
       sse.write({}, event: 'failure')
      when "fatal" then
       sse.write({}, event: 'fatal')
       finished = true
      when "status" then
       sse.write(message["data"], event: 'status')
      when "finished" then
       sse.write({}, event: 'finished')
       finished = true
      end
     end
   end while !finished
  rescue IOError
    # when clients disconnects
  ensure
   sse.close
  end
 end
end
class MarksController < ApplicationController
 include ActionController::Live

 def search!
  uuid = UUID.new.generate(:compact)
  TORT_QUEUE << { phrase: params[:q], job_id: uuid }

  render status: 202, text: marks_results_path(job: uuid)
 end

 def results
  response.headers['Content-Type'] = 'text/event-stream'
  sse = SSE.new(response.stream)
  queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis)   Get queue for this job,
  finished = false                                                      async job is pushing
  begin
                                                                            to this queue
   begin
     queue.next_message do |json_message|
      message = JSON.parse(json_message)
      case message["type"]
      when "results" then
       sse.write(message["data"], event: 'results')
      when "failure" then
       sse.write({}, event: 'failure')
      when "fatal" then
       sse.write({}, event: 'fatal')
       finished = true
      when "status" then
       sse.write(message["data"], event: 'status')
      when "finished" then
       sse.write({}, event: 'finished')
       finished = true
      end
     end
   end while !finished
  rescue IOError
    # when clients disconnects
  ensure
   sse.close
  end
 end
end
class MarksController < ApplicationController
 include ActionController::Live

 def search!
  uuid = UUID.new.generate(:compact)
  TORT_QUEUE << { phrase: params[:q], job_id: uuid }

  render status: 202, text: marks_results_path(job: uuid)
 end

 def results
  response.headers['Content-Type'] = 'text/event-stream'
  sse = SSE.new(response.stream)
  queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis)
  finished = false

  begin
   begin
     queue.next_message do |json_message|
                                                                         Fetch next message
      message = JSON.parse(json_message)                               from queue (blocks until
      case message["type"]
      when "results" then                                                  one is available)
       sse.write(message["data"], event: 'results')
      when "failure" then
       sse.write({}, event: 'failure')
      when "fatal" then
       sse.write({}, event: 'fatal')
       finished = true
      when "status" then
       sse.write(message["data"], event: 'status')
      when "finished" then
       sse.write({}, event: 'finished')
       finished = true
      end
     end
   end while !finished
  rescue IOError
    # when clients disconnects
  ensure
   sse.close
  end
 end
end
class MarksController < ApplicationController
 include ActionController::Live

 def search!
  uuid = UUID.new.generate(:compact)
  TORT_QUEUE << { phrase: params[:q], job_id: uuid }

  render status: 202, text: marks_results_path(job: uuid)
 end

 def results
  response.headers['Content-Type'] = 'text/event-stream'
  sse = SSE.new(response.stream)
  queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis)
  finished = false

  begin
   begin
     queue.next_message do |json_message|
      message = JSON.parse(json_message)
      case message["type"]
      when "results" then
       sse.write(message["data"], event: 'results')
      when "failure" then
       sse.write({}, event: 'failure')
      when "fatal" then
       sse.write({}, event: 'fatal')
       finished = true
      when "status" then
       sse.write(message["data"], event: 'status')
      when "finished" then
       sse.write({}, event: 'finished')
       finished = true
      end
     end
   end while !finished
  rescue IOError
    # when clients disconnects                        IOError is raised when client
  ensure
   sse.close                                            disconnected and we are
  end
 end                                                   writing to response.stream
end
GirlFriday worker
class SearchWorker
 def self.perform(phrase, job_id)
   channel = Channel.for_job(job_id)
   queue = SafeQueue.new(channel, Tmzone.redis)

  Tort.search(phrase) do |on|
   on.results do |hits|
    queue.push({ type: "results", data: hits }.to_json)
   end
   on.status_change do |status|
    queue.push({ type: "status", data: status }.to_json)
   end
   on.error do
    queue.push({ type: 'failure' }.to_json)
   end
  end
  queue.push({ type: "finished" }.to_json)
 end
end
SafeQueue
class SafeQueue
 def initialize(channel, redis)
   @channel = channel
   @redis = redis
 end

 def next_message(&block)
  begin
   _, message = @redis.blpop(@channel)
   block.call(message)
  rescue => error
   @redis.lpush(@channel, message)
   raise error
  end
 end

 def push(message)
  @redis.rpush(@channel, message)
 end
end
EventSource Compatibility
●   Firefox 6+, Chrome 6+, Safari 5+, Opera 11+,
    iOS Safari 4+, Blackberry, Opera Mobile,
    Chrome for Android, Firefox for Android
Fallback
●   Polyfills
    –   https://github.com/remy/polyfills/blob/master/Event
        Source.js
        ●   Hanging GET, waits until the request terminates,
            essentially buffering the live output
    –   https://github.com/Yaffle/EventSource
        ●   send a keep-alive message each 15 seconds
Summary
●   Unidirectional server-to-client communication
●   Single request
●   Real-time
●   Easy to implement
●   Well supported except for IE

Weitere ähnliche Inhalte

Was ist angesagt?

Building Event Driven (Micro)services with Apache Kafka
Building Event Driven (Micro)services with Apache KafkaBuilding Event Driven (Micro)services with Apache Kafka
Building Event Driven (Micro)services with Apache KafkaGuido Schmutz
 
Building an Enterprise Eventing Framework
Building an Enterprise Eventing FrameworkBuilding an Enterprise Eventing Framework
Building an Enterprise Eventing Frameworkconfluent
 
Orchestration Patterns for Microservices with Messaging by RabbitMQ
Orchestration Patterns for Microservices with Messaging by RabbitMQOrchestration Patterns for Microservices with Messaging by RabbitMQ
Orchestration Patterns for Microservices with Messaging by RabbitMQVMware Tanzu
 
Reactive Card Magic: Understanding Spring WebFlux and Project Reactor
Reactive Card Magic: Understanding Spring WebFlux and Project ReactorReactive Card Magic: Understanding Spring WebFlux and Project Reactor
Reactive Card Magic: Understanding Spring WebFlux and Project ReactorVMware Tanzu
 
Networking in Java with NIO and Netty
Networking in Java with NIO and NettyNetworking in Java with NIO and Netty
Networking in Java with NIO and NettyConstantine Slisenka
 
Whitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsWhitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsYura Nosenko
 
Benefits of Stream Processing and Apache Kafka Use Cases
Benefits of Stream Processing and Apache Kafka Use CasesBenefits of Stream Processing and Apache Kafka Use Cases
Benefits of Stream Processing and Apache Kafka Use Casesconfluent
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Domenic Denicola
 
Event Sourcing & CQRS, Kafka, Rabbit MQ
Event Sourcing & CQRS, Kafka, Rabbit MQEvent Sourcing & CQRS, Kafka, Rabbit MQ
Event Sourcing & CQRS, Kafka, Rabbit MQAraf Karsh Hamid
 
Reducing Microservice Complexity with Kafka and Reactive Streams
Reducing Microservice Complexity with Kafka and Reactive StreamsReducing Microservice Complexity with Kafka and Reactive Streams
Reducing Microservice Complexity with Kafka and Reactive Streamsjimriecken
 
Microservices Integration Patterns with Kafka
Microservices Integration Patterns with KafkaMicroservices Integration Patterns with Kafka
Microservices Integration Patterns with KafkaKasun Indrasiri
 
Kafka Tutorial - Introduction to Apache Kafka (Part 1)
Kafka Tutorial - Introduction to Apache Kafka (Part 1)Kafka Tutorial - Introduction to Apache Kafka (Part 1)
Kafka Tutorial - Introduction to Apache Kafka (Part 1)Jean-Paul Azar
 
Polyglot payloads in practice by avlidienbrunn at HackPra
Polyglot payloads in practice by avlidienbrunn at HackPraPolyglot payloads in practice by avlidienbrunn at HackPra
Polyglot payloads in practice by avlidienbrunn at HackPraMathias Karlsson
 
Scaling Apache Pulsar to 10 Petabytes/Day - Pulsar Summit NA 2021 Keynote
Scaling Apache Pulsar to 10 Petabytes/Day - Pulsar Summit NA 2021 KeynoteScaling Apache Pulsar to 10 Petabytes/Day - Pulsar Summit NA 2021 Keynote
Scaling Apache Pulsar to 10 Petabytes/Day - Pulsar Summit NA 2021 KeynoteStreamNative
 
Real-time Data Streaming from Oracle to Apache Kafka
Real-time Data Streaming from Oracle to Apache Kafka Real-time Data Streaming from Oracle to Apache Kafka
Real-time Data Streaming from Oracle to Apache Kafka confluent
 
Messaging Standards and Systems - AMQP & RabbitMQ
Messaging Standards and Systems - AMQP & RabbitMQMessaging Standards and Systems - AMQP & RabbitMQ
Messaging Standards and Systems - AMQP & RabbitMQAll Things Open
 
[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기
[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기
[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기YongSung Yoon
 

Was ist angesagt? (20)

Building Event Driven (Micro)services with Apache Kafka
Building Event Driven (Micro)services with Apache KafkaBuilding Event Driven (Micro)services with Apache Kafka
Building Event Driven (Micro)services with Apache Kafka
 
Building an Enterprise Eventing Framework
Building an Enterprise Eventing FrameworkBuilding an Enterprise Eventing Framework
Building an Enterprise Eventing Framework
 
Orchestration Patterns for Microservices with Messaging by RabbitMQ
Orchestration Patterns for Microservices with Messaging by RabbitMQOrchestration Patterns for Microservices with Messaging by RabbitMQ
Orchestration Patterns for Microservices with Messaging by RabbitMQ
 
Reactive Card Magic: Understanding Spring WebFlux and Project Reactor
Reactive Card Magic: Understanding Spring WebFlux and Project ReactorReactive Card Magic: Understanding Spring WebFlux and Project Reactor
Reactive Card Magic: Understanding Spring WebFlux and Project Reactor
 
Networking in Java with NIO and Netty
Networking in Java with NIO and NettyNetworking in Java with NIO and Netty
Networking in Java with NIO and Netty
 
Whitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsWhitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applications
 
Benefits of Stream Processing and Apache Kafka Use Cases
Benefits of Stream Processing and Apache Kafka Use CasesBenefits of Stream Processing and Apache Kafka Use Cases
Benefits of Stream Processing and Apache Kafka Use Cases
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
 
Event Sourcing & CQRS, Kafka, Rabbit MQ
Event Sourcing & CQRS, Kafka, Rabbit MQEvent Sourcing & CQRS, Kafka, Rabbit MQ
Event Sourcing & CQRS, Kafka, Rabbit MQ
 
Reducing Microservice Complexity with Kafka and Reactive Streams
Reducing Microservice Complexity with Kafka and Reactive StreamsReducing Microservice Complexity with Kafka and Reactive Streams
Reducing Microservice Complexity with Kafka and Reactive Streams
 
Microservices Integration Patterns with Kafka
Microservices Integration Patterns with KafkaMicroservices Integration Patterns with Kafka
Microservices Integration Patterns with Kafka
 
Api security-testing
Api security-testingApi security-testing
Api security-testing
 
Kafka Tutorial - Introduction to Apache Kafka (Part 1)
Kafka Tutorial - Introduction to Apache Kafka (Part 1)Kafka Tutorial - Introduction to Apache Kafka (Part 1)
Kafka Tutorial - Introduction to Apache Kafka (Part 1)
 
Polyglot payloads in practice by avlidienbrunn at HackPra
Polyglot payloads in practice by avlidienbrunn at HackPraPolyglot payloads in practice by avlidienbrunn at HackPra
Polyglot payloads in practice by avlidienbrunn at HackPra
 
Scaling Apache Pulsar to 10 Petabytes/Day - Pulsar Summit NA 2021 Keynote
Scaling Apache Pulsar to 10 Petabytes/Day - Pulsar Summit NA 2021 KeynoteScaling Apache Pulsar to 10 Petabytes/Day - Pulsar Summit NA 2021 Keynote
Scaling Apache Pulsar to 10 Petabytes/Day - Pulsar Summit NA 2021 Keynote
 
RabbitMQ
RabbitMQRabbitMQ
RabbitMQ
 
Real-time Data Streaming from Oracle to Apache Kafka
Real-time Data Streaming from Oracle to Apache Kafka Real-time Data Streaming from Oracle to Apache Kafka
Real-time Data Streaming from Oracle to Apache Kafka
 
Messaging Standards and Systems - AMQP & RabbitMQ
Messaging Standards and Systems - AMQP & RabbitMQMessaging Standards and Systems - AMQP & RabbitMQ
Messaging Standards and Systems - AMQP & RabbitMQ
 
RabbitMQ
RabbitMQ RabbitMQ
RabbitMQ
 
[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기
[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기
[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기
 

Ähnlich wie Live Streaming & Server Sent Events

Spring Web Services: SOAP vs. REST
Spring Web Services: SOAP vs. RESTSpring Web Services: SOAP vs. REST
Spring Web Services: SOAP vs. RESTSam Brannen
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applicationsTom Croucher
 
Future Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETFuture Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETGianluca Carucci
 
A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...Tom Croucher
 
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
Node.js: Continuation-Local-Storage and the Magic of AsyncListenerNode.js: Continuation-Local-Storage and the Magic of AsyncListener
Node.js: Continuation-Local-Storage and the Magic of AsyncListenerIslam Sharabash
 
Intoduction to Play Framework
Intoduction to Play FrameworkIntoduction to Play Framework
Intoduction to Play FrameworkKnoldus Inc.
 
Comet from JavaOne 2008
Comet from JavaOne 2008Comet from JavaOne 2008
Comet from JavaOne 2008Joe Walker
 
Introduction of server sent events (sse)
Introduction of server sent events (sse)Introduction of server sent events (sse)
Introduction of server sent events (sse)Yuji KONDO
 
Speed up your Web applications with HTML5 WebSockets
Speed up your Web applications with HTML5 WebSocketsSpeed up your Web applications with HTML5 WebSockets
Speed up your Web applications with HTML5 WebSocketsYakov Fain
 
Intro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiIntro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiRan Mizrahi
 
Sherlock Homepage - A detective story about running large web services - NDC ...
Sherlock Homepage - A detective story about running large web services - NDC ...Sherlock Homepage - A detective story about running large web services - NDC ...
Sherlock Homepage - A detective story about running large web services - NDC ...Maarten Balliauw
 
Reactive & Realtime Web Applications with TurboGears2
Reactive & Realtime Web Applications with TurboGears2Reactive & Realtime Web Applications with TurboGears2
Reactive & Realtime Web Applications with TurboGears2Alessandro Molina
 
PlayFab Advanced Cloud Script
PlayFab Advanced Cloud ScriptPlayFab Advanced Cloud Script
PlayFab Advanced Cloud ScriptThomas Robbins
 
A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...Tom Croucher
 
Rhebok, High Performance Rack Handler / Rubykaigi 2015
Rhebok, High Performance Rack Handler / Rubykaigi 2015Rhebok, High Performance Rack Handler / Rubykaigi 2015
Rhebok, High Performance Rack Handler / Rubykaigi 2015Masahiro Nagano
 
Monitoring und Metriken im Wunderland
Monitoring und Metriken im WunderlandMonitoring und Metriken im Wunderland
Monitoring und Metriken im WunderlandD
 
HTML5 Server Sent Events/JSF JAX 2011 Conference
HTML5 Server Sent Events/JSF  JAX 2011 ConferenceHTML5 Server Sent Events/JSF  JAX 2011 Conference
HTML5 Server Sent Events/JSF JAX 2011 ConferenceRoger Kitain
 
Server Side Events
Server Side EventsServer Side Events
Server Side Eventsthepilif
 
soft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch
 

Ähnlich wie Live Streaming & Server Sent Events (20)

Intro to Node
Intro to NodeIntro to Node
Intro to Node
 
Spring Web Services: SOAP vs. REST
Spring Web Services: SOAP vs. RESTSpring Web Services: SOAP vs. REST
Spring Web Services: SOAP vs. REST
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
 
Future Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETFuture Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NET
 
A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...
 
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
Node.js: Continuation-Local-Storage and the Magic of AsyncListenerNode.js: Continuation-Local-Storage and the Magic of AsyncListener
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
 
Intoduction to Play Framework
Intoduction to Play FrameworkIntoduction to Play Framework
Intoduction to Play Framework
 
Comet from JavaOne 2008
Comet from JavaOne 2008Comet from JavaOne 2008
Comet from JavaOne 2008
 
Introduction of server sent events (sse)
Introduction of server sent events (sse)Introduction of server sent events (sse)
Introduction of server sent events (sse)
 
Speed up your Web applications with HTML5 WebSockets
Speed up your Web applications with HTML5 WebSocketsSpeed up your Web applications with HTML5 WebSockets
Speed up your Web applications with HTML5 WebSockets
 
Intro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiIntro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran Mizrahi
 
Sherlock Homepage - A detective story about running large web services - NDC ...
Sherlock Homepage - A detective story about running large web services - NDC ...Sherlock Homepage - A detective story about running large web services - NDC ...
Sherlock Homepage - A detective story about running large web services - NDC ...
 
Reactive & Realtime Web Applications with TurboGears2
Reactive & Realtime Web Applications with TurboGears2Reactive & Realtime Web Applications with TurboGears2
Reactive & Realtime Web Applications with TurboGears2
 
PlayFab Advanced Cloud Script
PlayFab Advanced Cloud ScriptPlayFab Advanced Cloud Script
PlayFab Advanced Cloud Script
 
A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...
 
Rhebok, High Performance Rack Handler / Rubykaigi 2015
Rhebok, High Performance Rack Handler / Rubykaigi 2015Rhebok, High Performance Rack Handler / Rubykaigi 2015
Rhebok, High Performance Rack Handler / Rubykaigi 2015
 
Monitoring und Metriken im Wunderland
Monitoring und Metriken im WunderlandMonitoring und Metriken im Wunderland
Monitoring und Metriken im Wunderland
 
HTML5 Server Sent Events/JSF JAX 2011 Conference
HTML5 Server Sent Events/JSF  JAX 2011 ConferenceHTML5 Server Sent Events/JSF  JAX 2011 Conference
HTML5 Server Sent Events/JSF JAX 2011 Conference
 
Server Side Events
Server Side EventsServer Side Events
Server Side Events
 
soft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.js
 

Mehr von tkramar

Lessons learned from SearchD development
Lessons learned from SearchD developmentLessons learned from SearchD development
Lessons learned from SearchD developmenttkramar
 
Learning to rank fulltext results from clicks
Learning to rank fulltext results from clicksLearning to rank fulltext results from clicks
Learning to rank fulltext results from clickstkramar
 
Unix is my IDE
Unix is my IDEUnix is my IDE
Unix is my IDEtkramar
 
Optimising Web Application Frontend
Optimising Web Application FrontendOptimising Web Application Frontend
Optimising Web Application Frontendtkramar
 
MongoDB: Repository for Web-scale metadata
MongoDB: Repository for Web-scale metadataMongoDB: Repository for Web-scale metadata
MongoDB: Repository for Web-scale metadatatkramar
 
Cassandra: Indexing and discovering similar images
Cassandra: Indexing and discovering similar imagesCassandra: Indexing and discovering similar images
Cassandra: Indexing and discovering similar imagestkramar
 
CouchDB: replicated data store for distributed proxy server
CouchDB: replicated data store for distributed proxy serverCouchDB: replicated data store for distributed proxy server
CouchDB: replicated data store for distributed proxy servertkramar
 
Ruby vim
Ruby vimRuby vim
Ruby vimtkramar
 

Mehr von tkramar (8)

Lessons learned from SearchD development
Lessons learned from SearchD developmentLessons learned from SearchD development
Lessons learned from SearchD development
 
Learning to rank fulltext results from clicks
Learning to rank fulltext results from clicksLearning to rank fulltext results from clicks
Learning to rank fulltext results from clicks
 
Unix is my IDE
Unix is my IDEUnix is my IDE
Unix is my IDE
 
Optimising Web Application Frontend
Optimising Web Application FrontendOptimising Web Application Frontend
Optimising Web Application Frontend
 
MongoDB: Repository for Web-scale metadata
MongoDB: Repository for Web-scale metadataMongoDB: Repository for Web-scale metadata
MongoDB: Repository for Web-scale metadata
 
Cassandra: Indexing and discovering similar images
Cassandra: Indexing and discovering similar imagesCassandra: Indexing and discovering similar images
Cassandra: Indexing and discovering similar images
 
CouchDB: replicated data store for distributed proxy server
CouchDB: replicated data store for distributed proxy serverCouchDB: replicated data store for distributed proxy server
CouchDB: replicated data store for distributed proxy server
 
Ruby vim
Ruby vimRuby vim
Ruby vim
 

Kürzlich hochgeladen

4. Cobus Valentine- Cybersecurity Threats and Solutions for the Public Sector
4. Cobus Valentine- Cybersecurity Threats and Solutions for the Public Sector4. Cobus Valentine- Cybersecurity Threats and Solutions for the Public Sector
4. Cobus Valentine- Cybersecurity Threats and Solutions for the Public Sectoritnewsafrica
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integrationmarketing932765
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Mark Goldstein
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI AgeCprime
 
Kuma Meshes Part I - The basics - A tutorial
Kuma Meshes Part I - The basics - A tutorialKuma Meshes Part I - The basics - A tutorial
Kuma Meshes Part I - The basics - A tutorialJoão Esperancinha
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesThousandEyes
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesThousandEyes
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesKari Kakkonen
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch TuesdayIvanti
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfpanagenda
 
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...BookNet Canada
 
Accelerating Enterprise Software Engineering with Platformless
Accelerating Enterprise Software Engineering with PlatformlessAccelerating Enterprise Software Engineering with Platformless
Accelerating Enterprise Software Engineering with PlatformlessWSO2
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Nikki Chapple
 
Irene Moetsana-Moeng: Stakeholders in Cybersecurity: Collaborative Defence fo...
Irene Moetsana-Moeng: Stakeholders in Cybersecurity: Collaborative Defence fo...Irene Moetsana-Moeng: Stakeholders in Cybersecurity: Collaborative Defence fo...
Irene Moetsana-Moeng: Stakeholders in Cybersecurity: Collaborative Defence fo...itnewsafrica
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentPim van der Noll
 
Digital Tools & AI in Career Development
Digital Tools & AI in Career DevelopmentDigital Tools & AI in Career Development
Digital Tools & AI in Career DevelopmentMahmoud Rabie
 
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...Karmanjay Verma
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 

Kürzlich hochgeladen (20)

4. Cobus Valentine- Cybersecurity Threats and Solutions for the Public Sector
4. Cobus Valentine- Cybersecurity Threats and Solutions for the Public Sector4. Cobus Valentine- Cybersecurity Threats and Solutions for the Public Sector
4. Cobus Valentine- Cybersecurity Threats and Solutions for the Public Sector
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI Age
 
Kuma Meshes Part I - The basics - A tutorial
Kuma Meshes Part I - The basics - A tutorialKuma Meshes Part I - The basics - A tutorial
Kuma Meshes Part I - The basics - A tutorial
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examples
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch Tuesday
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
 
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
 
Accelerating Enterprise Software Engineering with Platformless
Accelerating Enterprise Software Engineering with PlatformlessAccelerating Enterprise Software Engineering with Platformless
Accelerating Enterprise Software Engineering with Platformless
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
 
Irene Moetsana-Moeng: Stakeholders in Cybersecurity: Collaborative Defence fo...
Irene Moetsana-Moeng: Stakeholders in Cybersecurity: Collaborative Defence fo...Irene Moetsana-Moeng: Stakeholders in Cybersecurity: Collaborative Defence fo...
Irene Moetsana-Moeng: Stakeholders in Cybersecurity: Collaborative Defence fo...
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
 
Digital Tools & AI in Career Development
Digital Tools & AI in Career DevelopmentDigital Tools & AI in Career Development
Digital Tools & AI in Career Development
 
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 

Live Streaming & Server Sent Events

  • 1. Live Streaming & Server Sent Events Tomáš Kramár @tkramar
  • 2. When? ● Server needs to stream data to client – Server decides when and what to send – Client waits and listens – Client does not need to send messages – Uni-directional communication – Asynchronously
  • 3. How? / Terminology ● AJAX polling ● Comet ● WebSockets ● Server-Sent Events
  • 4. AJAX polling Any news? Browser/Client Server
  • 5. AJAX polling Any news? No Browser/Client Server
  • 6. AJAX polling Any news? No Any news? No Browser/Client Server
  • 7. AJAX polling Any news? No Any news? No Any news? Browser/Client Yes! Server
  • 8. AJAX polling Any news? No Any news? No Any news? Browser/Client Yes! Server Any news? No
  • 9. AJAX polling ● Overhead – Establishing new connections, TCP handshakes – Sending HTTP headers – Multiply by number of clients ● Not really realtime – Poll each 2 seconds
  • 10. Comet ● set of technology principles/communication patterns ● mostly hacks – forever-iframe – htmlfile ActiveX object – XHR multipart/streaming/long-polling – Flash – ..
  • 11. WebSockets ● bi-directional, full-duplex communication channels over a single TCP connection ● HTML5 ● being standardized
  • 12. Server-Sent Events ● HTML5 ● Traditional HTTP – No special protocol or server implementation ● Browser establishes single connection and waits ● Server generates events
  • 13. SSE Request w parameters id: 1 event: display data: { foo: 'moo' } Browser/Client Server
  • 14. SSE Request w parameters id: 1 event: display data: { foo: 'moo' } Browser/Client id: 2 Server event: redraw data: { boo: 'hoo' }
  • 15. Case study ● Live search in trademark databases ● query – search in register #1 ● Search (~15s), parse search result list, fetch each result (~3s each), go to next page in search result list (~10s), fetch each result, ... – search in register #2 ● ... – … ● Don't let the user wait, display results when they are available
  • 16. Demo
  • 17. Client this.source = new EventSource('marks/search'); self.source.addEventListener('results', function(e) { self.marks.appendMarks($.parseJSON(e.data)); }); self.source.addEventListener('failure', function(e) { self.errors.showError(); }); self.source.addEventListener('status', function(e) { self.paging.update($.parseJSON(e.data)); });
  • 18. Client gotchas ● Special events: – open – error ● Don't forget to close the request self.source.addEventListener('finished', function(e) { self.status.searchFinished(); self.source.close(); });
  • 19. Server ● Must support – long-running request – Live-streaming (i.e., no output buffering) ● Rainbows!, Puma or Thin ● Rails 4 (beta) supports live streaming
  • 20. Rails 4 Live Streaming class MarksController < ApplicationController include ActionController::Live def results response.headers['Content-Type'] = 'text/event-stream' sse = SSE.new(response.stream) Tort.search(params[:query]) do |on| on.results do |hits| sse.write(hits, event: 'result') end on.status_change do |status| sse.write(status, event: 'status') end on.error do sse.write({}, event: 'failure') end end end end
  • 21. require 'json' class SSE def initialize io event: displayn @io = io data: { foo: 'moo' }nn end def write object, options = {} options.each do |k,v| @io.write "#{k}: #{v}n" end @io.write "data: #{JSON.dump(object)}nn" end def close @io.close end end
  • 22. Timeouts, lost connections, internet explorers and other bad things ● EventSource request can be interrupted ● EventSource will reconnect automatically ● What happens with the data during the time connection was not available?
  • 23. Handling reconnections ● When EventSource reconnects, we need to continue sending the data from the point the connection was lost – Do the work in the background and store events somewhere – In the controller, load events from the storage ● EventSource sends Last-Event-Id in HTTP header – But we don't need it if we remove the processed events
  • 24. marks/search?q=eset GirlFriday Search 3342345 HTTP 202 Accepted marks/results?job_id=3342345 Browser Server Redis marks/results?job_id=3342345 MarksController event: results data: {foo: 'boo'} event: status data: {moo: 'hoo'}
  • 25. class MarksController < ApplicationController include ActionController::Live def search! uuid = UUID.new.generate(:compact) TORT_QUEUE << { phrase: params[:q], job_id: uuid } render status: 202, text: marks_results_path(job: uuid) end def results response.headers['Content-Type'] = 'text/event-stream' sse = SSE.new(response.stream) queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis) finished = false begin begin queue.next_message do |json_message| message = JSON.parse(json_message) case message["type"] when "results" then sse.write(message["data"], event: 'results') when "failure" then sse.write({}, event: 'failure') when "fatal" then sse.write({}, event: 'fatal') finished = true when "status" then sse.write(message["data"], event: 'status') when "finished" then sse.write({}, event: 'finished') finished = true end end end while !finished rescue IOError # when clients disconnects ensure sse.close end end end
  • 26. class MarksController < ApplicationController include ActionController::Live def search! uuid = UUID.new.generate(:compact) TORT_QUEUE << { phrase: params[:q], job_id: uuid } generate job_id render status: 202, text: marks_results_path(job: uuid) end def results response.headers['Content-Type'] = 'text/event-stream' sse = SSE.new(response.stream) queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis) finished = false begin begin queue.next_message do |json_message| message = JSON.parse(json_message) case message["type"] when "results" then sse.write(message["data"], event: 'results') when "failure" then sse.write({}, event: 'failure') when "fatal" then sse.write({}, event: 'fatal') finished = true when "status" then sse.write(message["data"], event: 'status') when "finished" then sse.write({}, event: 'finished') finished = true end end end while !finished rescue IOError # when clients disconnects ensure sse.close end end end
  • 27. class MarksController < ApplicationController include ActionController::Live def search! uuid = UUID.new.generate(:compact) TORT_QUEUE << { phrase: params[:q], job_id: uuid } start async job (GirlFriday) render status: 202, text: marks_results_path(job: uuid) end def results response.headers['Content-Type'] = 'text/event-stream' sse = SSE.new(response.stream) queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis) finished = false begin begin queue.next_message do |json_message| message = JSON.parse(json_message) case message["type"] when "results" then sse.write(message["data"], event: 'results') when "failure" then sse.write({}, event: 'failure') when "fatal" then sse.write({}, event: 'fatal') finished = true when "status" then sse.write(message["data"], event: 'status') when "finished" then sse.write({}, event: 'finished') finished = true end end end while !finished rescue IOError # when clients disconnects ensure sse.close end end end
  • 28. class MarksController < ApplicationController include ActionController::Live def search! uuid = UUID.new.generate(:compact) TORT_QUEUE << { phrase: params[:q], job_id: uuid } render status: 202, text: marks_results_path(job: uuid) end send results URL def results response.headers['Content-Type'] = 'text/event-stream' sse = SSE.new(response.stream) queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis) finished = false begin begin queue.next_message do |json_message| message = JSON.parse(json_message) case message["type"] when "results" then sse.write(message["data"], event: 'results') when "failure" then sse.write({}, event: 'failure') when "fatal" then sse.write({}, event: 'fatal') finished = true when "status" then sse.write(message["data"], event: 'status') when "finished" then sse.write({}, event: 'finished') finished = true end end end while !finished rescue IOError # when clients disconnects ensure sse.close end end end
  • 29. class MarksController < ApplicationController include ActionController::Live def search! uuid = UUID.new.generate(:compact) TORT_QUEUE << { phrase: params[:q], job_id: uuid } render status: 202, text: marks_results_path(job: uuid) end def results response.headers['Content-Type'] = 'text/event-stream' sse = SSE.new(response.stream) queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis) Get queue for this job, finished = false async job is pushing begin to this queue begin queue.next_message do |json_message| message = JSON.parse(json_message) case message["type"] when "results" then sse.write(message["data"], event: 'results') when "failure" then sse.write({}, event: 'failure') when "fatal" then sse.write({}, event: 'fatal') finished = true when "status" then sse.write(message["data"], event: 'status') when "finished" then sse.write({}, event: 'finished') finished = true end end end while !finished rescue IOError # when clients disconnects ensure sse.close end end end
  • 30. class MarksController < ApplicationController include ActionController::Live def search! uuid = UUID.new.generate(:compact) TORT_QUEUE << { phrase: params[:q], job_id: uuid } render status: 202, text: marks_results_path(job: uuid) end def results response.headers['Content-Type'] = 'text/event-stream' sse = SSE.new(response.stream) queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis) finished = false begin begin queue.next_message do |json_message| Fetch next message message = JSON.parse(json_message) from queue (blocks until case message["type"] when "results" then one is available) sse.write(message["data"], event: 'results') when "failure" then sse.write({}, event: 'failure') when "fatal" then sse.write({}, event: 'fatal') finished = true when "status" then sse.write(message["data"], event: 'status') when "finished" then sse.write({}, event: 'finished') finished = true end end end while !finished rescue IOError # when clients disconnects ensure sse.close end end end
  • 31. class MarksController < ApplicationController include ActionController::Live def search! uuid = UUID.new.generate(:compact) TORT_QUEUE << { phrase: params[:q], job_id: uuid } render status: 202, text: marks_results_path(job: uuid) end def results response.headers['Content-Type'] = 'text/event-stream' sse = SSE.new(response.stream) queue = SafeQueue.new(Channel.for_job(params[:job]), Tmzone.redis) finished = false begin begin queue.next_message do |json_message| message = JSON.parse(json_message) case message["type"] when "results" then sse.write(message["data"], event: 'results') when "failure" then sse.write({}, event: 'failure') when "fatal" then sse.write({}, event: 'fatal') finished = true when "status" then sse.write(message["data"], event: 'status') when "finished" then sse.write({}, event: 'finished') finished = true end end end while !finished rescue IOError # when clients disconnects IOError is raised when client ensure sse.close disconnected and we are end end writing to response.stream end
  • 32. GirlFriday worker class SearchWorker def self.perform(phrase, job_id) channel = Channel.for_job(job_id) queue = SafeQueue.new(channel, Tmzone.redis) Tort.search(phrase) do |on| on.results do |hits| queue.push({ type: "results", data: hits }.to_json) end on.status_change do |status| queue.push({ type: "status", data: status }.to_json) end on.error do queue.push({ type: 'failure' }.to_json) end end queue.push({ type: "finished" }.to_json) end end
  • 33. SafeQueue class SafeQueue def initialize(channel, redis) @channel = channel @redis = redis end def next_message(&block) begin _, message = @redis.blpop(@channel) block.call(message) rescue => error @redis.lpush(@channel, message) raise error end end def push(message) @redis.rpush(@channel, message) end end
  • 34. EventSource Compatibility ● Firefox 6+, Chrome 6+, Safari 5+, Opera 11+, iOS Safari 4+, Blackberry, Opera Mobile, Chrome for Android, Firefox for Android
  • 35. Fallback ● Polyfills – https://github.com/remy/polyfills/blob/master/Event Source.js ● Hanging GET, waits until the request terminates, essentially buffering the live output – https://github.com/Yaffle/EventSource ● send a keep-alive message each 15 seconds
  • 36. Summary ● Unidirectional server-to-client communication ● Single request ● Real-time ● Easy to implement ● Well supported except for IE