单例不断更改其在rails中的每个请求的状态

时间:2013-06-28 00:19:18

标签: ruby-on-rails ruby ruby-on-rails-3 web-applications

这对我来说真的很奇怪。几个小时都感到沮丧。一旦收到RUN请求,RUNNER将启动一个永远运行的后台线程。在任何时候,只有一个正在运行的线程(我使用互斥锁)。 问题是,对于状态请求,有时它返回true,有时它返回false

(完成方法从未被调用,因为后端线程将永远运行,所以is_running没有办法?可能是假的)

#config/initializers/some_init.rb
RUNNER = Runner.instance

#app/controllers/some_controller.rb
class SomeController < ApplicationController
  def run
    RUNNER.run {
      loop do
        Rails.logger.debug "backend thread running #{Time.zone.now}"
        sleep(5)
      end
    }
  end

  def status
    Rails.logger.debug RUNNER.inspect
    RUNNER.is_running?
  end
end

#lib/runner.rb
require 'singleton'
class Runner
  include Singleton
  def initialize
    @running = false
    @mutex = Mutex.new
  end

  def run(&blk)
    @mutex.synchronize do
      return if @running #only one job run is allowed at anytime

      @running = true
      @thr = Thread.new(self) do |r|
        blk.call
        r.done
      end 
    end
  end

  def is_running?
    @mutex.synchronize{ @running }
  end

  private
  def done
    @mutex.synchronize {@running = false}
  end
end

1 个答案:

答案 0 :(得分:4)

简短回答:你不能这样做。

答案很长,Rails由一个或多个进程组成,它们为传入的请求提供服务。随机启动其中一个进程中的线程不会使is_running?标志在除true方法被触发的进程之外的任何进程中变为run

大多数Rails托管环境都会经常创建和销毁进程,但不保证您的进程的活动时间比其当前正在处理的请求长。这意味着你的线程很可能会被意外杀死。

Rails严格来说是一个请求 - 响应系统。如果您需要后台进程,则必须将其创建为独立脚本,而不是通过Rack创建的内容。

可以创建在Rails环境中运行的长时间运行的脚本,例如,以rails runner开头,但这些必须独立于您的应用程序启动。通常情况下,您使用像systemctlgod这样的流程主管来启动它并使其保持运行。

根据您的使用案例,delayed job等作业处理系统可能更适合您。这非常适合执行间歇性&#34;后台任务&#34;这不适合面向网络的Rails流程模型。