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 +5 -0
- data/CHANGELOG +11 -0
- data/Gemfile +4 -0
- data/README.md +64 -6
- data/bin/jugglite +16 -11
- data/lib/jugglite/app.rb +1 -1
- data/lib/jugglite/deferrable_body.rb +1 -1
- data/lib/jugglite/version.rb +1 -1
- metadata +4 -2
data/.travis.yml
ADDED
data/CHANGELOG
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Jugglite
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](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
|
-
|
|
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
|
-
|
|
84
|
+
## Performance
|
|
27
85
|
|
|
28
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
data/lib/jugglite/version.rb
CHANGED
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.
|
|
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-
|
|
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
|