如何在Rails应用程序中生成一个"内部"的EventMachine?

21
我有一个Rails应用程序,并希望添加一些WebSocket支持。通过各种谷歌搜索,似乎最好的基于Ruby的WebSocket解决方案是在EventMachine上运行的em-websocket
我想知道是否有一种方法将EventMachine反应器集成到Rails中? 我该把初始化代码放在哪里? 这是正确的实现方式吗?
我看到了这个示例,它退回到Sinatra进行EventMachine GET请求,但这不是我要找的东西。
感谢任何形式的帮助。
8个回答

9

由于Eventmachine是一个持久性运行循环,会永久地阻塞其中一个Rails进程,因此您无法在Rails内部运行Eventmachine引擎。通常的做法是使用一个侧面进程来使用Eventmachine,而Rails通过套接字与其通信以发送通知。

Juggernaut 是这种实现的一个示例,它实现了一个Websocket客户端和一个Rails钩子来向其发送通知。该项目已经弃用了Ruby版本,转而使用JavaScript Node.js版本,但仍然是如何使用Eventmachine的非常全面的示例。


你说的“side-process”是什么意思?你是指在初始化文件中生成一个进程吗? - Daniel Viglione
@Donato 我的意思是你需要维护一个独立的进程,通常使用类似于Daemons的工具来创建后台进程,并使用像Eye这样的进程启动器来保持其运行。 - tadman
我不喜欢守护进程,因为你必须将其与Rails应用程序分开管理。我希望Passenger使用其预加载器进程在初始化程序中分叉一个新进程(使用Ruby 2的写时复制语义,从而使用共享内存空间),并且我希望该新进程由Passenger管理。我希望该进程使用空闲轮询IMAP,同时可以访问Rails环境。我想使用EventMachine,因为会有很多空闲时间,并且不想浪费内存资源在单独的线程堆栈上。 - Daniel Viglione
基本上,我只想知道如何在初始化程序中分叉这个单独的Passenger进程,并在这个新进程中运行EventMachine。 - Daniel Viglione
你喜欢的方式和你解决问题的方式似乎有些不一致。如果没有流量,Passenger可以自由地杀死你的Rails进程,这是它必须做的。这意味着在空闲时间内,你没有后台进程。你需要从Rails外部获取某些东西来保持运行状态,而最好的计划是一个独立于Passenger控制之外的进程。希望你的Passenger进程不会被杀死,或者防止Passenger这样做只会带来麻烦。 - tadman

6
如果您在thin服务器(bundle exec thin start)上运行rails应用程序,则thin服务器会为您运行EventMachine,然后您的rails应用程序可以在需要时执行EM代码。
例如:
具有以下代码的库或初始化器:
EM.next_tick do
  EM.add_periodic_timer(20) do
    puts 'from Event Machine in rails code'
  end
end

不会阻塞Rails进程应用程序。


谢谢,这个提示真的帮助了我处理基于 Rails 的轻量级应用程序! - OzBandit

5

不知道这是否符合您的需求。但如果您想提供某种套接字消息系统。

可以看看 Faye。它为Node.js和Rack提供了消息服务器。还有一个由Ryan Bates制作的rails cast,应该可以简化实现过程。

希望能帮到您。


4

2
我建议您使用em-synchrony来在Fiber中启动反应器。在Rails应用程序中,您可以在初始化器中启动它,因为您似乎只想让反应器保持运行以响应WebSocket请求。如其他答案所建议的那样,我认为您要么要设置与反应器的套接字通信,要么使用异步客户端连接到数据存储区,这两者都可以使您的反应器和Rails代码读取和写入以交换数据。
我的一些同事编写了一些示例,展示了如何在Ruby代码中按需启动EM反应器来运行他们的测试。我建议您尝试使用这个示例:raking and testing with eventmachine

2

我建议你考虑使用Cramp。它是一个基于EventMachine构建的异步框架,支持Thin服务器:

支持Rack中间件和Rainbows!以及Thin web服务器


1
遗憾的是,那个网站已经崩溃了,而且Cramp已经被放弃了。如果今天要解决这个问题,需要考虑一些事情。 - tadman

0

如果可以的话,您最好不要再使用EM,因为它似乎已经不再维护了 - 如果遇到错误 - 您将自行解决。

由于https://github.com/eventmachine/eventmachine/issues/479,上面大多数答案在JRuby中都无法正常工作 - 即模式:

Thread.new{ EM.run }

EM::Synchrony被许多互联网上的答案(例如EventMachine and Ruby Threads - what's really going on here?)所使用,但在JRuby eventmachine实现下是有问题的(在jruby中,纤维是线程,并且目前没有时间表来改变这一点)。

JRuby消息选项将会是

  1. 使用TorqueBox(内置HornetQ),http://torquebox.org/news/2011/08/15/websockets-stomp-and-torquebox/进行部署,非常令人印象深刻和企业级,但如果您来自Java背景,则不太优雅
  2. 更新版本的Faye应该可以与JRuby一起使用,Faye in jruby on rails
  3. 未来请注意Celluloid社区,那里有一些有趣的分布式解决方案https://github.com/celluloid/reel/wiki/WebSockets, https://github.com/celluloid/dcell

0
我曾经遇到过同样的问题并找到了解决方案。首先,将您的代码放在lib目录下(例如/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

之后我使用了dante gem。创建/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)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接