Reputation: 25649
I've got a Rails application, and am looking to add some sort of WebSocket support to it. From various googling, it appears that the best Ruby based WebSocket solution is em-websocket running on EventMachine.
I was wondering if there was a way to "integrate" an EventMachine reactor into Rails? Where do I put the initialization code? Is this the proper way to accomplish this?
I've seen this example that falls back on Sinatra to do an EventMachine GET
request, but that isn't quite what I'm looking for.
Any help is appreciated.
Upvotes: 21
Views: 8855
Reputation: 5333
I had same problem and found solution. First, put your code in lib
dir (for example /lib/listener/init.rb
) and create one class method that run EM, for example Listener.run
.
#!/usr/bin/env ruby
require File.expand_path('../../config/environment', File.dirname(__FILE__))
class Listener
def self.run
# your code here
# you can access your models too
end
end
After that I used dante gem. Create /init/listener
file. The code may be like that:
#!/usr/bin/env ruby
require File.expand_path('../../lib/listener/init.rb', __FILE__)
log_file = File.expand_path('../../log/listener.stdout.log', __FILE__)
pid_file = File.expand_path('../../tmp/listener.pid', __FILE__)
listener = Dante::Runner.new('listener')
if ARGV[0] === 'start'
listener.execute(daemonize: true,
pid_path: pid_file,
log_path: log_file) { Listener.run }
elsif ARGV[0] === 'restart'
listener.execute(daemonize: true,
restart: true,
pid_path: pid_file,
log_path: log_file) { Listener.run }
elsif ARGV[0] === 'stop'
listener.execute(kill: true, pid_path: pid_file)
end
Now you can run you code like that: ./bin/listener start
, ./bin/listener restart
, ./bin/listener stop
You can use god for monitoring your listener is running. But make sure you're using same pid file (/tmp/listener.pid
).
Upvotes: 0
Reputation: 19713
Don't know if this is what you are after. But if you would like to do provide some kind of socket-messaging system.
Have a look at Faye. It provides message servers for Node.js and Rack. There is also a rails cast for this by Ryan Bates which should simplify the implementation.
Hope that helps.
Upvotes: 5
Reputation: 7301
You probably shouldn't use EM any more if you can help it, it seems to no longer be maintained - if you encounter a bug - you're on your own.
Most of the answers above don't work in JRuby due to https://github.com/eventmachine/eventmachine/issues/479 - namely the pattern:
Thread.new{ EM.run }
used by EM::Synchrony
and various answers found around the internet (such as EventMachine and Ruby Threads - what's really going on here?) are broken under JRuby eventmachine implementation (fibers are threads in jruby and there's currently no roadmap on when this will change).
JRuby messaging options would be
Upvotes: 0
Reputation: 1683
I'd consider looking into Cramp. It's an async framework built on top of EventMachine, and it supports Thin server:
Rack Middlewares support + Rainbows! and Thin web servers
Upvotes: 2
Reputation: 5494
I spent a considerable amount of time looking into this. EventMachine need to run as a thread in your rails install (unless you are using Thin,) and there are some special considerations for Passenger. I wrote our implementation up here: http://www.hiringthing.com/2011/11/04/eventmachine-with-rails.html
UPDATE
We pulled this configuration out into a gem called Momentarily. Source is here https://github.com/eatenbyagrue/momentarily
Upvotes: 4
Reputation: 61
If you run rails application in a thin server (bundle exec thin start) thin server run EventMachine for you and then your rails application can execute EM code wherever you need.
By example:
A library o initializer with that code:
EM.next_tick do
EM.add_periodic_timer(20) do
puts 'from Event Machine in rails code'
end
end
not blocks rails processes application.
Upvotes: 6
Reputation: 17958
I'd try using em-synchrony to start a reactor in a fiber. In a rails app you can probably start it in an initializer since it sounds like you just want to leave the reactor running to respond to websocket requests. As suggested by the other answers I think you want to either setup socket communication with your reactor or use one of the asynchronous clients to a data store which both your reactor and rails code can read from and write to to exchange data.
Some of my coworkers put together some examples of starting EM reactors on demand in ruby code to run their tests within EventMachine. I'd try using that as a possible example; raking and testing with eventmachine
Upvotes: 2
Reputation: 211540
You cannot run the Eventmachine engine inside of Rails itself as it is a persistent run loop that would block one of your Rails processes permanently. What is usually done is there's a side-process that uses Eventmachine and Rails communicates with it through sockets to send notifications.
Juggernaut serves as an example of this kind of thing where it implements a Websocket client and a Rails hook to send notifications to it. The project has since deprecated the Ruby version in favor of a JavaScript Node.js version but this still serves as a very thorough example of how Eventmachine can be used.
Upvotes: 9