2013-01-24 36 views
5

Tôi đang xây dựng một dịch vụ cơ bản cho phép người dùng trò chuyện với bot và bot thực hiện xử lý lạ với cuộc trò chuyện do người dùng gửi và cuối cùng trả lời bằng một số dữ liệu có ý nghĩa. Về cơ bản một cái gì đó tương tự như cách Aardvark sử dụng (?) Để làm việc.Rails + XMPP bot ở chế độ nền

Tôi đã làm việc với bot và đang nghe ngay bây giờ, và tôi có một ứng dụng đường ray riêng biệt sẽ thực hiện tất cả các công việc nặng nhọc khác. Cả hai phần này đều hoạt động tốt, và bây giờ tôi đang bị mắc kẹt khi giao tiếp với hai phần. Ý tưởng của tôi là giao diện bot (về cơ bản là một tập lệnh ruby ​​nhỏ) với ứng dụng đường ray qua Resque - mọi thứ đi vào hàng đợi, được chọn và kết quả sau đó được đẩy trở lại hàng đợi và sau đó kịch bản sẽ trả lời lại với kết quả.

Tôi không phải là rất rõ ràng như thế nào để thiết lập giao diện này:

  1. Tôi có cần phải viết một nhiệm vụ cào để start/stop/reload bot
  2. Nếu tôi chạy nó mà không cần cào (được cho là một quá trình độc lập được theo dõi bởi Monit) sau đó làm cách nào để giao tiếp với Resque hoặc truy cập các mô hình đường ray của tôi?

Tôi biết đây có thể là những câu hỏi rất tầm thường, nhưng tôi đang gặp khó khăn trong việc hiểu điều gì hoạt động tốt hơn và cách thiết lập.

+0

Các khởi đầu/dừng của bot là một mối quan tâm riêng biệt. Monit là một lựa chọn cho điều đó, như là một công cụ như quản đốc (https://github.com/ddollar/foreman). Câu hỏi thực sự đối với tôi là liệu bạn có cần bot làm việc với ứng dụng ruby ​​của bạn một cách không đồng bộ hoặc đồng bộ. Nếu bạn có thể thoát khỏi giao diện đồng bộ hóa, bot của bạn chỉ có thể thực hiện cuộc gọi HTTP đến ứng dụng đường ray và cuộc sống thật đơn giản. :) –

Trả lời

4

Có ba cách để giao tiếp giữa ứng dụng Rails và daemon bot này:

  1. Bằng cách gọi ứng dụng Rails là một yêu cầu HTTP (đẩy/kéo dữ liệu từ ứng dụng Rails)
  2. Bằng cách tương tác trực tiếp với một cơ sở dữ liệu Rails sử dụng ứng dụng (khả năng Mysql/Postgres)
  3. bằng cách tương tác với một hệ thống hàng đợi công việc Resque, được hỗ trợ bởi cơ sở dữ liệu Redis

Khi bạn đang enqueuing và kéo Resq ue công việc ra hàng đợi công việc khác nhau, bạn chỉ cần đọc/ghi vào cơ sở dữ liệu Redis được chia sẻ thông qua một API. Cả bot và ứng dụng Rails đều nói chuyện với Redis DB qua mạng.

Tôi khuyên bạn nên chạy bot trực tiếp dưới dạng quy trình ruby ​​hoặc công việc cào được quản lý bởi monit. Có vẻ như bạn đã biết cách làm điều này.

2

Tôi nghĩ rằng các vấn đề chính ở đây là bạn cần một giải pháp khác để nhắn tin (IPC giống, chứ không phải IM) thay vì cố gắng bẻ cong Resque là "chỉ" một hàng đợi. Một số tùy chọn là amqp gem (giao thức AMQP) hoặc zmq gem (giao thức ZeroMQ), nhưng bạn cũng có thể sử dụng ổ cắm UNIX cũ thông qua thư viện chuẩn của Ruby Lớp ổ cắm (good examples). Tất cả đều có ưu và khuyết điểm khác nhau, vì vậy nó có thể tùy thuộc vào bạn.

Sau đó, sự tương tác có thể trông giống như một cái gì đó như thế này:

  1. Bot bắt đầu.
  2. Bot bắt đầu nghe tin nhắn IPC.
  3. Bot nhận được một truy vấn từ người gửi (thông qua XMPP).
  4. Bot xếp hàng qua công việc Resque.
  5. Công việc gọi ứng dụng Rails qua HTTP.
  6. Ứng dụng Rails thực hiện chia sẻ công việc của mình.
  7. Ai đó hoặc một cái gì đó giải quyết bất cứ điều gì là truy vấn và nhập một kết quả thông qua ứng dụng Rails.
  8. Ứng dụng Rails gửi kết quả bằng cách sử dụng một số phương pháp IPC cho bot.
  9. Bot gửi kết quả đến người gửi ban đầu (qua XMPP).

Có thể có một số thay đổi như bình thường. Ví dụ, tôi nghĩ bạn không cần Resque. Bot có thể chuyển yêu cầu ngay lập tức tới ứng dụng Rails nhưng nó phụ thuộc vào tải, thời gian trả lời bạn muốn đạt được, kiến ​​trúc hiện tại, v.v. Có thể công việc Resque có thể đợi ứng dụng Rails trả lại kết quả và sau đó là công việc (không phải ứng dụng Rails) sẽ sử dụng IPC. Có các biến thể khác ...

Tôi có cần phải viết một nhiệm vụ cào để start/stop/reload bot

Không, bạn không. Đó là vào bạn như thế nào và khi bạn chạy nó. Sau khi tất cả, Rake có thể được xem như là một cách thuận tiện để đặt nhiều kịch bản Ruby với nhau và tạo ra sự phụ thuộc giữa chúng. Nếu bạn nghĩ rằng sẽ có một số nhiệm vụ khác xung quanh bot hơn là chỉ chạy nó (một số dọn dẹp, triển khai, vv), nó sẽ là tốt để sử dụng Rake cho thuận tiện. Nếu chưa, logic của refactor bot đến lớp và sử dụng nhiệm vụ Rake để khởi tạo nó. Nhưng nó có lẽ sẽ tốt nếu bạn rời khỏi nó và chỉ chạy kịch bản của bạn như là (sử dụng monit, script init.d tùy chỉnh của bạn, ad-hoc, vv).

Nếu tôi chạy nó mà không có cào (được cho là quá trình độc lập được Monit giám sát) thì làm cách nào để giao tiếp với Resque hoặc truy cập mô hình đường ray?

Rake không ảnh hưởng đến điều này. Từ quan điểm hệ điều hành, nếu bạn chạy Resque thông qua Rake và bot của bạn thông qua Rake hoặc như là một kịch bản độc lập, nó không quan trọng, họ sẽ được các quá trình khác nhau anyway. Cũng nên nhớ rằng Resque cần Redis để chạy ở đâu đó.

Tôi biết những câu hỏi có thể là rất tầm thường

Không gì cả. Tôi nghĩ rằng nó sẽ mất một thời gian trước khi các vấn đề như có thể được coi là tầm thường.

0

Bạn có thể đặt mã để chạy trên trình khởi tạo và có toàn quyền truy cập tất cả các mô hình hoặc lib của Rails của bạn.

Bằng cách này, bạn không cần phải "giao tiếp" giữa bạn bot và ứng dụng Rails của bạn, bởi vì bot của bạn nằm trong ứng dụng Rails của bạn.

đang Boilerplate sẽ như thế nào:

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) 
Các vấn đề liên quan