1

状況:

  • 典型的なクラスター設定では、Apache 2 の背後で実行されている mongrel の 5 つのインスタンスがあります。
  • Rufus::Scheduler初期化ファイルの 1 つで、基本的に 2 通の電子メールを送信するcron タスクをスケジュールします。

問題:

  • タスクは 5 回実行され、mongrel インスタンスごとに 1 回ずつ実行され、各受信者は 5 つのメールを受け取ることになります (送信された各メールのログを保存し、送信前にログを確認しているにもかかわらず)。5 つのインスタンスすべてがまったく同時にタスクを実行するため、メール ログが書き込まれる前に読み取られる可能性はありますか?

タスクを 1 回だけ実行するソリューションを探しています。また、Starling デーモンを稼働させて利用することもできます。

4

3 に答える 3

3

rooster rails プラグインは、特に問題に対処します。rufus-scheduler を使用し、環境が一度だけ読み込まれるようにします。

于 2009-09-20T06:10:53.993 に答える
1

私が今やっている方法:

  1. 排他ロック モードでファイルを開こうとする
  2. ロックが取得されたら、Starling でメッセージを確認します
  3. メッセージが存在する場合、他のプロセスがすでにジョブをスケジュールしています
  4. メッセージを再度キューに設定して終了します。
  5. メッセージが見つからない場合は、ジョブをスケジュールし、メッセージを設定して終了します

これを行うコードは次のとおりです。

    starling = MemCache.new("#{Settings[:starling][:host]}:#{Settings[:starling][:port]}")
    mutex_filename = "#{RAILS_ROOT}/config/file.lock"
    scheduler = Rufus::Scheduler.start_new


    # The filelock method, taken from Ruby Cookbook
    # This will ensure unblocking of the files
    def flock(file, mode)
      success = file.flock(mode)
      if success
        begin
          yield file
        ensure
          file.flock(File::LOCK_UN)
        end
      end
      return success
    end

    # open_lock method, taken from Ruby Cookbook
    # This will create and hold the locks
    def open_lock(filename, openmode = "r", lockmode = nil)
      if openmode == 'r' || openmode == 'rb'
        lockmode ||= File::LOCK_SH
      else
        lockmode ||= File::LOCK_EX
      end
      value = nil
      # Kernerl's open method, gives IO Object, in our case, a file
      open(filename, openmode) do |f|
        flock(f, lockmode) do
          begin
            value = yield f
          ensure
            f.flock(File::LOCK_UN) # Comment this line out on Windows.
          end
        end
        return value
      end
    end

    # The actual scheduler
    open_lock(mutex_filename, 'r+') do |f|
      puts f.read
      digest_schedule_message = starling.get("digest_scheduler")
      if digest_schedule_message
        puts "Found digest message in Starling. Releasing lock. '#{Time.now}'"
        puts "Message: #{digest_schedule_message.inspect}"
        # Read the message and set it back, so that other processes can read it too
        starling.set "digest_scheduler", digest_schedule_message
      else
        # Schedule job
        puts "Scheduling digest emails now. '#{Time.now}'"
        scheduler.cron("0 9 * * *") do
          puts "Begin sending digests..."
          WeeklyDigest.new.send_digest!
          puts "Done sending digests."
        end
        # Add message in queue
        puts "Done Scheduling. Sending the message to Starling. '#{Time.now}'"
        starling.set "digest_scheduler", :date => Date.today
      end
    end

    # Sleep will ensure all instances have gone thorugh their wait-acquire lock-schedule(or not) cycle
    # This will ensure that on next reboot, starling won't have any stale messages
    puts "Waiting to clear digest messages from Starling."
    sleep(20)
    puts "All digest messages cleared, proceeding with boot."
    starling.get("digest_scheduler")
于 2009-09-20T12:49:45.737 に答える
-1

なぜ mod_passenger (phusion) を使わないのですか? mongrel から phusion に移行したところ、完璧に機能しました (時間は 5 分未満)。

于 2009-09-17T10:47:54.407 に答える