0

Ruby/Railsで非同期的に長時間実行する操作を実行する必要があります。私が見つけたオプションの1つをグーグルで検索すると、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')

すべてがうまくいきます!しかし問題がある。

この非同期メソッドを数秒ごとに呼び出し続けても、重い操作が実際に実行される時間は1分であり、機能しません。

例を挙げましょう。

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

これで、私の重い操作が5回実行されます。最適には、5番目の非同期呼び出しが行われる前に最初の操作の実行が開始されたかどうかに応じて、1回または2回だけ実行する必要があります。

それを解決するためのヒントはありますか?

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

I am not sure I understood your scenario well, but how about looking at this gem:

https://github.com/collectiveidea/delayed_job

So instead of doing:

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

You can do:

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

Out of the box, this will make the worker process the second job after the first job, but only if you use the default settings (because by default the worker process is only one).

The gem offers possibilities to:

  • Put jobs on a queue;
  • Have different queues for different jobs if that is required;
  • Have more than one workers to process a queue (for example, you can start 4 workers on a 4-CPU machine for higher efficiency);
  • Schedule jobs to run at exact times, or after set amount of time after queueing the job. (Or, by default, schedule for immediate background execution).

I hope it can help you as you did to me.

于 2012-08-31T15:21:16.097 に答える