扩展Dejw的答案(edit2):
File.open(filename,'w'){ |f|
uri = URI.parse(url)
Net::HTTP.start(uri.host,uri.port){ |http|
http.request_get(uri.path){ |res|
res.read_body{ |seg|
f << seg
#hack -- adjust to suit:
sleep 0.005
}
}
}
}
其中filename
和url
是字符串。
当网络成为限制因素时,该sleep
命令是一种可以大大减少CPU使用率的黑客。Net :: HTTP不会等待缓冲区(在v1.9.2中为16kB)填满才让步,因此CPU忙于自己移动小块内存。睡眠片刻可以使缓冲区有机会在两次写操作之间充满,并且CPU使用率与curl解决方案相当,在我的应用程序中相差4-5倍。一个更强大的解决方案可能会检查f.pos
并调整超时以达到目标,例如缓冲区大小的95%-实际上,这就是我在示例中获得的0.005值的方式。
抱歉,但是我不知道让Ruby等待缓冲区填充的更优雅的方法。
编辑:
这是一个自动调整自身以使缓冲区保持等于或低于容量的版本。这是一个不太好的解决方案,但是它看起来一样快,并且只需占用很少的CPU时间,就像它呼唤卷曲一样。
它分为三个阶段。短暂的学习时间和故意较长的睡眠时间可以确定整个缓冲区的大小。下降周期通过将其乘以一个较大的因子来迅速减少每次迭代的睡眠时间,直到找到未满的缓冲区为止。然后,在正常期间,它会以较小的系数上下调整。
我的Ruby有点生锈,所以我敢肯定它可以改善。首先,没有错误处理。另外,也许可以将其分离为一个对象,而不必进行下载本身,这样您就可以autosleep.sleep(f.pos)
在循环中进行调用了吗?更好的是,可以将Net :: HTTP更改为在产生:-)之前等待完整的缓冲区
def http_to_file(filename,url,opt={})
opt = {
:init_pause => 0.1, #start by waiting this long each time
# it's deliberately long so we can see
# what a full buffer looks like
:learn_period => 0.3, #keep the initial pause for at least this many seconds
:drop => 1.5, #fast reducing factor to find roughly optimized pause time
:adjust => 1.05 #during the normal period, adjust up or down by this factor
}.merge(opt)
pause = opt[:init_pause]
learn = 1 + (opt[:learn_period]/pause).to_i
drop_period = true
delta = 0
max_delta = 0
last_pos = 0
File.open(filename,'w'){ |f|
uri = URI.parse(url)
Net::HTTP.start(uri.host,uri.port){ |http|
http.request_get(uri.path){ |res|
res.read_body{ |seg|
f << seg
delta = f.pos - last_pos
last_pos += delta
if delta > max_delta then max_delta = delta end
if learn <= 0 then
learn -= 1
elsif delta == max_delta then
if drop_period then
pause /= opt[:drop_factor]
else
pause /= opt[:adjust]
end
elsif delta < max_delta then
drop_period = false
pause *= opt[:adjust]
end
sleep(pause)
}
}
}
}
end