简体   繁体   English

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

[英]Execute a stored procedure asynchronously in a Rails application

I have an application in Rails and what I'm trying to do is execute a database stored procedure asynchronously when a user navigates from one particular page to another - the user must continue his navigation while the stored procedure runs. 我在Rails中有一个应用程序,我要做的是当用户从一个特定页面导航到另一个页面时异步执行数据库存储过程-用户必须在存储过程运行时继续其导航。

I don't really need to have a callback when the procedure finishes, I just need to run it in background. 过程完成时,我真的不需要回调,只需要在后台运行即可。

I'm trying to use the following code: 我正在尝试使用以下代码:

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

The problem here is that when the stored procedure starts, the user must be redirected to another page, but the next page remains "pending" and renders only when the procedure finishes. 这里的问题是,存储过程启动时,必须将用户重定向到另一页,但是下一页保持“挂起”状态,并且仅在该过程完成时才呈现。

I tried to use thin (in my development environment) as my server with --threaded option with no success, and now I'm thinking about using Phusion Passenger Enterprise in multithreaded mode in the production server, but it's a commercial version and it does not have any trials, I'm afraid that it's not what I need. 我尝试在我的开发环境中使用带有--threaded选项的服务器作为服务器,但没有成功,现在我正在考虑在生产服务器中以多线程模式使用Phusion Passenger Enterprise,但这是一个商业版本,它确实没有任何试验,我担心这不是我所需要的。

Does anybody know a good solution to achieve this? 有人知道实现此目标的好方法吗? To execute the stored procedure I have to make a request to the same webserver my application is running, so my webserver must accept multiple connections at a time (multithreaded), is it correct? 要执行存储过程,我必须向运行应用程序的同一台Web服务器发出请求,因此我的Web服务器必须一次接受多个连接(多线程),对吗?

Some useful information: 一些有用的信息:

  • Ruby 1.9.3p385 红宝石1.9.3p385
  • Rails 3.2.13 导轨3.2.13
  • SQL Server 2012 SQL Server 2012

Development: 发展:

  • Linux lucid32 2.6.32-45-generic #102-Ubuntu (vagrant machine) Linux lucid32 2.6.32-45-通用#102-Ubuntu(无用的机器)
  • thin webserver 瘦网络服务器

Production: 生产:

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

I really appreciate any help. 我非常感谢您的帮助。

UPDATE #1 更新1

I tried celluloid as recommended by Jesse. 我尝试了杰西推荐的赛璐oid。 Here is my code: 这是我的代码:

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

When then action 'create' is called, the record is created, the stored procedure starts but the next page is not rendered, the request remains "pending" in the browser until the procedure finishes. 然后调用操作“创建”时,将创建记录,存储过程将启动,但不会呈现下一页,该请求在浏览器中保持“待处理”状态,直到过程完成为止。 As soon as the procedure finishes, the page is rendered. 该过程完成后,将立即呈现该页面。

I can't figure out what is going on. 我不知道发生了什么事。 Was the procedure not supposed to run in background? 该程序不应该在后台运行吗?

In something like this, I recommend either Sidekiq or Celluloid . 在这种情况下,我建议使用SidekiqCelluloid What you want to do is spin off a thread and execute something, returning access to the calling process and having it continue on. 您要做的是剥离线程并执行某些操作,返回对调用过程的访问权,然后继续执行该过程。

Sidekiq would require a separate process to run (and Redis), Celluloid would not. Sidekiq将需要一个单独的进程来运行(和Redis),Celluloid则不需要。 Otherwise, they are similar. 否则,它们是相似的。

Sidekiq: Sidekiq:

class AsyncProc
  include Sidekiq::Worker

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

You'd call it with: 您可以通过以下方式调用它:

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

This would schedule a job in Redis and get executed when a spare Sidekiq worker has time 这将在Redis中安排作业,并在有备用Sidekiq工人有时间时执行

Celluloid: 赛璐珞:

require 'celluloid/autostart'
class AsyncProc
  include Celluloid

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

And to call it: 并称之为:

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

This would execute asynchronously, pretty much right away. 这将异步执行,几乎立即执行。

If you want to run something asynchronous from the request/response thread, which is what this sounds like, then you should be using a background processing system to do this. 如果您想从请求/响应线程运行异步的东西,这听起来像是这样,那么您应该使用后台处理系统来执行此操作。

There are various gems that do this - look into DelayedJob , Resque , and Sidekiq as some of the more popular options. 有很多宝石可以做到这一点-将DelayedJobResqueSidekiq作为更受欢迎的选择。 They will typically require some backing store (Redis, MongoDB, your Rails database) to keep track of tasks that need to be, or are currently, running. 他们通常需要一些后备存储(Redis,MongoDB,您的Rails数据库)来跟踪需要或正在运行的任务。

Alternatively, Unicorn might work for you as it actually spawns separate processes that won't lock the GVL - but I don't think the underlying system should be determined by the kind of concern you are addressing. 另外,Unicorn可能会为您工作,因为它实际上会生成不会锁定GVL的单独进程-但我认为底层系统不应由您要解决的问题类型决定。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM