Rails 最近增加了一个作业队列系统,让我们来看看如何使用。
这个队列 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 可能还有有所更改。
用过一段时间,虽然现在不再使用,但还是强烈支持!######想用 rails,可惜公司不给推,靠
###### 怎么感觉应用不给力啊。。。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。