описание - package java.io что это




Потоковая передача больших файлов в сервлет Java (6)

В дополнение к тому, что предложил Джон, вы должны повторно очистить выходной поток. В зависимости от вашего веб-контейнера возможно, что он кэширует части или даже весь ваш выход и сбрасывает его один раз (например, для вычисления заголовка Content-Length). Это сгорело бы немного памяти.

Я создаю java-сервер, который нужно масштабировать. Один из сервлетов будет обслуживать изображения, хранящиеся в Amazon S3.

Недавно под загрузкой у меня закончилась нехватка памяти в моей виртуальной машине, и это было после добавления кода для обслуживания изображений, поэтому я уверен, что потоковая передача больших ответов сервлетов вызывает мои проблемы.

Мой вопрос: есть ли какая-либо передовая практика в том, как закодировать сервлет Java для потоковой передачи большого (> 200k) ответа на браузер при чтении из базы данных или другого облачного хранилища?

Я рассмотрел возможность записи файла на локальный временный диск, а затем создаю другой поток для обработки потоковой передачи, чтобы поток сервлетов tomcat можно было повторно использовать. Кажется, это было бы тяжело.

Любые мысли были бы оценены. Благодарю.


Вы должны проверить две вещи:

  • Вы закрываете поток? Очень важно
  • Возможно, вы предоставляете потоковые соединения «бесплатно». Поток невелик, но многие потоки одновременно могут украсть всю вашу память. Создайте пул, чтобы вы не могли одновременно запускать определенное количество потоков

Когда это возможно, вы не должны хранить все содержимое файла для обслуживания в памяти. Вместо этого загрузите InputStream для данных и скопируйте данные в Servlet OutputStream по частям. Например:

ServletOutputStream out = response.getOutputStream();
InputStream in = [ code to get source input stream ];
String mimeType = [ code to get mimetype of data to be served ];
byte[] bytes = new byte[FILEBUFFERSIZE];
int bytesRead;

response.setContentType(mimeType);

while ((bytesRead = in.read(bytes)) != -1) {
    out.write(bytes, 0, bytesRead);
}

// do the following in a finally block:
in.close();
out.close();

Я согласен с тобой, вместо этого вы должны указывать их на URL-адрес S3.

Что касается исключения OOM, вы уверены, что оно связано с обслуживанием данных изображения? Допустим, ваша JVM имеет 256 МБ «лишней» памяти для использования для обработки данных изображения. С помощью Google «256 МБ / 200 КБ» = 1310. Для 2 ГБ «лишней» памяти (в наши дни очень разумная сумма) может поддерживаться более 10 000 одновременных клиентов. Тем не менее, 1300 одновременных клиентов - довольно большое число. Это тот тип нагрузки, который вы испытали? Если нет, вам может потребоваться искать в другом месте причину исключения OOM.

Изменить - Отношений:

В этом случае изображения могут содержать конфиденциальные данные ...

Когда я прочитал документацию S3 несколько недель назад, я заметил, что вы можете генерировать ключи с истечением срока действия, которые могут быть привязаны к URL-адресам S3. Таким образом, вам не придется открывать файлы на S3 для публики. Мое понимание техники:

  1. На первой странице HTML есть ссылки для загрузки на ваш веб-сайт
  2. Пользователь нажимает ссылку для загрузки
  3. Ваш webapp генерирует URL-адрес S3, который включает в себя ключ, который истекает, скажем, 5 минут.
  4. Отправьте HTTP-перенаправление клиенту с URL-адресом с шага 3.
  5. Пользователь загружает файл с S3. Это работает, даже если загрузка занимает более 5 минут - после начала загрузки она может продолжаться до завершения.

Почему бы вам просто не указать их на S3-url? Принимая артефакт от S3, а затем передавая его через свой собственный сервер, я побеждаю цель использования S3, которая заключается в выгрузке полосы пропускания и обработке обслуживания изображений на Amazon.


Я видел много кода, например, ответ от john-vasilef (в настоящее время принятый), плотный цикл чтения циклов из одного потока и запись их в другой поток.

Аргумент, который я сделал бы, - против ненужного дублирования кода, в пользу использования IOUtils от Apache. Если вы уже используете его в другом месте или если другая библиотека или фрейм, которые вы используете, уже в зависимости от этого, это единственная строка, которая известна и хорошо протестирована.

В следующем коде я передаю объект из Amazon S3 клиенту в сервлет.

import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;

InputStream in = null;
OutputStream out = null;

try {
    in = object.getObjectContent();
    out = response.getOutputStream();
    IOUtils.copy(in, out);
} finally {
    IOUtils.closeQuietly(in);
    IOUtils.closeQuietly(out);
}

6 строк четко определенного шаблона с правильным закрытием потока кажутся довольно прочными.


Я согласен с обоими toby и John Vasileff - S3 отлично подходит для загрузки больших медиа-объектов, если вы можете терпеть связанные с ними проблемы. (Экземпляр собственного приложения делает это для FLV и MP4 10-1000 МБ.) Например: Нет частичных запросов (заголовок диапазона байтов). Нужно обрабатывать это «вручную», время от времени и т. Д.

Если это не вариант, код Джона выглядит хорошо. Я обнаружил, что байтовый буфер 2k FILEBUFFERSIZE является наиболее эффективным в методах микрообработки. Другим вариантом может быть общий FileChannel. (FileChannels потокобезопасны.)

Тем не менее, я также добавлю, что догадка о том, что вызвало ошибку в памяти, является классической ошибкой оптимизации. Вы бы улучшили свои шансы на успех, работая с жесткими метриками.

  1. Place -XX: + HeapDumpOnOutOfMemoryError в вас параметры запуска JVM, на всякий случай
  2. использовать jmap на запущенной JVM ( jmap -histo <pid> ) под нагрузкой
  3. Проанализируйте метрики (jmap -histo out put или посмотрите, как вы смотрите на кучу кучи). Очень хорошо, может быть, что ваш недостаток памяти исходит откуда-то неожиданным.

Есть, конечно, другие инструменты, но jmap & jhat поставляется с Java 5+ «из коробки»,

Я рассмотрел возможность записи файла на локальный временный диск, а затем создаю другой поток для обработки потоковой передачи, чтобы поток сервлетов tomcat можно было повторно использовать. Кажется, это было бы тяжело.

Ах, я не думаю, что ты не можешь этого сделать. И даже если бы это было возможно, это звучит сомнительно. Поток tomcat, управляющий соединением, должен контролироваться. Если вы испытываете головокружение потоков, то увеличивайте количество доступных потоков в ./conf/server.xml. Опять же, метрики - это способ обнаружить это - не догадывайтесь.

Вопрос: Вы также работаете на EC2? Каковы параметры запуска JVM вашего tomcat?







java-io