How do you spawn an EventMachine "inside" a Rails app?
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开发者_如何学JAVA 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.
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.
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.
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.
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
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
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
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
- deploy with TorqueBox (which comes bundled with HornetQ), http://torquebox.org/news/2011/08/15/websockets-stomp-and-torquebox/, impressive and enterprisey but not really elegant unless you're coming from a Java background
- newer versions of Faye should work with JRuby, Faye in jruby on rails
- note for the future, keep an eye on the celluloid community, some interesting distributed solutions are coming from there https://github.com/celluloid/reel/wiki/WebSockets, https://github.com/celluloid/dcell
- ?
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
).
精彩评论