我们在大多数项目中都使用了Resque,我们对它很满意。
在最近的一个项目中,我们遇到了一种情况,我们正在通过Twitter建立与实时流API的连接。因为,我们必须维护连接,我们将每个行从流API转储到resque队列,以免连接丢失。我们当时正在处理队列。
我们遇到这样的情况:队列中的插入速率是30-40 /秒的顺序,并且弹出队列的速率只有3-5 /秒。因此,队列总是在增加。当我们检查原因时,我们发现resque有一个父进程,并且对于队列的每个作业,它都会分叉子进程,子进程将处理该作业。我们的铁路环境非常繁重,儿童流程分叉需要时间。
因此,我们暂时实施了另一种rake任务:
rake :process_queue => :environment do
while true
begin
interaction = Resque.pop("process_twitter_resque")
if interaction
ProcessTwitterResque.perform(interaction)
end
rescue => e
puts e.message
puts e.backtrace.join("\n")
end
end
end
并开始这样的任务:
nohup bundle exec rake process_queue --trace >> log/workers/process_queue/worker.log 2>&1 &
这不会处理失败的作业和所有作业。
但是,我的问题是为什么Resque实现了一个子分叉进程来处理队列中的作业。绝对不需要处理作业(因为它是一个队列,我们希望它一个接一个地处理,顺序和我相信Resque也一次只分配一个子进程)。
我确信Resque已经考虑到了一些目的。这个父/子流程架构背后的确切目的是什么?
答案 0 :(得分:2)
在Redis中驻留和侦听作业的Ruby进程不是最终运行在perform方法中编写的作业代码的进程。这是“主”过程,其唯一的责任是倾听工作。当它收到一个作业时,它会再运行另一个进程来运行代码。这个其他“子”过程完全由其主人管理。用户不负责使用rake任务启动或与之交互。当子进程完成运行作业代码时,它将退出并将控制权返回给其主进程。主人现在继续听取Redis的下一份工作。
这个主子流程组织的优势 - 以及Resque流程优于线程 - 是工作代码的隔离。 Resque假定您的代码存在缺陷,并且包含内存泄漏或其他会导致异常行为的错误。子进程声明的任何内存将在退出时释放。这消除了随着时间的推移不受管理的内存增长的可能性。它还使主过程能够从孩子的任何错误中恢复,无论多么严重。例如,如果需要使用kill -9终止子进程,则不会影响主进程从Redis队列继续处理作业的能力。
在早期版本的Ruby中,Resque的主要批评是其消耗大量内存的潜力。创建新进程意味着为每个进程创建单独的内存空间。由于copy-on-write,随着Ruby 2.0的发布,一些开销减轻了。但是,Resque总是需要比使用线程的解决方案更多的内存,因为主进程不是分叉的。它是使用rake任务手动创建的,因此必须从一开始就将所需内容加载到内存中。当然,手动管理具有潜在大量作业的生产应用程序中的每个工作进程很快就会变得难以维持。值得庆幸的是,我们有游泳池经理。
答案 1 :(得分:0)
Resque使用#fork有两个原因(其中包括):防止僵尸工作者的能力(只是杀死他们)和使用多个核心的能力(因为这是另一个过程)。
也许这可以帮助您快速执行作业:http://thewebfellas.com/blog/2012/12/28/resque-worker-performance