2

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

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

我正在尝试使用以下代码:

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

这里的问题是,当存储过程启动时,用户必须被重定向到另一个页面,但下一页仍然“待定”并且仅在过程完成时呈现。

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

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

一些有用的信息:

  • 红宝石 1.9.3p385
  • 导轨 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
  • 阿帕奇 / Phusion 乘客

我真的很感激任何帮助。

更新#1

我按照杰西的建议尝试了赛璐珞。这是我的代码:

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

然后调用“create”操作时,创建记录,存储过程启动但不呈现下一页,请求在浏览器中保持“待处理”,直到过程完成。一旦过程完成,页面就会被渲染。

我不知道发生了什么。该程序不应该在后台运行吗?

4

2 回答 2

3

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

Sidekiq 需要一个单独的进程来运行(和 Redis),而赛璐珞不需要。否则,它们是相似的。

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}

这将异步执行,几乎马上。

于 2013-05-22T14:36:11.840 回答
1

如果您想从请求/响应线程异步运行某些东西,听起来就是这样,那么您应该使用后台处理系统来执行此操作。

有各种各样的 gem 可以做到这一点 - 将DelayedJobResqueSidekiq视为一些更受欢迎的选项。它们通常需要一些后备存储(Redis、MongoDB、Rails 数据库)来跟踪需要或当前正在运行的任务。

或者,Unicorn 可能对您有用,因为它实际上会产生不会锁定 GVL 的单独进程 - 但我认为底层系统不应由您正在解决的问题类型决定。

于 2013-05-22T14:26:50.330 回答