2017-02-09 308 views
0

我正在为一个LMS功能工作,以便即时下载一堆选定的文件和文件夹。我使用ZipOutputStream来防止OutOfMemory问题。ZipOutputStream的并发使用使用100%的CPU

该功能很好,但我们做了压力测试,当几个用户同时下载zip文件(可以说10个用户每个压缩大约100MB),4个CPU中有4个达到100%的负载直到拉链被创建。我们的系统管理员认为这是不可接受的。

我不知道是否有一些机制可以做ZipOutputStream使用较少的系统资源,无论是否需要更多时间才能完成。

我当前的代码:

protected void compressResource(ZipOutputStream zipOut, String collectionId, String rootFolderName, String resourceId) throws Exception 
{ 
    if (ContentHostingService.isCollection(resourceId)) 
    { 
     try 
     { 
      ContentCollection collection = ContentHostingService.getCollection(resourceId); 
      List<String> children = collection.getMembers(); 
      if(children != null) 
      { 
       for(int i = children.size() - 1; i >= 0; i--) 
       { 
        String child = children.get(i); 
        compressResource(zipOut,collectionId,rootFolderName,child); 
       } 
      } 
     } 
     catch (PermissionException e) 
     { 
      //Ignore 
     } 
    } 
    else 
    { 
     try 
     { 
      ContentResource resource = ContentHostingService.getResource(resourceId); 
      String displayName = isolateName(resource.getId()); 
      displayName = escapeInvalidCharsEntry(displayName); 

      InputStream content = resource.streamContent(); 
      byte data[] = new byte[1024 * 10]; 
      BufferedInputStream bContent = null; 

      try 
      { 
       bContent = new BufferedInputStream(content, data.length); 

       String entryName = (resource.getContainingCollection().getId() + displayName); 
       entryName=entryName.replace(collectionId,rootFolderName+"/"); 
       entryName = escapeInvalidCharsEntry(entryName); 

       ZipEntry resourceEntry = new ZipEntry(entryName); 
       zipOut.putNextEntry(resourceEntry); //A duplicate entry throw ZipException here. 
       int bCount = -1; 
       while ((bCount = bContent.read(data, 0, data.length)) != -1) 
       { 
        zipOut.write(data, 0, bCount); 
       } 

       try 
       { 
        zipOut.closeEntry(); 
       } 
       catch (IOException ioException) 
       { 
        logger.error("IOException when closing zip file entry",ioException); 
       } 
      } 
      catch (IllegalArgumentException iException) 
      { 
       logger.error("IllegalArgumentException while creating zip file",iException); 
      } 
      catch (java.util.zip.ZipException e) 
      { 
       //Duplicate entry: ignore and continue. 
       try 
       { 
        zipOut.closeEntry(); 
       } 
       catch (IOException ioException) 
       { 
        logger.error("IOException when closing zip file entry",ioException); 
       } 
      } 
      finally 
      { 
       if (bContent != null) 
       { 
        try 
        { 
         bContent.close(); 
        } 
        catch (IOException ioException) 
        { 
         logger.error("IOException when closing zip file",ioException); 
        } 
       } 
      } 
     } 
     catch (PermissionException e) 
     { 
      //Ignore 
     } 
    } 
} 

在此先感谢。

+3

您可以使用信号量来限制并发用户的数量。 – shmosel

+1

不允许同时发生这么多的并发zip进程。使用执行程序执行ziptask,并且可以调整用于它们的线程数量。 – Kayaman

+0

考虑到您同时控制了读取和写入过程,'ZipOutputStream'与您的问题没有关系,您可以改为放置'OutputStream',任务不会改变。基本上你的问题类似于[这一个](http://stackoverflow.com/questions/667508/whats-a-good-rate-limiting-algorithm)。 – user3707125

回答

0

我用@shmosel告诉我一个简单的黑客解决了它。

private static Semaphore mySemaphore= new Semaphore(ServerConfigurationService.getInt("content.zip.download.maxconcurrentdownloads",5),true); 

(...) 

ZipOutputStream zipOut = null; 
    try 
    { 
     mySemaphore.acquire(); 
     ContentCollection collection = ContentHostingService.getCollection(collectionId); 

(...) 

zipOut.flush(); 
zipOut.close(); 
mySemaphore.release(); 

(...) 

这是在我的测试服务器上工作。但如果有人有任何异议或任何额外的建议,我会很乐意听到。