我有一个 Rails 应用程序,并希望为其添加某种 WebSocket 支持。从各种谷歌搜索来看,基于 Ruby 的最佳 WebSocket 解决方案似乎是在 EventMachine 上运行的em-websocket 。
我想知道是否有办法将 EventMachine 反应器“集成”到 Rails 中?我在哪里放置初始化代码?这是完成此任务的正确方法吗?
我见过这个例子,它依靠 Sinatra 来执行 EventMachineGET
请求,但这并不是我想要的。
任何帮助表示赞赏。
我有一个 Rails 应用程序,并希望为其添加某种 WebSocket 支持。从各种谷歌搜索来看,基于 Ruby 的最佳 WebSocket 解决方案似乎是在 EventMachine 上运行的em-websocket 。
我想知道是否有办法将 EventMachine 反应器“集成”到 Rails 中?我在哪里放置初始化代码?这是完成此任务的正确方法吗?
我见过这个例子,它依靠 Sinatra 来执行 EventMachineGET
请求,但这并不是我想要的。
任何帮助表示赞赏。
你不能在 Rails 内部运行 Eventmachine 引擎,因为它是一个持久的运行循环,会永久阻塞你的 Rails 进程之一。通常所做的是有一个使用 Eventmachine 的辅助进程,Rails 通过套接字与其通信以发送通知。
Juggernaut就是这种事情的一个例子,它实现了一个 Websocket 客户端和一个 Rails 钩子来向它发送通知。该项目已经弃用了 Ruby 版本,转而支持 JavaScript Node.js 版本,但这仍然是一个非常详尽的例子,说明了如何使用 Eventmachine。
如果您在瘦服务器(bundle exec thin start)中运行 rails 应用程序,则瘦服务器会为您运行 EventMachine,然后您的 rails 应用程序可以在您需要的任何地方执行 EM 代码。
举例:
具有该代码的库 o 初始化程序:
EM.next_tick do
EM.add_periodic_timer(20) do
puts 'from Event Machine in rails code'
end
end
不阻止 rails 进程应用程序。
我花了相当多的时间来研究这个。EventMachine 需要在您的 rails 安装中作为线程运行(除非您使用的是 Thin),并且对于 Passenger 有一些特殊的注意事项。我在这里写了我们的实现:http ://www.hiringthing.com/2011/11/04/eventmachine-with-rails.html
更新
我们将此配置提取到一个名为 Momentarily 的 gem 中。来源在这里https://github.com/eatenbyagrue/momentarily
我会尝试使用em-synchrony在光纤中启动反应器。在 Rails 应用程序中,您可能可以在初始化程序中启动它,因为听起来您只想让反应器运行以响应 websocket 请求。正如其他答案所建议的那样,我认为您想要设置与反应堆的套接字通信,或者使用异步客户端之一到数据存储,您的反应堆和 Rails 代码都可以读取和写入以交换数据。
我的一些同事整理了一些在 ruby 代码中按需启动 EM 反应器的示例,以在 EventMachine 中运行他们的测试。我会尝试使用它作为一个可能的例子;使用 eventmachine 进行搜索和测试
我会考虑调查抽筋。它是一个建立在 EventMachine 之上的异步框架,它支持瘦服务器:
机架中间件支持 + 彩虹!和瘦 Web 服务器
如果你能提供帮助,你可能不应该再使用 EM,它似乎不再被维护——如果你遇到错误——你只能靠自己了。
由于https://github.com/eventmachine/eventmachine/issues/479,上述大多数答案在 JRuby 中不起作用- 即模式:
Thread.new{ EM.run }
在互联网上找到的EM::Synchrony
各种答案(例如EventMachine 和 Ruby 线程 - 这里到底发生了什么?)在 JRuby 事件机器实现下被破坏(纤维是 jruby 中的线程,目前没有关于何时改变的路线图)。
JRuby 消息传递选项将是
我有同样的问题并找到了解决方案。首先,将您的代码放在lib
dir 中(例如/lib/listener/init.rb
)并创建一个运行 EM 的类方法,例如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
之后我使用了但丁宝石。创建/init/listener
文件。代码可能是这样的:
#!/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
现在你可以像这样运行你的代码:./bin/listener start
, ./bin/listener restart
,./bin/listener stop
你可以使用god来监控你的监听器正在运行。但请确保您使用的是相同的 pid 文件 ( /tmp/listener.pid
)。