为什么我的Ruby脚本使用了90%的CPU?

时间:2012-07-06 18:27:19

标签: ruby-on-rails ruby multithreading logging daemon

我写了一个管理脚本,每个n秒记录一个heroku日志,它总结了平均值并通知我,如果我越过某个阈值(是的我知道并喜欢新的遗物 - 但我想做自定义的东西)。< / p>

这是entire script

我从未成为IO和线程的主人,我想知道我是否犯了一个愚蠢的错误。我有几个守护程序线程while(true){}可能是罪魁祸首。例如:

# read new lines
f = File.open(file, "r")
f.seek(0, IO::SEEK_END)
while true do
  select([f])
  line = f.gets
  parse_heroku_line(line)
end

我使用一个守护进程来监视日志的新行,另一个用于定期汇总。

是否有人认为可以降低处理器密集度?

2 个答案:

答案 0 :(得分:6)

这可能很热,因为从临时文件读取时你从未真正阻止过。 IO::select是POSIX select(2)上的薄层。看起来你试图阻止,直到文件准备好读取,但select(2)认为EOF准备就绪(“文件描述符也准备好在文件结尾”),所以你总是立即返回从select然后调用gets在EOF返回nil。

通过避免写入临时文件的线程,而不是使用IO::popen来分叉连接到ruby IO对象的%x[heroku logs --ps router --tail --app pipewave-cedar]日志记录器,您可以获得更真实的EOF读取和良好的阻塞行为您可以循环gets,当gets返回nil时退出(表示日志标题已完成)。当没有任何内容可读时,来自分页器的管道上的gets将被阻止,并且您的脚本只会像执行行解析和报告一样热。

编辑:我没有设置为实际尝试您的代码,但您应该能够使用此代码替换日志分页器线程和临时文件读取循环以获得上述行为:

IO.popen( %w{ heroku logs --ps router --tail --app my-heroku-app } ) do |logf|
  while line = logf.gets
    parse_heroku_line(line) if line =~ /^/
  end
end

我还注意到您的报告线程没有做任何事情来同步对@total_lines@total_errors等的访问。因此,您有一些较小的竞争条件,您可以从实例变量中获取不一致的值parse_heroku_line方法更新。

答案 1 :(得分:1)

select是关于读取是否会阻止。 f只是一个普通的旧文件,所以当你到最后读取时不要阻塞,它们只是立即返回nil。结果select立即返回,而不是等待某些内容附加到文件中,因为我认为你正在期待。因此,你正处于一个紧张的繁忙循环中,因此需要很高的cpu。

如果你在eof(你可以检查f.eof?gets是否返回nil),那么你可以开始睡觉(可能有某种退避)或使用类似{{{ 3}}通知文件系统更改