【问题标题】:Rails + XMPP bot in backgroundRails + XMPP 机器人在后台
【发布时间】:2013-01-08 09:24:17
【问题描述】:

我正在构建一个服务,它基本上允许用户与机器人聊天,然后机器人对用户发送的聊天进行一些奇怪的处理,并最终回复一些有意义的数据。基本上类似于 Aardvark 使用 (?) 的工作方式。

我的 bot 正在工作并正在收听,而且我有一个单独的 rails 应用程序,它将完成所有其他繁重的工作。这两个部分单独工作都很好,现在我被困在两者的接口上。我的想法是通过 Resque 将机器人(基本上是一个小的 ruby​​ 脚本)与 rails 应用程序接口 - 任何进入的东西都会进入队列,被拾取,然后将结果再次推回队列,然后脚本会回复结果。

这个接口怎么建立我不是很清楚:

  1. 我是否需要编写 rake 任务来启动/停止/重新加载机器人
  2. 如果我在不使用 rake 的情况下运行它(假设是一个由 Monit 监控的独立进程),那么我如何与 Resque 交互或访问我的 rails 模型?

我知道这些可能是非常琐碎的问题,但我很难理解哪个效果更好,以及如何进行设置。

【问题讨论】:

  • 机器人的启动/停止是一个单独的问题。 Monit 是一种选择,像工头 (github.com/ddollar/foreman) 这样的工具也是如此。对我来说真正的问题是你是否需要机器人异步或同步地处理你的 ruby​​ 应用程序。如果您可以使用同步接口,您的机器人只需对 rails 应用程序进行 HTTP 调用,生活就很简单了。 :)

标签: ruby ruby-on-rails-3 xmpp resque xmpp4r


【解决方案1】:

您的 Rails 应用程序和此机器人守护程序之间可以通过三种方式进行通信:

  1. 通过作为 HTTP 请求调用 Rails 应用(从 Rails 应用推送/拉取数据)
  2. 通过直接与 Rails 应用使用的数据库(可能是 Mysql/Postgres)进行交互
  3. 通过与 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 套接字。它们都有不同的优缺点,所以这取决于您和您的需求。

    交互可能如下所示:

    1. 机器人启动。
    2. Bot 开始侦听 IPC 消息。
    3. Bot 收到来自发件人的查询(通过 XMPP)。
    4. 机器人通过 Resque 对作业进行排队。
    5. Job 通过 HTTP 调用 Rails 应用程序。
    6. Rails 应用完成了它的工作。
    7. 某人或某物解决了任何查询并通过 Rails 应用程序输入结果。
    8. Rails 应用使用某种 IPC 方法将结果发送到机器人。
    9. 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)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-05-19
        • 2011-08-22
        • 1970-01-01
        • 2011-07-23
        • 1970-01-01
        • 1970-01-01
        • 2011-07-12
        相关资源
        最近更新 更多