0

我需要在 ruby​​/rails 中异步执行长时间运行的操作。谷歌搜索我发现的一个选项是 Sidekiq。

class WeeklyReportWorker
  include Sidekiq::Worker

  def perform(user, product, year = Time.now.year, week = Date.today.cweek)
    report = WeeklyReport.build(user, product, year, week)
    report.save
  end
end

# call WeeklyReportWorker.perform_async('user', 'product')

一切都很好!但有一个问题。

如果我每隔几秒就一直调用这个异步方法,但是执行繁重操作的实际时间是一分钟,那么事情就不起作用了。

让我举个例子。

5.times { WeeklyReportWorker.perform_async('user', 'product') }

现在我的繁重手术将进行5次。最佳情况下,它应该只执行一次或两次,具体取决于第一个操作的执行是否在第 5 次异步调用之前开始。

你有提示如何解决它?

4

2 回答 2

1

这是一种天真的方法。我是一个 resque 用户,也许 sidekiq 可以提供更好的东西。

def perform(user, product, year = Time.now.year, week = Date.today.cweek)
  # first, make a name for lock key. For example, include all arguments
  # there, so that another perform with the same arguments won't do any work
  # while the first one is still running
  lock_key_name = make_lock_key_name(user, product, year, week)
  Sidekiq.redis do |redis| # sidekiq uses redis, let us leverage that
    begin
      res = redis.incr lock_key_name
      return if res != 1 # protection from race condition. Since incr is atomic, 
                         # the very first one will set value to 1. All subsequent
                         # incrs will return greater values.
                         # if incr returned not 1, then another copy of this 
                         # operation is already running, so we quit.

      # finally, perform your business logic here
      report = WeeklyReport.build(user, product, year, week)
      report.save
    ensure
      redis.del lock_key_name # drop lock key, so that operation may run again.
    end
  end
end
于 2012-08-31T13:23:14.203 回答
0

我不确定我是否很好地理解了您的情况,但是看看这个宝石怎么样:

https://github.com/collectiveidea/delayed_job

所以不要这样做:

5.times { WeeklyReportWorker.perform_async('user', 'product') }

你可以做:

5.times { WeeklyReportWorker.delay.perform('user', 'product') }

开箱即用,这将使工作进程在第一个工作之后处理第二个工作,但前提是您使用默认设置(因为默认情况下工作进程只有一个)。

宝石提供了以下可能性:

  • 将作业放入队列;
  • 如果需要,为不同的工作设置不同的队列;
  • 有多个worker处理一个队列(例如,可以在4-CPU机器上启动4个worker,效率更高);
  • 安排作业在准确的时间运行,或在作业排队后的设定时间后运行。(或者,默认情况下,安排立即后台执行)。

我希望它能像你对我一样帮助你。

于 2012-08-31T15:21:16.097 回答