【问题标题】:Rails + XMPP bot in backgroundRails + XMPP 机器人在后台
【发布时间】:2013-01-08 09:24:17
【问题描述】:
我正在构建一个服务,它基本上允许用户与机器人聊天,然后机器人对用户发送的聊天进行一些奇怪的处理,并最终回复一些有意义的数据。基本上类似于 Aardvark 使用 (?) 的工作方式。
我的 bot 正在工作并正在收听,而且我有一个单独的 rails 应用程序,它将完成所有其他繁重的工作。这两个部分单独工作都很好,现在我被困在两者的接口上。我的想法是通过 Resque 将机器人(基本上是一个小的 ruby 脚本)与 rails 应用程序接口 - 任何进入的东西都会进入队列,被拾取,然后将结果再次推回队列,然后脚本会回复结果。
这个接口怎么建立我不是很清楚:
- 我是否需要编写 rake 任务来启动/停止/重新加载机器人
- 如果我在不使用 rake 的情况下运行它(假设是一个由 Monit 监控的独立进程),那么我如何与 Resque 交互或访问我的 rails 模型?
我知道这些可能是非常琐碎的问题,但我很难理解哪个效果更好,以及如何进行设置。
【问题讨论】:
-
机器人的启动/停止是一个单独的问题。 Monit 是一种选择,像工头 (github.com/ddollar/foreman) 这样的工具也是如此。对我来说真正的问题是你是否需要机器人异步或同步地处理你的 ruby 应用程序。如果您可以使用同步接口,您的机器人只需对 rails 应用程序进行 HTTP 调用,生活就很简单了。 :)
标签:
ruby
ruby-on-rails-3
xmpp
resque
xmpp4r
【解决方案1】:
您的 Rails 应用程序和此机器人守护程序之间可以通过三种方式进行通信:
- 通过作为 HTTP 请求调用 Rails 应用(从 Rails 应用推送/拉取数据)
- 通过直接与 Rails 应用使用的数据库(可能是 Mysql/Postgres)进行交互
- 通过与 Redis 数据库支持的 Resque 工作队列系统交互
当您将 Resque 作业排入队列并将其从各种作业队列中拉出时,您只是通过 API 读取/写入共享的 Redis 数据库。机器人和 Rails 应用都通过网络与 Redis DB 通信。
我建议直接将机器人作为 ruby 进程或由 monit 管理的 rake 任务运行。听起来您已经知道如何执行此操作了。
【解决方案2】:
我认为这里的主要问题是您需要另一种消息传递解决方案(类似 IPC,而不是 IM),而不是试图弯曲“只是”一个队列的 Resque。一些选项是amqp gem(AMQP 协议)或zmq gem(ZeroMQ 协议),但您也可以通过 Ruby 标准库 Socket 类 (good examples) 使用普通的旧 UNIX 套接字。它们都有不同的优缺点,所以这取决于您和您的需求。
交互可能如下所示:
- 机器人启动。
- Bot 开始侦听 IPC 消息。
- Bot 收到来自发件人的查询(通过 XMPP)。
- 机器人通过 Resque 对作业进行排队。
- Job 通过 HTTP 调用 Rails 应用程序。
- Rails 应用完成了它的工作。
- 某人或某物解决了任何查询并通过 Rails 应用程序输入结果。
- Rails 应用使用某种 IPC 方法将结果发送到机器人。
- Bot 将结果发送给原始发件人(通过 XMPP)。
可能会像往常一样进行一些更改。例如,我认为你根本不需要 Resque。机器人可以简单地将请求立即传递给 Rails 应用程序。但是,这取决于负载、您想要实现的响应时间、您当前的架构等。也许 Resque 作业可以等待 Rails 应用程序返回结果,然后该作业(不是 Rails 应用程序)将使用 IPC。还有其他变体……
我是否需要编写一个 rake 任务来启动/停止/重新加载机器人
不,你没有。如何以及何时运行它取决于您。毕竟,Rake 可以被视为将多个 Ruby 脚本放在一起并在它们之间创建依赖关系的便捷方式。如果您认为除了运行机器人之外还有其他任务(一些清理、部署等),那么为了方便起见,使用 Rake 会很好。如果您还没有,请将机器人的逻辑重构为类并使用 Rake 任务对其进行初始化。但是,如果您离开它也可以,并且按原样运行您的脚本(使用 monit、您的自定义 init.d 脚本、ad-hoc 等)。
如果我在没有 rake 的情况下运行它(假设是一个由 Monit 监控的独立进程),那么我如何与 Resque 交互或访问我的 rails 模型?
Rake 对此没有任何影响。从操作系统的角度来看,如果您通过 Rake 运行 Resque 并通过 Rake 运行您的机器人或作为独立脚本运行,这并不重要。无论如何,它们将是不同的过程。另外,请记住,Resque 需要 Redis 在某个地方运行。
我知道这些可能是非常琐碎的问题
完全没有。我认为需要一些时间才能将诸如此类的问题视为微不足道的问题。
【解决方案3】:
您可以将您的代码放在初始化程序上运行,并且可以完全访问您的所有 Rails 模型或库。
这样,您无需在机器人和 Rails 应用程序之间进行“通信”,因为机器人在 Rails 应用程序中。
样板代码如下:
config/initializers/background_app_tasks.rb
class BackgroundWorker
#-------------------------------
def initialize(operation='normal')
@exit = false
@lock = Mutex.new # For thread safety
@thread = nil
say "Starting in '#{operation}' mode..."
case operation
when 'normal'
@thread = Thread.new() { loopme }
when 'cleanup'
@thread = Thread.new() { cleanup }
when 'nothing'
#startup without threads
end
@thread.run if @thread
end
#-------------------------------
def exit!
begin
return if @exit # #stop?
say "Exiting #{}, waiting for mutex..."
@lock.synchronize {
say "exiting thread #{@thread.to_s || '<sem nome>' }..."
@exit = true # #stop
}
rescue Exception => e
exceptme(e)
end
end
#-------------------------------
def loopme
at_exit { exit! }
i=0; ok=false;
nap = 30
while true do
begin
break if @exit
i+=1
#lock mutex for processing...
@lock.synchronize {
#.... do some work ....
}
rescue StandardError => e
#....
end
sleep(nap)
end
end
end #class
# ------ M A I N --------
Thread.abort_on_exception=false
e = BackgroundWorker.new(OPERATION)