SlideShare une entreprise Scribd logo
1  sur  36
Télécharger pour lire hors ligne
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

Contenu connexe

Tendances

What is REST API? REST API Concepts and Examples | Edureka
What is REST API? REST API Concepts and Examples | EdurekaWhat is REST API? REST API Concepts and Examples | Edureka
What is REST API? REST API Concepts and Examples | EdurekaEdureka!
 
API Security : Patterns and Practices
API Security : Patterns and PracticesAPI Security : Patterns and Practices
API Security : Patterns and PracticesPrabath Siriwardena
 
Cloud Migration Patterns: A Multi-Cloud Architectural Perspective
Cloud Migration Patterns: A Multi-Cloud Architectural PerspectiveCloud Migration Patterns: A Multi-Cloud Architectural Perspective
Cloud Migration Patterns: A Multi-Cloud Architectural PerspectivePooyan Jamshidi
 
Understanding MicroSERVICE Architecture with Java & Spring Boot
Understanding MicroSERVICE Architecture with Java & Spring BootUnderstanding MicroSERVICE Architecture with Java & Spring Boot
Understanding MicroSERVICE Architecture with Java & Spring BootKashif Ali Siddiqui
 
AngularJS Security: defend your Single Page Application
AngularJS Security: defend your Single Page Application AngularJS Security: defend your Single Page Application
AngularJS Security: defend your Single Page Application Carlo Bonamico
 
2015/06/12 - IBM Systems & Middleware - IBM DataPower and API Management
2015/06/12 - IBM Systems & Middleware - IBM DataPower and API Management2015/06/12 - IBM Systems & Middleware - IBM DataPower and API Management
2015/06/12 - IBM Systems & Middleware - IBM DataPower and API ManagementRui Santos
 
Microservices Design Patterns
Microservices Design PatternsMicroservices Design Patterns
Microservices Design PatternsHaim Michael
 
Leveraging Envoy Proxy and GraphQL to Lower the Risk of Monolith to Microserv...
Leveraging Envoy Proxy and GraphQL to Lower the Risk of Monolith to Microserv...Leveraging Envoy Proxy and GraphQL to Lower the Risk of Monolith to Microserv...
Leveraging Envoy Proxy and GraphQL to Lower the Risk of Monolith to Microserv...Christian Posta
 
WebLogic in Practice: SSL Configuration
WebLogic in Practice: SSL ConfigurationWebLogic in Practice: SSL Configuration
WebLogic in Practice: SSL ConfigurationSimon Haslam
 
AWS Foundational and Platform Services - Module 1 Parts 2 & 3 - AWSome Day 2017
AWS Foundational and Platform Services - Module 1 Parts 2 & 3 - AWSome Day 2017AWS Foundational and Platform Services - Module 1 Parts 2 & 3 - AWSome Day 2017
AWS Foundational and Platform Services - Module 1 Parts 2 & 3 - AWSome Day 2017Amazon Web Services
 
CNIT 129S: 12: Attacking Users: Cross-Site Scripting (Part 1 of 2)
CNIT 129S: 12: Attacking Users: Cross-Site Scripting (Part 1 of 2)CNIT 129S: 12: Attacking Users: Cross-Site Scripting (Part 1 of 2)
CNIT 129S: 12: Attacking Users: Cross-Site Scripting (Part 1 of 2)Sam Bowne
 
Cloud App Security Customer Presentation.pdf
Cloud App Security Customer Presentation.pdfCloud App Security Customer Presentation.pdf
Cloud App Security Customer Presentation.pdfErikHof4
 
Introduction to SAML 2.0
Introduction to SAML 2.0Introduction to SAML 2.0
Introduction to SAML 2.0Mika Koivisto
 
Implementing Microservices Security Patterns & Protocols with Spring
Implementing Microservices Security Patterns & Protocols with SpringImplementing Microservices Security Patterns & Protocols with Spring
Implementing Microservices Security Patterns & Protocols with SpringVMware Tanzu
 
Partner Briefing_January 25 (FINAL).pptx
Partner Briefing_January 25 (FINAL).pptxPartner Briefing_January 25 (FINAL).pptx
Partner Briefing_January 25 (FINAL).pptxCloudera, Inc.
 
Microsoft Azure Security Overview
Microsoft Azure Security OverviewMicrosoft Azure Security Overview
Microsoft Azure Security OverviewAlert Logic
 

Tendances (20)

What is REST API? REST API Concepts and Examples | Edureka
What is REST API? REST API Concepts and Examples | EdurekaWhat is REST API? REST API Concepts and Examples | Edureka
What is REST API? REST API Concepts and Examples | Edureka
 
API Security : Patterns and Practices
API Security : Patterns and PracticesAPI Security : Patterns and Practices
API Security : Patterns and Practices
 
Cloud Migration Patterns: A Multi-Cloud Architectural Perspective
Cloud Migration Patterns: A Multi-Cloud Architectural PerspectiveCloud Migration Patterns: A Multi-Cloud Architectural Perspective
Cloud Migration Patterns: A Multi-Cloud Architectural Perspective
 
Understanding MicroSERVICE Architecture with Java & Spring Boot
Understanding MicroSERVICE Architecture with Java & Spring BootUnderstanding MicroSERVICE Architecture with Java & Spring Boot
Understanding MicroSERVICE Architecture with Java & Spring Boot
 
AngularJS Security: defend your Single Page Application
AngularJS Security: defend your Single Page Application AngularJS Security: defend your Single Page Application
AngularJS Security: defend your Single Page Application
 
2015/06/12 - IBM Systems & Middleware - IBM DataPower and API Management
2015/06/12 - IBM Systems & Middleware - IBM DataPower and API Management2015/06/12 - IBM Systems & Middleware - IBM DataPower and API Management
2015/06/12 - IBM Systems & Middleware - IBM DataPower and API Management
 
AWS IoT Webinar
AWS IoT WebinarAWS IoT Webinar
AWS IoT Webinar
 
Microservices Design Patterns
Microservices Design PatternsMicroservices Design Patterns
Microservices Design Patterns
 
Leveraging Envoy Proxy and GraphQL to Lower the Risk of Monolith to Microserv...
Leveraging Envoy Proxy and GraphQL to Lower the Risk of Monolith to Microserv...Leveraging Envoy Proxy and GraphQL to Lower the Risk of Monolith to Microserv...
Leveraging Envoy Proxy and GraphQL to Lower the Risk of Monolith to Microserv...
 
WebLogic in Practice: SSL Configuration
WebLogic in Practice: SSL ConfigurationWebLogic in Practice: SSL Configuration
WebLogic in Practice: SSL Configuration
 
AWS Foundational and Platform Services - Module 1 Parts 2 & 3 - AWSome Day 2017
AWS Foundational and Platform Services - Module 1 Parts 2 & 3 - AWSome Day 2017AWS Foundational and Platform Services - Module 1 Parts 2 & 3 - AWSome Day 2017
AWS Foundational and Platform Services - Module 1 Parts 2 & 3 - AWSome Day 2017
 
Security Architectures on AWS
Security Architectures on AWSSecurity Architectures on AWS
Security Architectures on AWS
 
CNIT 129S: 12: Attacking Users: Cross-Site Scripting (Part 1 of 2)
CNIT 129S: 12: Attacking Users: Cross-Site Scripting (Part 1 of 2)CNIT 129S: 12: Attacking Users: Cross-Site Scripting (Part 1 of 2)
CNIT 129S: 12: Attacking Users: Cross-Site Scripting (Part 1 of 2)
 
Cloud App Security Customer Presentation.pdf
Cloud App Security Customer Presentation.pdfCloud App Security Customer Presentation.pdf
Cloud App Security Customer Presentation.pdf
 
REST API
REST APIREST API
REST API
 
Introduction to SAML 2.0
Introduction to SAML 2.0Introduction to SAML 2.0
Introduction to SAML 2.0
 
Implementing Microservices Security Patterns & Protocols with Spring
Implementing Microservices Security Patterns & Protocols with SpringImplementing Microservices Security Patterns & Protocols with Spring
Implementing Microservices Security Patterns & Protocols with Spring
 
Introduction to JAX-RS
Introduction to JAX-RSIntroduction to JAX-RS
Introduction to JAX-RS
 
Partner Briefing_January 25 (FINAL).pptx
Partner Briefing_January 25 (FINAL).pptxPartner Briefing_January 25 (FINAL).pptx
Partner Briefing_January 25 (FINAL).pptx
 
Microsoft Azure Security Overview
Microsoft Azure Security OverviewMicrosoft Azure Security Overview
Microsoft Azure Security Overview
 

Similaire à 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
 

Similaire à 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
 

Plus de 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
 

Plus de 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
 

Dernier

Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...DianaGray10
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024The Digital Insurer
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?Antenna Manufacturer Coco
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfhans926745
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAndrey Devyatkin
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobeapidays
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdflior mazor
 

Dernier (20)

Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 

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