Недавно работая над проектом, который потребовал еще немного взаимодействия IO, чем, я привык к, я чувствовал, что хотел посмотреть мимо обычных библиотек (палата общин IO, в особенности) и заняться еще многие подробно проблемы IO.
Как академический тест, я решил реализовать основной, многопоточный загрузчик 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;
}
"Устройство записи файла" пишет в диск с помощью a 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()
прибывает из (в случае, если любой задается вопросом).
Моей немедленной мыслью для лучшей производительности в Windows было бы использование портов завершения ввода-вывода . Чего я не знаю, так это (а) есть ли аналогичные концепции в других ОС и (б) есть ли подходящие оболочки Java? Однако, если переносимость для вас не важна, возможно, удастся создать собственную оболочку с помощью JNI.
Предположительно HTTP-клиент Apache будет выполнять некоторую буферизацию, с меньшим буфером. Ему понадобится буфер для разумного чтения HTTP-заголовков и, вероятно, для обработки кодировки chunked.
Установить очень большой буфер приема сокета. Но на самом деле ваша производительность будет ограничиваться пропускной способностью сети, а не пропускной способностью процессора. Все, что вы на самом деле делаете, - это выделяете 1/3 пропускной способности сети каждому загрузчику. Я был бы удивлен, если бы вы получили много пользы.