在Rails应用程序中异步执行存储过程

时间:2013-05-22 14:13:15

标签: ruby-on-rails sidekiq

我在Rails中有一个应用程序,我尝试做的是当用户从一个特定页面导航到另一个页面时异步执行数据库存储过程 - 用户必须在存储过程运行时继续导航。 / p>

程序完成后我真的不需要回调,我只需要在后台运行它。

我试图使用以下代码:

require 'eventmachine'
require 'em-http'
require 'fiber'

def async_fetch(url)
  f = Fiber.current
  http = EventMachine::HttpRequest.new(url).get :timeout => 10
  http.callback { f.resume(http) }
  http.errback { f.resume(http) }

  return Fiber.yield
end

EventMachine.run do
  Fiber.new{
    url = url_for :controller => 'common', :action => 'execute_stored_procedure'
    data = async_fetch(url)
    EventMachine.stop
  }.resume
end

这里的问题是,当存储过程启动时,必须将用户重定向到另一个页面,但下一页仍然是" pending"并且仅在程序结束时呈现。

我尝试使用 thin (在我的开发环境中)作为我的服务器使用--threaded选项但没有成功,现在我正考虑在多线程模式下使用Phusion Passenger Enterprise生产服务器,但它是一个商业版本,它没有任何试验,我担心它不是我需要的。

有人知道实现这个目标的好方法吗?要执行存储过程,我必须向我的应用程序运行的同一个Web服务器发出请求,因此我的网络服务器必须一次接受多个连接(多线程),这是否正确?

一些有用的信息:

  • Ruby 1.9.3p385
  • Rails 3.2.13
  • SQL Server 2012

发展:

  • Linux lucid32 2.6.32-45-generic#102-Ubuntu(vagrant machine)
  • 瘦网络服务器

生产:

  • Linux debian 2.6.32-5-xen-amd64
  • Apache / Phusion Passenger

我真的很感激任何帮助。

更新#1

我按照Jesse的推荐尝试了赛璐珞。 这是我的代码:

require 'celluloid/autostart'
class PropertyWorker
  include Celluloid

  def engage(args)
    ActiveRecord::Base.execute_procedure("gaiainc.sp_ins_property_profiles", args[:id])
  end
end

...

def create
    @property = Property.new(params[:property])

    respond_to do |format|
      if @property.save
        PropertyWorker.new.async.engage({:id => @property.id})
        format.html { redirect_to new_enterprise_property_activation_url(@property.enterprise.id, @property.id) }
        format.json { render json: @property, status: :created, location: @property }
      else
        format.html { render action: "new" }
        format.json { render json: @property.errors, status: :unprocessable_entity }
      end
    end
end

然后行动'创造'调用,创建记录,存储过程启动但下一页未呈现,请求仍然是"挂起"在浏览器中,直到该过程完成。一旦过程完成,就会呈现页面。

我无法弄清楚发生了什么。该程序不应该在后台运行吗?

2 个答案:

答案 0 :(得分:3)

在这种情况下,我建议使用SidekiqCelluloid。你想要做的是分离一个线程并执行一些东西,返回对调用进程的访问并继续它。

Sidekiq需要一个单独的进程来运行(和Redis),Celluloid不会。否则,他们是相似的。

<强> Sidekiq:

class AsyncProc
  include Sidekiq::Worker

  def perform(args)
    CodeToExecuteStoredProcedure.engage! args
  end
end

你打电话给:

AsyncProc.perform_async {whatever: arguments, you: want}

这将在Redis中安排一份工作,并在备用Sidekiq工人有时间时执行

<强>赛璐珞:

require 'celluloid/autostart'
class AsyncProc
  include Celluloid

  def engage(args)
    CodeToExecuteStoredProcedure.engage! args
  end
end

并称之为:

AsyncProc.new.async.engage {whatever: arguments, you: want}

这将异步执行,几乎立即执行。

答案 1 :(得分:1)

如果你想从请求/响应线程运行异步,这听起来像是这样,那么你应该使用后台处理系统来做这件事。

有各种各样的宝石可以做到这一点 - 查看DelayedJobResqueSidekiq作为一些比较流行的选项。他们通常需要一些后备存储(Redis,MongoDB,您的Rails数据库)来跟踪需要或当前正在运行的任务。

或者,Unicorn可能会为你工作,因为它实际上产生了不会锁定GVL的单独进程 - 但我不认为底层系统应该由你正在解决的问题来决定。< / p>