最近我的项目需要比以前更多的IO交互,我觉得我想要浏览常规库(特别是Commons IO)并解决更深入的IO问题。< / p>
作为学术测试,我决定实现一个基本的,多线程的HTTP下载器。这个想法很简单:提供一个下载URL,代码将下载该文件。为了提高下载速度,文件被分块并且每个块同时下载(使用HTTP Range: bytes=x-x
标头)以尽可能多地使用带宽。
我有一个工作原型,但你可能已经猜到了,它并不完全理想。目前我手动启动3个“下载程序”线程,每个线程下载文件的1/3。这些线程使用通用的同步“文件编写器”实例来实际将文件写入磁盘。完成所有线程后,“文件编写器”完成,任何打开的流都关闭。一些代码片段可以给你一个想法:
线程启动:
ExecutorService downloadExecutor = Executors.newFixedThreadPool(3);
...
downloadExecutor.execute(new Downloader(fileWriter, download, start1, end1));
downloadExecutor.execute(new Downloader(fileWriter, download, start2, end2));
downloadExecutor.execute(new Downloader(fileWriter, download, start3, end3));
每个“下载程序”线程下载一个块(缓冲)并使用“文件编写器”写入磁盘:
int bytesRead = 0;
byte[] buffer = new byte[1024*1024];
InputStream inStream = entity.getContent();
long seekOffset = chunkStart;
while ((bytesRead = inStream.read(buffer)) != -1)
{
fileWriter.write(buffer, bytesRead, seekOffset);
seekOffset += bytesRead;
}
“文件编写器”使用RandomAccessFile
到seek()
和write()
块到磁盘写入磁盘:
public synchronized void write(byte[] bytes, int len, long start) throws IOException
{
output.seek(start);
output.write(bytes, 0, len);
}
考虑到所有事情,这种方法似乎有效。但是,它不能很好地工作。我对以下几点有一些建议/帮助/意见表示感谢。非常感谢。
InputStream
几乎从未实际填充缓冲区,这导致了比我想要的更多的IO写入。我的印象是,在这种情况下,最好将IO访问权限保持在最低限度,但我不确定这是否是最佳方法。 注意:我使用Apache HTTPClient进行HTTP交互,这是entity.getContent()
来自的地方(万一有人想知道)。
答案 0 :(得分:6)
回答我自己的问题:
while() {}
循环造成的。事实证明,awaitTermination
是等待Executor
完成的更好的选择:)答案 1 :(得分:3)
据推测,Apache HTTP客户端将使用较小的缓冲区进行缓冲。它需要一个缓冲区来合理地读取HTTP头,并且可能需要处理分块编码。
答案 2 :(得分:2)
我想在Windows上获得最佳性能的想法是使用IO completions ports。我不知道的是(a)其他操作系统中是否有类似的概念,以及(b)是否有任何合适的Java包装器?但是,如果可移植性对您来说并不重要,则可以使用JNI滚动自己的包装器。
答案 3 :(得分:0)
设置一个非常大的套接字接收缓冲区。但实际上,您的性能将受到网络带宽的限制,而不受CPU带宽的限制。您所做的只是为每个下载器分配1/3的网络带宽。如果你得到很多好处,我会感到惊讶。