0

我编写了一个管理脚本,该脚本跟踪 heroku 日志,每隔 n 秒,它会汇总平均值并在我超过某个阈值时通知我(是的,我知道并喜欢新的遗物——但我想做自定义的东西)。

这是整个脚本

我从来都不是 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

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

有人看到了一种减少处理器密集型的方法吗?

4

2 回答 2

6

这可能很热,因为您在从临时文件中读取时从未真正阻塞。是 POSIX select(2)IO::select上的一个薄层。看起来您正试图阻止直到文件准备好读取,但 select(2) 认为 EOF 已准备好(“文件描述符在文件结束时也已准备好”),因此您总是立即返回from select then call gets 在 EOF 处返回 nil。

您可以获得更真实的 EOF 读取和良好的阻塞行为,方法是避免写入临时文件的线程,而是使用IO::popenfork%x[heroku logs --ps router --tail --app pipewave-cedar]日志尾部,连接到可以循环的 ruby​​ IO 对象,返回gets时退出(指示日志裁缝完成)。当没有可读取的内容时,来自tailer 的管道上的管道将阻塞,并且您的脚本只会在执行行解析和报告时尽可能热地运行。getsnilgets

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

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

于 2012-07-06T19:01:49.287 回答
1

select是关于读取是否会阻塞。f 只是一个普通的旧文件,所以当你读到最后时不会阻塞,它们只会立即返回 nil 。结果select立即返回,而不是像我假设的那样等待将某些内容附加到文件中。正因为如此,你正处于一个繁忙的循环中,所以高 CPU 是可以预料的。

如果您处于 eof 状态(您可以检查f.eof?或是否gets返回 nil),那么您可以开始睡眠(可能有某种退避)或使用诸如listen之类的东西来接收文件系统更改的通知

于 2012-07-06T18:52:21.757 回答