Работа со Starling и несколькими экземплярами Mongrel через кластер Mongrel - PullRequest
1 голос
/ 17 сентября 2009

Ситуация:

  • В типичной настройке кластера у меня есть 5 экземпляров mongrel, работающих за Apache 2.
  • В одном из моих файлов инициализатора я планирую задачу cron, используя Rufus::Scheduler, которая в основном отправляет пару писем.

Проблема:

  • Задача выполняется 5 раз, по одному разу для каждого экземпляра монгрела, и каждый получатель получает 5 писем (несмотря на то, что я храню журналы каждой отправленной почты и проверяю журнал перед отправкой). Возможно ли, что, поскольку все 5 экземпляров запускают задачу в одно и то же время, они заканчивают тем, что читают журналы электронной почты до того, как они будут записаны?

Я ищу решение, которое позволит выполнять задачи только один раз. У меня также есть демон Starling, который можно использовать.

Ответы [ 3 ]

3 голосов
/ 20 сентября 2009

Плагин rooster rails специально решает вашу проблему. Он использует rufus-планировщик и обеспечивает загрузку среды только один раз.

1 голос
/ 20 сентября 2009

То, как я делаю это прямо сейчас:

  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")
0 голосов
/ 17 сентября 2009

Почему вы не используете mod_passenger (phusion)? Я перешел из монгрела в фьюжн, и он работал отлично (с временем <5 минут)! </p>

...