1

Situation:

  • In a typical cluster setup, I have a 5 instances of mongrel running behind Apache 2.
  • In one of my initializer files, I schedule a cron task using Rufus::Scheduler which basically sends out a couple of emails.

Problem:

  • The task runs 5 times, once for each mongrel instance and each recipient ends up getting 5 mails (despite the fact I store logs of each sent mail and check the log before sending). Is it possible that since all 5 instances run the task at exact same time, they end up reading the email logs before they are written?

I am looking for a solution that will make the tasks run only once. I also have a Starling daemon up and running which can be utilized.

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)?我从杂种移到了phusion,它工作得很好(时间<5分钟)!

于 2009-09-17T10:47:54.407 回答