Java многопоточное выполнение загрузки файла

Недавно работая над проектом, который потребовал еще немного взаимодействия 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);
}

Учитывая все обстоятельства, этот подход, кажется, работает. Однако это не работает очень хорошо. Я ценил бы некоторый совет/справку/мнения относительно следующих моментов. Очень ценивший.

  1. Использование ЦП этого кода через крышу. Это использует половину моего ЦП (50% каждого из этих 2 ядер), чтобы сделать это, которое является экспоненциально больше, чем сопоставимые инструменты загрузки, которые едва подчеркивают ЦП вообще. Я немного мистифицирован как, туда, где это использование ЦП прибывает из, поскольку я не ожидал это.
  2. Обычно, кажется, существует 1 из 3 потоков, который отстает значительно. Другие 2 потока закончатся, после которого это берет третий поток (который, кажется, главным образом первый поток с первым блоком), 30 или больше секунд для завершения. Я вижу от диспетчера задач, что процесс javaw все еще делает маленькие записи IO, но я действительно не знаю, почему это происходит (я предполагаю условия состязания?).
  3. Несмотря на то, что я выбрал вполне большой буфер (1 МБ), я получаю чувство что InputStream почти никогда на самом деле заполняет буфер, который вызывает больше записей IO, чем я хотел бы. У меня создается впечатление, что в этом сценарии, было бы лучше свести доступ IO к минимуму, но я не знаю наверняка, является ли это лучшим подходом.
  4. Я понимаю, что Java не может быть идеальным языком, чтобы сделать что-то вроде этого, но я убежден, что существует намного больше производительности, которая будет иметься, чем я вхожу в свою текущую реализацию. Действительно ли NIO стоит исследовать в этом случае?

Примечание: Я использую Apache HTTPClient, чтобы сделать взаимодействие HTTP, которое является где entity.getContent() прибывает из (в случае, если любой задается вопросом).

18
задан tmbrggmn 4 August 2010 в 20:35
поделиться

3 ответа

Моей немедленной мыслью для лучшей производительности в Windows было бы использование портов завершения ввода-вывода . Чего я не знаю, так это (а) есть ли аналогичные концепции в других ОС и (б) есть ли подходящие оболочки Java? Однако, если переносимость для вас не важна, возможно, удастся создать собственную оболочку с помощью JNI.

2
ответ дан 30 November 2019 в 09:30
поделиться

Предположительно HTTP-клиент Apache будет выполнять некоторую буферизацию, с меньшим буфером. Ему понадобится буфер для разумного чтения HTTP-заголовков и, вероятно, для обработки кодировки chunked.

3
ответ дан 30 November 2019 в 09:30
поделиться

Установить очень большой буфер приема сокета. Но на самом деле ваша производительность будет ограничиваться пропускной способностью сети, а не пропускной способностью процессора. Все, что вы на самом деле делаете, - это выделяете 1/3 пропускной способности сети каждому загрузчику. Я был бы удивлен, если бы вы получили много пользы.

0
ответ дан 30 November 2019 в 09:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: