Rails + XMPP机器人在背景中

时间:2013-01-24 15:06:13

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

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

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

我不清楚如何建立这个界面:

  1. 我是否需要编写一个rake任务来启动/停止/重新加载bot
  2. 如果我在没有rake的情况下运行它(据说是由Monit监控的独立进程),那么我该如何与Resque接口或访问我的rails模型?
  3. 我知道这些可能是非常微不足道的问题,但我很难理解哪个更好,以及如何进行设置。

3 个答案:

答案 0 :(得分:4)

您的Rails应用程序和此僵尸程序守护程序之间有三种通信方式:

  1. 通过调用Rails应用程序作为HTTP请求(从Rails应用程序推送/提取数据)
  2. 通过直接与数据库交互,Rails应用程序使用(可能是Mysql / Postgres)
  3. 通过与Redis数据库支持的Resque工作队列系统进行交互
  4. 当您将Resque作业从各种作业队列中排除并拔出时,您只需通过API读取/写入共享的Redis数据库。 bot和Rails应用程序都通过网络与Redis DB进行通信。

    我建议直接运行僵尸程序作为由monit管理的ruby进程或rake任务。听起来你已经知道如何做到这一点。

答案 1 :(得分:2)

我认为这里的主要问题是你需要另一种消息传递解决方案(类似于IPC,而不是IM),而不是试图弯曲“只是”队列的Resque。一些选项是amqp gem(AMQP协议)或zmq gem(ZeroMQ协议),但您也可以通过Ruby标准库Socket类(good examples)使用普通的旧UNIX套接字。它们都有不同的优点和缺点,所以它可能取决于你。

然后,交互可能看起来像这样:

  1. Bot开始。
  2. Bot开始收听IPC消息。
  3. Bot收到发件人的查询(通过XMPP)。
  4. Bot通过Resque排队工作。
  5. Job通过HTTP调用Rails应用程序。
  6. Rails应用程序可以分担工作。
  7. 某人解决了查询,并通过Rails应用程序输入结果。
  8. Rails应用程序使用某种IPC方法将结果发送到bot。
  9. Bot将结果发送给原始发件人(通过XMPP)。
  10. 可以像往常一样进行一些更改。例如,我认为你根本不需要Resque。机器人可以简单地将请求立即传递给Rails应用程序,但它取决于负载,您想要实现的响应时间,当前架构等。也许Resque作业可以等待Rails应用程序返回结果然后工作(不是Rails应用程序)会使用IPC。还有其他变化...

      

    我是否需要编写一个rake任务来启动/停止/重新加载bot

    不,你没有。这取决于你如何以及何时运行它。毕竟,Rake可以被视为将多个Ruby脚本放在一起并在它们之间创建依赖关系的便捷方式。如果你认为机器人周围会有一些其他任务而不仅仅是运行它(一些清理,部署等),为方便起见,使用Rake会很好。如果还没有,重构机器人的逻辑,并使用Rake任务来初始化它。但是如果你离开它并且只是按原样运行你的脚本(使用monit,你的自定义init.d脚本,ad-hoc等)可能会没问题。

      

    如果我在没有rake的情况下运行它(据说是由Monit监控的独立进程),那么我该如何与Resque接口或访问我的rails模型?

    耙子对此没有影响。从操作系统的角度来看,如果您通过Rake运行Resque并通过Rake运行bot或作为独立脚本运行,则无关紧要,无论如何它们都将是不同的进程。还要记住,Resque需要Redis才能在某个地方运行。

      

    我知道这些可能是非常微不足道的问题

    一点也不。我认为这样的问题需要一段时间才能被认为是微不足道的。

答案 2 :(得分:0)

您可以将代码放在初始化程序上,并拥有对所有Rails模型或库的完全访问权限。

这样,您无需在机器人和Rails应用程序之间进行“通信”,因为您的机器人位于Rails应用程序中。

Boilerplate代码如下:

配置/初始化/ 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)