2015-12-10 7 views
0

Я сталкиваясь с senerior, как это:OutputSream.write слишком медленно

Мой проект имеет сервлет, чтобы поймать запрос от Perl. Запрос - загрузить файл. Запрос представляет собой multipartRequest.

@RequestMapping(value = "/*", method = RequestMethod.POST) 
     public void tdRequest(@RequestHeader("Authorization") String authenticate, 
           HttpServletResponse response, 
           HttpServletRequest request) throws Exception 
     { 
    if (ServletFileUpload.isMultipartContent(request)) 
      { 
       ServletFileUpload sfu = new ServletFileUpload(); 
       FileItemIterator items = sfu.getItemIterator(request); 
       while (items.hasNext()) 
       { 
        FileItemStream item = items.next(); 
        if (("action").equals(item.getFieldName())) 
        { 
         InputStream stream = item.openStream(); 
         String value = Streams.asString(stream); 

         if (("upload").equals(value)) 
         { 
          uploadRequest(items, response); 
          return; 
         } 
         else if (("download").equals(value)) 
         { 
          downloadRequest(items, response); 
          return; 
         } 

Проблема не здесь, она появляется в функции downloadRequest().

void downloadRequest(FileItemIterator items, 
          HttpServletResponse response) throws Exception 
{ 
log.info("Start downloadRequest......."); 
OutputStream os = response.getOutputStream(); 
File file = new File("D:\\clip.mp4"); 
      FileInputStream fileIn = new FileInputStream(file); 
      //while ((datablock = dataOutputStreamServiceImpl.readBlock()) != null) 
      byte[] outputByte = new byte[ONE_MEGABYE]; 
      while (fileIn.read(outputByte) != -1) 
      { 

       System.out.println("--------" + (i = i + 1) + "--------"); 
       System.out.println(new Date()); 
       //dataContent = datablock.getContent(); 
       System.out.println("Start write " + new Date()); 
       os.write(outputByte, 0,outputByte.length); 
       System.out.println("End write " + new Date()); 
       //System.out.println("----------------------"); 
      } 
      os.close(); 
     } 
    } 

Я стараюсь читать и записывать блоки из 1 МБ из файла. Однако загрузка всего файла занимает слишком много времени. (Мой случай 20mins для файла 100MB)

Я стараюсь SYSOUT, и я увидел результат, как этот:

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

--------1-------- 
Mon Dec 07 16:24:20 ICT 2015 
Start write Mon Dec 07 16:24:20 ICT 2015 
End write Mon Dec 07 16:24:21 ICT 2015 
--------2-------- 
Mon Dec 07 16:24:21 ICT 2015 
Start write Mon Dec 07 16:24:21 ICT 2015 
End write Mon Dec 07 16:24:21 ICT 2015 
--------3-------- 
Mon Dec 07 16:24:21 ICT 2015 
Start write Mon Dec 07 16:24:21 ICT 2015 
End write Mon Dec 07 16:24:21 ICT 2015 

Но следующий блок медленнее, чем предыдущая

--------72-------- 
Mon Dec 07 16:29:22 ICT 2015 
Start write Mon Dec 07 16:29:22 ICT 2015 
End write Mon Dec 07 16:29:29 ICT 2015 
--------73-------- 
Mon Dec 07 16:29:29 ICT 2015 
Start write Mon Dec 07 16:29:29 ICT 2015 
End write Mon Dec 07 16:29:37 ICT 2015 

--------124-------- 
Mon Dec 07 16:38:22 ICT 2015 
Start write Mon Dec 07 16:38:22 ICT 2015 
End write Mon Dec 07 16:38:35 ICT 2015 
--------125-------- 
Mon Dec 07 16:38:35 ICT 2015 
Start write Mon Dec 07 16:38:35 ICT 2015 
End write Mon Dec 07 16:38:48 ICT 2015 

проблема находится в os.write() я действительно не могу понять, как OutputStream писать, почему это принять такое долгое время? или я допустил некоторые ошибки?

Извините за мой плохой английский. Я действительно нуждаюсь в вашей поддержке. Заранее спасибо!

Это Perl код на стороне клиента

# ----- get connected to download the file 
     # 
     $Response = $ua->request(POST $remoteHost , 
         Content_Type   => 'form-data', 
         Authorization   => $Authorization, 
         'Proxy-Authorization' => $Proxy_Authorization , 

         Content => [ DOS   => 1 , 
            action  => 'download' , 
            first_run => 0 , 
            dl_filename => $dl_filename , 
            delivery_dir => $delivery_dir , 
            verbose  => $Verbose , 
            debug  => $debug , 
            version  => $VERSION 
            ] 
         ); 

     unless ($Response->is_success) { 
       my $Msg = $Response->error_as_HTML; 

       # Remove HTML tags - we're in a DOS shell! 
         $Msg =~ s/<[^>]+>//g; 

       print "ERROR! SERVER RESPONSE:\n$Msg\n"; 
       print "$remoteHost\n\n" if $Options{'v'}; 
       Error "Could not connect to " . $remoteHost ; 
     } 

     my $Result2 = $Response->content(); 

     Error "Abnormal termination...\n$Result2" if $Result2 =~ /_APP_ERROR_/; 

     open(F, ">$dl_filename") or Error "Could not open '$dl_filename'!"; 
     binmode F; # unless $dl_filename =~ /\.txt$|\.htm$/; 
     print F $Result2; 
     close F; 
     print "received.\n"; 

     } 
+1

Я бы сказал, что 1MB буферы могут быть слишком большими, попробуйте что-то вроде 8K –

+0

@ScaryWombat Я пробовал случайный размер буфера (2K, 4K, 500Kb, 1Mb, 10Mb), но проблема все та же :( – MCT

+0

You не указывал тип ответа, например 'video/mp4'. По умолчанию это' text/html', и это неверно для файла '.mp4'. Вероятно, это не повлияет на производительность, но попробуй. - Поскольку он начинается быстро, а затем замедляется, это, вероятно, связано с перегрузкой/дросселированием сети, что вызывает вашу проблему. – Andreas

ответ

2

Одна проблема заключается в том, что fileIn.read (outputByte) может читать случайное число байтов, а не только полный outputByte. Вы читаете несколько КБ, тогда вы сохраняете полный 1 МБ, и очень быстро у вас заканчивается свободное место на диске. Попробуйте это, обратите внимание на параметр «readed».

void downloadRequest(FileItemIterator items, 
          HttpServletResponse response) throws Exception 
    { 
    log.info("Start downloadRequest......."); 
    OutputStream os = response.getOutputStream(); 
    File file = new File("D:\\clip.mp4"); 
       FileInputStream fileIn = new FileInputStream(file); 
       //while ((datablock = dataOutputStreamServiceImpl.readBlock()) != null) 
       byte[] outputByte = new byte[ONE_MEGABYE]; 
       int readed =0; 
       while ((readed =fileIn.read(outputByte)) != -1) 
       { 

        System.out.println("--------" + (i = i + 1) + "--------"); 
        System.out.println(new Date()); 
        //dataContent = datablock.getContent(); 
        System.out.println("Start write " + new Date()); 
        os.write(outputByte, 0,readed); 
        System.out.println("End write " + new Date()); 
        //System.out.println("----------------------"); 
       } 
       os.close(); 
      } 
     } 
+0

Большое спасибо, это хороший момент. Я исправил это, но производительность все тот же. есть идея? – MCT

+0

Какова ваша сеть? Какова пропускная способность сети принимающей стороны? –

+0

все сейчас на моем местном ... – MCT

2

Похоже, что скорость загрузки уменьшается медленнее и медленнее, чем дальше вы попадаете в загрузку. Вы начинаете с одной или меньшей секунды на блок, на блок 72 - 7 + секунд на блок, а на блок 128 - 13 секунд на блок.

На этом основании ничего не говорится об этом. Скорее, у него есть «запах» стороны клиента, делающий что-то неправильно. Я предполагаю, что клиентская сторона считывает данные из сокета в структуру данных в памяти, и эта структура данных (возможно, только String или StringBuffer или StringBuilder) становится все больше и больше. Либо время, затрачиваемое на его расширение, становится все больше, или ваш объем памяти растет, а GC занимает больше времени и дольше. (Или оба.)

Если вы показали нам код на стороне клиента .....


UPDATE

Как я и предполагал, эта строка кода будет чтение всего содержимое в Perl-эквивалент построителя строк, прежде чем превращать его в строку.

my $Result2 = $Response->content(); 

В зависимости от того, как она осуществляется под капотом, это приведет к многократному копированию данных, как строитель бежит из буферного пространства и должна быть расширена. В зависимости от стратегии расширения буфера, которую использует Perl для этого, он может давать поведение O(N^2), где N - размер файла, который вы передаете. (Доказательством является то, что вы не получаете поведения O(N) ...)

Если вам нужна более быстрая загрузка, вам необходимо передать данные на стороне клиента. Прочитайте содержимое ответа в кусках и напишите их в выходной файл. (Я не эксперт по Perl, поэтому я не могу предложить вам код.) Это также уменьшит площадь памяти на стороне клиента ... что может быть важно, если ваши размеры файлов увеличиваются.

+0

Я только что отредактировал вопрос, добавив код с клиентской стороны. Не могли бы вы взглянуть на него? – MCT

+0

Спасибо, я проверю, что – MCT