Skip to content

Instantly share code, notes, and snippets.

@alvinlai
Forked from siddharthbhagwan/Gemfile
Last active August 29, 2015 14:22
Show Gist options
  • Save alvinlai/83494e68844c211018c8 to your computer and use it in GitHub Desktop.
Save alvinlai/83494e68844c211018c8 to your computer and use it in GitHub Desktop.
This gist indends to show a sample run of the ActionController Live, included in Rails 4.
It also shows some issues faced in the common implementations. This approach is common for implemeting chat systems, and push notifications.
Solution to the issue isn't given here, but I've included links to the discussions that deal with the issues in depth.
An up and running redis server is a prerequisite for this demo.
To understand and analyze this scenario, it would be helpful to monitor Redis as well. This can be done by running the following two commands in the shell
redis-cli
monitor
The gist shows how 2 clients, in this case, Chrome and Firefox are listenting on the servers for data. On pushing data to redis with the appropriate handlers, data should be alerted on the respecive browsers. For instance, in the rails console
redis_conn = Redis.new # makes a default connection on localhost, port 6379
redis_conn.publish('listener_ff', 'Data for Firefox!')
This can also be done via a controller action specified for each browser.
Refreshing this page 5 times (in my case, or whatever is the max connection pool) leads the server to hang, as the connections have been maxed out.
You can try this with threading as well ( just uncomment the code ), and with unicorn server as well ( current is Puma, WebBrick won't work as it buffers the data and sends it in one go )
Below are links that should help you better udnerstand the Action Controller Live
http://tenderlovemaking.com/2012/07/30/is-it-live.html
https://github.com/rails/rails/issues/10989
Some hacks to this connection issues are using a poker/heartbeat.
Using an evented server should solve the issue altogether.
Havent tried it yet, but cramp is one such example - http://cramp.in/
config.cache_classes = true
config.eager_load = true
source 'https://rubygems.org'
gem 'rails', '4.0.2'
gem "redis-rails", "~> 4.0.0"
gem 'puma'
gem 'unicorn'
# Use sqlite3 as the database for Active Record
gem 'sqlite3'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 4.0.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# Use CoffeeScript for .js.coffee assets and views
gem 'coffee-rails', '~> 4.0.0'
# Use jquery as the JavaScript library
gem 'jquery-rails'
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
gem 'turbolinks'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 1.2'
group :doc do
# bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc', require: false
end
ACL::Application.routes.draw do
get 'chrome' => 'sse#sse_chrome'
get 'ff' => 'sse#sse_ff'
get 'pub_chrome' => 'sse#publish_to_chrome'
get 'pub_ff' => 'sse#publish_to_ff'
get 'hello' => 'sse#hello'
get 'hello_world' => 'sse#hello_world'
end
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
jQuery ->
# Check What browser you're running - programmed just to check FF and Chrome
browser = window.navigator.userAgent;
if browser.indexOf('Chrome') isnt -1
browser_action = 'chrome'
else
browser_action = 'ff'
# Creating handles to listen on, specific to the browser
# Assuming Chrome data will be sent on 'listener_chrome' and
# Similarly F data on 'listener_ff'
event_listener = 'listener_' + browser_action
# Event Source is listening on a controller action. Check routes.rb
# to see what browser_action is mapped to
source = new EventSource(browser_action)
source.addEventListener event_listener, (ea) ->
alert 'Data for ' + browser_action + ' : ' + ea.data
class SseController < ApplicationController
include ActionController::Live
def sse_chrome
response.headers["Content-Type"] = "text/event-stream"
redis_sub_chrome = Redis.new()
subscribe_channel = 'listener_chrome'
#Thread.new do
redis_sub_chrome.subscribe(subscribe_channel) do |on|
on.message do |event, data|
response.stream.write("event: #{event}\n")
response.stream.write("data: #{data}\n\n")
end
end
#end
rescue IOError
logger.info "Stream Closed"
ensure
redis_sub_chrome.quit
response.stream.close
end
# Fn to publish data with handle for the listener on chrome
def publish_to_chrome
response.headers["Content-Type"] = "text/event-stream"
redis_chrome = Redis.new()
10.times{
redis_chrome.publish('listener_chrome', 'Data for Chrome!')
}
render nothing: true
end
def sse_ff
response.headers["Content-Type"] = "text/event-stream"
redis_sub_chrome = Redis.new()
subscribe_channel = 'listener_ff'
#Thread.new do
redis_sub_chrome.subscribe(subscribe_channel) do |on|
on.message do |event, data|
response.stream.write("event: #{event}\n")
response.stream.write("data: #{data}\n\n")
end
end
#end
rescue IOError
logger.info "Stream Closed"
ensure
redis_sub_chrome.quit
response.stream.close
end
# Fn to publish data with handle for the listener on FireFox
def publish_to_ff
response.headers["Content-Type"] = "text/event-stream"
redis_chrome = Redis.new()
10.times{
redis_chrome.publish('listener_ff', 'Data for FF!')
}
render nothing: true
end
# Fn to show simple ACL demo
def hello_world
response.headers['Content-Type'] = 'text/event-stream'
10.times do |i|
response.stream.write "hello world #{i}\n"
sleep 1
end
response.stream.close
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment