Rails+XMPP机器人在后台



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

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

我不太清楚如何建立这个接口:

  1. 我需要写一个rake任务来启动/停止/重新加载机器人吗
  2. 如果我在没有rake的情况下运行它(据说是由Monit监控的独立进程(,那么我如何与Resque接口或访问我的rails模型

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

Rails应用程序和这个机器人守护进程之间有三种通信方式:

  1. 通过将Rails应用程序作为HTTP请求调用(从Rails应用程序推送/提取数据(
  2. 通过与Rails应用程序使用的数据库直接交互(可能是Mysql/Postgres(
  3. 通过与Redis数据库支持的Resque工作队列系统交互

当您将Resque作业排入队列并从各种作业队列中取出时,您只是通过API读取/写入共享Redis数据库。机器人和Rails应用程序都通过网络与Redis DB进行对话。

我建议将bot直接作为由monit管理的ruby进程或rake任务运行。听起来你已经知道怎么做了。

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

交互可能看起来像这样:

  1. Bot启动
  2. Bot开始侦听IPC消息
  3. Bot接收来自发送方的查询(通过XMPP(
  4. Bot通过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在某个地方运行。

我知道这些可能是非常琐碎的问题

没有。我认为像这样的问题还需要一段时间才能被认为是微不足道的。

您可以将代码放在初始化器上运行,并可以完全访问所有Rails模型或库。

这样,你就不需要在你的机器人和你的Rails应用程序之间"通信",因为你的机器人就在你的Rails App中。

Boilerplate代码如下:

config/ininitializers/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)

相关内容

  • 没有找到相关文章

最新更新