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

Jon*_*han 0 ruby logging multithreading daemon ruby-on-rails

我写了一个管理脚本,尾随一个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
Run Code Online (Sandbox Code Playgroud)

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

有人看到一种方法可以降低处理器密集度吗?

dbe*_*hur 6

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

您可以通过避免其写入临时文件的线程,而不是使用获得更真实的EOF阅读和漂亮的阻塞行为IO::popen到餐桌的%x[heroku logs --ps router --tail --app pipewave-cedar]日志零售商,连接到Ruby的IO对象,您可以遍历gets时,退出gets回报率nil(表示日志tailer完成).gets当没有什么东西可以阅读时,来自tailer的管道会阻塞,你的脚本只会像你的行解析和报告一样热.

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

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
Run Code Online (Sandbox Code Playgroud)

我也注意到你的报告线程没有做任何事情来同步访问@total_lines,@total_errors等等.所以,你有一些小的竞争条件,你可以得到不一致的值从实例瓦尔该parse_heroku_line方法的更新.