如何优雅地重启delayed_job消费者?

时间:2013-11-08 19:19:24

标签: ruby-on-rails capistrano delayed-job

我想知道在新代码推送后优雅地重启delayed_job消费者的最佳方法是什么?我正在使用capistrano推送代码,我知道有重启的命令,但如果当前正在运行的作业,该命令要么挂起(我的部署需要永久),要么强行退出当前正在运行的作业,我会丢失数据。

理想情况下,我希望我的部署发生如下:

  1. 现有delayed_job使用者使用版本1代码运行
  2. 我运行cap deploy,版本2代码被推送到新服务器
  3. 在部署期间,我们触摸文件以告知delayed_job在处理完当前作业时重新启动。这可以通过一系列不同的方式来完成,但我认为这与乘客优雅重启的方式类似。
  4. 现有delayed_job使用者继续使用版本1代码完成当前作业
  5. 当前作业完成,delayed_job消费者发现它需要在继续处理作业之前重新启动
  6. delayed_job使用者自动重启,现在运行版本2代码
  7. delayed_job使用者继续处理作业,现在正在运行第2版代码
  8. 我试图通过检查代码的当前版本来插入一些代码以在作业运行之前重新启动,但每次我这样做时,它就会死掉并且实际上不会重新启动任何东西。示例代码如下:

    def before(job)
      # check to make sure that the version of code here is the right version of code
      live_git_hash = LIVE_REVISION
      local_git_hash = LOCAL_REVISION
    
      if live_git_hash != local_git_hash
        # get environment to reload in
        environment = Rails.env # production, development, staging
    
        # restart the delayed job system
        %x("export RAILS_ENV=#{environment} && ./script/delayed_job restart")
      end
    end
    

    它检测到它很好但它在shell调用时死掉了。有什么想法吗?

    谢谢!

1 个答案:

答案 0 :(得分:4)

提出了一个有效的解决方案。

我有一个基类,我所有延迟的作业都从名为BaseJob继承:

class BaseJob
  attr_accessor :live_hash

  def before(job)
    # check to make sure that the version of code here is the right version of code
    resp = HTTParty.get("#{Rails.application.config.root_url}/revision")
    self.live_hash = resp.body.strip
  end

  def should_perform()
    return self.live_hash == GIT_REVISION
  end

  def perform()
    if self.should_perform == true
      self.safe_perform()
    end
  end

  def safe_perform()
    # override this method in subclasses
  end

  def success(job)
    if self.should_perform == false
      # log stats here about a failure

      # enqueue a new job of the same kind
      new_job = DelayedJob.new
      new_job.priority = job.priority
      new_job.handler = job.handler
      new_job.queue = job.queue
      new_job.run_at = job.run_at
      new_job.save
      job.delete

      # restart the delayed job system
      %x("export RAILS_ENV=#{Rails.env} && ./script/delayed_job stop")
    else
      # log stats here about a success
    end
  end

end

所有基类都继承自BaseJob并覆盖safe_perform以实际完成其工作。关于上述代码的一些假设:

  • Rails.application.config.root_url指向您应用的根目录(即:www.myapp.com)
  • 有一条名为/revision的路线(即:www.myapp.com/revision)
  • 您的应用知道有一个名为GIT_REVISION的全局常量

我最终做的是将git rev-parse HEAD的输出放在一个文件中并用代码推送它。它会在启动时加载,因此它可以在Web版本以及delayed_job使用者中使用。

当我们通过Capistrano部署代码时,我们不再停止,启动或重新启动delayed_job使用者。我们在每分钟运行的消费者节点上安装一个cronjob,并确定delayed_job进程是否正在运行。如果不是,那么将产生一个新的。

由于所有这些,满足以下所有条件:

  • 推送代码不会在delayed_job上等待重启/强制终止。当推送新代码时,正在运行的现有作业将保持不变。
  • 如果消费者正在运行旧代码,我们可以检测作业何时开始。工作被重新排队,消费者自杀。
  • 当delayed_job死掉时,会通过带有新代码的cronjob生成一个新代码(根据启动delayed_job的性质,它有新代码)。
  • 如果您对杀死delayed_job消费者感到偏执,请安装一个与cron作业相同的nagios检查,但是当delayed_job进程没有运行5分钟时会提醒您。