开发者社区> 问答> 正文

Rails 4.0 先睹为快:作业队列 Run, baby, run!:报错

Rails 最近增加了一个作业队列系统,让我们来看看如何使用。

Run, baby, run!

这个队列 API 非常简单,你将对象放到队列中,而这个对象需要提供一个名为 run 的方法,下面是个简单例子:

class TestJob
  def run
    puts "I am running!"
  end
end

Rails.queue.push(TestJob.new)
=> "I am running!"

对大多数人来说,这已足够。队列是在一个独立的线程中运行,因此你的应用不会因为一些很复杂的作业而导致无响应

Rails 中的基本队列并不是一个长期的解决方案,其目的仅仅是提供一个通用的 API 可以用来支持更可靠的队列系统。例如当你需要从 Resque 切换到 Sidekiq,你不需要更改你应用代码,你只需关心对象进入队列以及编组。

你也可以编写自己的队列,下面是一个简单的队列实现:

class MyQueue
  def push(job)
    job.run
  end
end

要使用你自定义的队列,只需要在 application.rb 中设置:

config.queue = MyQueue 

上面例子来源于 Rails 的测试套件,它定义了一个非异步的队列,当作业被放到队列中便立即执行。下面让我们开发一个实际的作业,无需依赖于 Queue 类。

class MyQueue
  def initialize
    @queue = []
  end

  def push(job)
    @queue.push(job)
  end

  def pop
    @queue.pop
  end
end

这个例子我们实现了一个简单的队列,接下来你需要告诉 Rails 的 QueueConsumer 来使用这个队列,可以在 application.rb 的 initializer 块中设置:

intializer 'start queue consumer' do |app|
  qpp.queue_consumer = config.queue_consumer.start(app.queue)
  at_exit { app.queue.consumer.shutdown }
end

然后我们将新的作业放到队列中:

Rails.queue.push(TestJob.new) 

啥也没有,为什么?检查 QueueConsumer:

Rails.application.queue_consumer
=> #<Rails::Queueing::ThreadedConsumer @queue=#<MyQueue @queue=[]>, @thread=#<Thread dead>>

然后你会发现线程死了,我们可以强行要求队列处理:

Rails.application.queue_consumer.start
=> "I am running!"

回过头来看看到底发生了什么。首先我们找到 ThreadedConsumer#start

def start
  @thread = Thread.new do
    while job = @queue.pop
      begin
        job.run
      rescue Exception => e
        handle_exception e
      end
    end
  end
  self
end

这个线程只有在 @queue.pop 返回一个 true 值的时候才会运行,这不太合理,我们需要不断的将对象推到队列中,让我们来看看 Queue#pop 发生了什么:

# Retrieves data from the queue.  If the queue is empty, the calling thread is
# suspended until data is pushed onto the queue.  If +non_block+ is true, the
# thread isn't suspended, and an exception is raised.
#
def pop(non_block=false)
  while true
    @mutex.synchronize do
      @waiting.delete(Thread.current)
      if @que.empty?
        raise ThreadError, "queue empty" if non_block
        @waiting.push Thread.current
        @resource.wait(@mutex)
      else
        retval = @que.shift
        @resource.signal
        return retval
      end
    end
  end
end

终于有点明白了,Queue#pop 是一个无限的循环在等待其需要的内容。而我们简单的 MyQueue 类在 ThreadConsumer#start 调用的时候会返回 nil,因此队列里没对象,线程就结束了。甚至当我们往队列里放对象时,再次 pop 操作后也会结束。

简单起见,只需要让 MyQueue 继承 Queue 即可:

class MyQueue < Queue
end

现在我们可以:

Rails.queue.push(TestJob.new)
=> "I am running!"

Rails 4.0 中的队列系统是一个非常简单的解决方案,我们期待正式版的发布,能够支持更多更好的后台作业处理库。

需要注意的是,目前的队列还是 beta 版本,API 可能还有有所更改。

英文原文OSCHINA原创翻译

展开
收起
kun坤 2020-06-06 13:47:07 575 0
1 条回答
写回答
取消 提交回答
  • 用过一段时间,虽然现在不再使用,但还是强烈支持!######想用  rails,可惜公司不给推,靠
    ###### 怎么感觉应用不给力啊。。。

    2020-06-06 13:47:14
    赞同 展开评论 打赏
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
Go To Hunt Then Sleep 立即下载
Go To Hunt,Then Sleep 立即下载
Run containerd as Container Runtime in production 立即下载