2016-01-27 3 views
0

У меня есть веб-приложение на основе Spring, которое иногда нужно отправлять и получать потенциально большие документы в виде файлов с несколькими частями. Когда файлы загружаются, они обычно записываются прямо в базу данных. При загрузке всегда считываются и отправляются с любой обработкой. При тестировании нагрузки в какой-то момент приложение начинает бросать OutOfMemoryErrors. Чтобы решить эту проблему, вместо того, чтобы загружать полный многокомпонентный файл в память, я читаю и пишу прямо между потоками запроса/ответа в/из потока и блобом в db. Как и ожидалось, это фиксировало OutOfMemoryErrors. Тем не менее, тогда приложение работает в 3-7 раз медленнее при низких нагрузках.Загрузка файла в оптимизацию сервлетов

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

Редактировать: акцент делается на низких нагрузках. После того, как он будет работать достаточно долго или с достаточно высокой нагрузкой, будет еще больше ухудшаться, потому что (Im guessing) gc должен работать довольно много постоянно. А что раньше?

добавив соответствующий код: в основном это контроллеры типа

public ResponseEntity<String> saveStuff(..., MultipartFile file){ 
    .... 
    dao.save(..., file.getBytes()); 
} 

были заменены

public ResponseEntity<String> saveStuff(..., HttpServletRequest request){ 
    .... 
    dao.save(..., request.getInputStream()); 
} 

и в дао той части, которая записывает входной поток в сгустка является

try { 
    byte[] bytesRead = new byte[4096]; 
    while (in.read(bytesRead, 0, bytesRead.length) != -1) { 
     toBlob.write(bytesRead); 
    } 
} catch (IOException e) { 
    e.printStackTrace(); 
} finally { 
    try { 
     in.close(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
    try { 
     toBlob.close(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 
+0

Оставьте свой код. Основной проблемой будет размер буфера чтения. Чтение всей загрузки в память сначала добавляет латентность, а также тратит пространство, поэтому оно не должно быть быстрее, чем правильно написанный цикл с использованием разумного размера буфера. – EJP

+0

уверен. добавлен соответствующий код. – aiguy

+1

Вы отключили разбор партирования, т. Е. Удалили «MultipartResolver»? Кроме того, https://commons.apache.org/proper/commons-fileupload/streaming.html объясняет, как выполнять загрузку потоковых файлов с помощью файла commons-fileupload. «InputStream» в вашем случае - это не то же самое, что и «MultipartFile», поэтому не уверен, действительно ли вы написали правильный материал в базу данных ... Также интересно, что такое 'toBlob'? –

ответ

1

Ваш код не совсем правильный, так почему вы беспокоитесь о его производительность не ясна. Попробуйте следующее:

int count; 
byte[] buffer = new byte[8192]; 
while ((count = in.read(buffer)) > 0) 
{ 
    out.write(buffer, 0, count); 
} 

В отличие от вашей, эта версия правильно записывает окончательный частичный буфер.

Что касается производительности, я бы экспериментировал с еще большим размером буфера 32k и более.

+0

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

0

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

statement.setBinaryStream(paramIndex, requestInputStream, contentLength); 
//....execute statement and stuff.... 
Смежные вопросы