通过 Mongrel Cluster 使用 Starling 和 Mongrel 的多个实例

发布于 2024-08-05 04:52:17 字数 373 浏览 7 评论 0原文

情况:

  • 在典型的集群设置中,我有 5 个 mongrel 实例在 Apache 2 后面运行。
  • 在我的一个初始化程序文件中,我使用 Rufus::Scheduler 安排一个 cron 任务,该任务基本上会发送几个电子邮件。

问题:

  • 该任务运行 5 次,每个 mongrel 实例运行一次,每个收件人最终会收到 5 封邮件(尽管我存储了每封已发送邮件的日志并在发送前检查日志)。是否有可能由于所有 5 个实例同时运行该任务,因此它们最终会在写入电子邮件日志之前读取它们?

我正在寻找一种解决方案,使任务仅运行一次。我还有一个正在运行的 Starling 守护进程可供使用。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

如痴如狂 2024-08-12 04:52:17

rooster Rails 插件专门解决了您的问题。它使用 rufus-scheduler 并确保环境仅加载一次。

The rooster rails plugin specifically addresses your issue. It uses rufus-scheduler and ensures the environment is loaded only once.

为人所爱 2024-08-12 04:52:17

我现在的做法是:

  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")

The way I am doing it right now:

  1. Try to open a file in exclusive locked mode
  2. When lock is acquired, check for messages in Starling
  3. If message exists, other process has already scheduled the job
  4. Set the message again to the queue and exit.
  5. If message is not found, schedule the job, set the message and exit

Here is the code that does it:

    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")
万劫不复 2024-08-12 04:52:17

为什么不使用 mod_passenger (phusion)?我从 mongrel 转向 phusion,效果非常好(时间少于 5 分钟)!

Why dont you use mod_passenger (phusion)? I moved from mongrel to phusion and it worked perfect (with a timeamount of < 5 minutes)!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文