jugglite 0.0.1.alpha → 0.0.2.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - rbx-19mode
data/CHANGELOG ADDED
@@ -0,0 +1,11 @@
1
+ = 0.0.3 / Not Yet Released
2
+
3
+ * TODO: add support for Remy's Polyfill
4
+
5
+ = 0.0.2.alpha / 2012-11-04
6
+
7
+ * standalone binary supports some thin commandline options
8
+
9
+ = 0.0.1.alpha / 2012-11-15
10
+
11
+ * Initial gem release
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in jugglite.gemspec
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem 'rake'
8
+ end
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Jugglite
2
2
 
3
- Jugglite is a replacement for the incredible [Juggernaut](https://github.com/maccman/juggernaut) by Maccman. It uses [Server Sent Events](http://www.html5rocks.com/en/tutorials/eventsource/basics/) to push events from your application to the client's browser.
3
+ [![Build Status](https://secure.travis-ci.org/andruby/jugglite.png?branch=master)](https://travis-ci.org/andruby/jugglite)
4
+
5
+ Jugglite is a replacement for the incredible [Juggernaut](https://github.com/maccman/juggernaut) by Maccman. It uses [Server Sent Events](http://www.html5rocks.com/en/tutorials/eventsource/basics/) to push events from your application to the client's browser. It uses [Redis](http://www.redis.io) for publish/subscribe and [Thin](http://code.macournoyer.com/thin/) + [EventMachine](https://github.com/eventmachine/eventmachine) to run an evented server that can handle 10K+ concurrent connections.
4
6
 
5
7
  ## Installation
6
8
 
@@ -16,16 +18,72 @@ Or install it yourself as:
16
18
 
17
19
  $ gem install jugglite
18
20
 
19
- ## Usage
21
+ ## Server Usage
22
+
23
+ I use jugglite as rack middleware in development and as a standalone cluster in production behind nginx.
24
+
25
+ ### Stand-alone binary
20
26
 
21
27
  Jugglite comes with a binary. This binary runs a thin server that listens on redis for application messages and passes it along to all connected clients.
22
28
 
23
- You can run the binary from any terminal
24
- `jugglite`
29
+ You can run the binary from any terminal like this (these options are the defaults):
30
+
31
+ `jugglite --address 0.0.0.0 --port 3000 --max-conns 1024`
32
+
33
+ ### As rack middleware
34
+
35
+ Add it to your `config.ru` file:
36
+ `use Juglite::App, path: '/stream'` (use the `path` option )
37
+
38
+ ### As a cluster behind Nginx reverse proxy
39
+
40
+ NOTE: because the html5 SSE implementation requires the connection to have the same hostname and port, you'll need to add a reverse proxy in front of your app and jugglite.
41
+
42
+ TODO with Foreman?
43
+
44
+ ## Client Usage
45
+
46
+ Use the browser's native [Server-Sent Events](http://www.html5rocks.com/en/tutorials/eventsource/basics/) implementation:
47
+
48
+ ```javascript
49
+ es = new EventSource('/stream?channel=yourchannelname');
50
+
51
+ es.addEventListener('message', function(e) {
52
+ // Do something with the data
53
+ console.log(e.data);
54
+ // If you JSON encoded the message
55
+ msg = jQuery.parseJSON(e.data);
56
+ }, false);
57
+
58
+ es.onopen = function(e) {
59
+ // Connection was opened.
60
+ };
61
+
62
+ es.onerror = function(e) {
63
+ if (e.readyState == EventSource.CLOSED) {
64
+ // Connection was closed.
65
+ } else {
66
+ // Some other error?
67
+ };
68
+ };
69
+ ```
70
+
71
+ To support older browsers, use [Remy's](http://html5doctor.com/server-sent-events/) excellent [Pollyfill](https://github.com/remy/polyfills/blob/master/EventSource.js). It does revert to ajax long polling for browsers without a native EventSource implementation. Supports almost every old browser (even IE7).
72
+
73
+ ## Sending messages
74
+
75
+ Use your favorite Redis client to simply publish messages to the channel your clients are subscribing to:
76
+
77
+ ```ruby
78
+ redis = Redis.new
79
+ redis.publish('yourchannelname', 'This is a message')
80
+ # You may want to JSON encode your data
81
+ redis.publish('yourchannelname', {hello: 'world', number: 47}.to_json)
82
+ ```
25
83
 
26
- TODO: Foreman & multiple processes
84
+ ## Performance
27
85
 
28
- TODO: Behind nginx so the client connects on one port
86
+ It's been tested on a local machine with the `spec/benchmark/max_connections.rb` spec up to 16K concurrent connections.
29
87
 
30
88
  ## Contributing
31
89
 
data/bin/jugglite CHANGED
@@ -1,15 +1,20 @@
1
1
  #!/usr/bin/env ruby
2
-
3
- host = ENV['HOST'] || '127.0.0.1'
4
- port = (ENV['PORT'] || 3000).to_i
5
- file_descriptors = (ENV['FD_SIZE'] || 202400).to_i
6
-
7
2
  require 'jugglite'
8
3
 
9
- new_size = EM.set_descriptor_table_size( file_descriptors )
10
- STDERR.puts "New descriptor-table size is #{new_size}"
11
- EM.epoll
12
- EM.kqueue
4
+ # You may use (almost) all options you would pass to thin
5
+ # eg: --max-conns NUM (might require a higher system "ulimit -n" setting)
6
+ # --port PORT
7
+ # --address HOST
8
+ options = Thin::Runner.new(ARGV).options
9
+ thin = Thin::Server.new(Jugglite::App.new, options[:port], options[:address], options)
10
+
11
+ # Need to increase the descriptor-table for EventMachine
12
+ if options[:max_conns]
13
+ new_size = EM.set_descriptor_table_size( options[:max_conns].to_i )
14
+ STDERR.puts "New descriptor-table size is #{new_size}"
15
+ EM.epoll
16
+ EM.kqueue
17
+ thin.maximum_connections = options[:max_conns] # Need to do this manually because it gets overriden on thin/server.rb:123
18
+ end
13
19
 
14
- STDERR.puts "Starting server at #{host}:#{port}"
15
- Thin::Server.start(host, port, Jugglite::App.new)
20
+ thin.start!
data/lib/jugglite/app.rb CHANGED
@@ -16,7 +16,6 @@ module Jugglite
16
16
  path: '/stream',
17
17
  keepalive_timeout: 20
18
18
  }.merge(options)
19
- STDERR.puts "Registered Jugglite to listen to #{@options[:path]}"
20
19
  @subscription_map = {}
21
20
  EventMachine::next_tick { setup_redis }
22
21
  EventMachine::next_tick { setup_keepalive }
@@ -26,6 +25,7 @@ module Jugglite
26
25
  if @app.nil? || (env["PATH_INFO"] == @options[:path])
27
26
  handle_stream(env)
28
27
  else
28
+ # Running as middleware and path did not match so pass it along
29
29
  @app.call(env)
30
30
  end
31
31
  end
@@ -10,7 +10,7 @@ module Jugglite
10
10
  @queue.push(body)
11
11
  end
12
12
 
13
- def each &blk
13
+ def each(&blk)
14
14
  @body_callback = blk
15
15
  processor = proc { |item|
16
16
  @body_callback.call(item)
@@ -1,3 +1,3 @@
1
1
  module Jugglite
2
- VERSION = "0.0.1.alpha"
2
+ VERSION = "0.0.2.alpha"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jugglite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.alpha
4
+ version: 0.0.2.alpha
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-15 00:00:00.000000000 Z
12
+ date: 2012-12-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thin
@@ -85,6 +85,8 @@ extra_rdoc_files: []
85
85
  files:
86
86
  - .gitignore
87
87
  - .rspec
88
+ - .travis.yml
89
+ - CHANGELOG
88
90
  - Gemfile
89
91
  - LICENSE.txt
90
92
  - README.md