【问题标题】:Slow concurrent file download with Java Spring and Tomcat使用 Java Spring 和 Tomcat 缓慢的并发文件下载
【发布时间】:2017-10-02 19:07:01
【问题描述】:

我开发了一个提供 REST 的 Spring MVC 项目。 其中一种方法返回数据的 zip 文件。如果数据包很小(小于 10MB)或者我们收到的连接很少,响应时间还可以,但是如果我有一个 20MB 的文件或一百个请求,它就会很慢。这是我的代码:

@RequestMapping(value = "{venue}/packet", method = RequestMethod.GET)
public ResponseEntity getPackage(@PathVariable("venue") int id,
        @RequestParam(value = "reqid", required = false) int reqid,
        @RequestParam(value = "version", required = false, defaultValue = "1.0") String version,
        @ModelAttribute("user") Developer user, HttpServletRequest request, HttpServletResponse resp) {

    String req_code = "[" + Thread.currentThread().getId() + "] ";
    long start = System.currentTimeMillis();

    // Check if the version of the client is already updated
    if (!version.equals(venue.getVersion())) {

        //Returns a file from File System
        File zipped = venueService.getPacket(venue);
        resp.setContentType("application/zip");
        resp.setContentLength((int) zipped.length());
        resp.setHeader("Content-Disposition", "attachment;filename=" + zipped.getName());

        try {

        InputStream is = new BufferedInputStream(new FileInputStream(zipped));
        long copytime = System.currentTimeMillis();
        packet_log.info(req_code + "Start copy req:" + reqid);

        IOUtils.copy(is, resp.getOutputStream());
        resp.flushBuffer();
        is.close();
        packet_log.info(req_code + "End copy req:" + reqid + " in " + (System.currentTimeMillis() - copytime));

        } catch (IOException e) {
        e.printStackTrace();
        }
        long dur = System.currentTimeMillis() - start;
        packet_log.info(req_code + "Send to client req:" + reqid + " in " + dur / 1000);
        packet_log.info("");
        return new ResponseEntity<Object>("", headers, HttpStatus.OK);
    }

我插入了很多日志和一个由客户端和服务器共享的“reqid”参数来监控每个请求。 当我有多个并发请求时,命令IOUtils.copy(is, resp.getOutputStream()); 变得非常慢。如果我发送 80 个请求,复制一个 20MB 的文件需要一分钟以上的时间。

我认为我的代码没有问题。也许这取决于 Spring MVC 如何管理 @RestController bean?还是访问磁盘的 I/O 问题?该系统位于 AWS EC2 虚拟机上。

【问题讨论】:

  • 对网速有什么衡量标准吗?一切都在本地主机上吗?它的慢是什么意思?现在几点了?你知不知道,100*20MB 就是 2GB,即使没有网络因素也需要一些时间?
  • 你试过copyLarge方法而不是copy
  • @AjitSoman 来自 IOUtils 文档 (commons.apache.org/proper/commons-io/javadocs/api-2.5/org/…):各种复制方法都将实际复制委托给以下方法之一:copyLarge(InputStream, OutputStream, byte[]) copyLarge(InputStream, OutputStream, long, long, byte[]) 例如 copy(InputStream, OutputStream) 调用 copyLarge(InputStream, OutputStream) 再调用 copy(InputStream, OutputStream, int)
  • @mlecz 就像我说的,系统在AWS上,时间记录在远程机器上,所以没有网络问题。我还意识到 Amazon EBS 通用 SSD 的吞吐量为 160 MB/s
  • @zuno 同样的问题,三年后。你终于找到解决办法了吗?

标签: java spring spring-mvc tomcat optimization


【解决方案1】:

您实现的是通过直接写入HttpServletResponseOutputStream来下载文件的阻塞方式。

如果您使用的是 Spring 4.2+,您可以返回一个 StreamingResponseBody,它将异步将文件字节流式传输到接收客户端。

查看postpost 了解更多信息

【讨论】:

    【解决方案2】:

    不要使用IOUtils.copy(is, resp.getOutputStream()); 将文件的输入流复制到HttpServletResponse 的输出流使用 FileSystemResource 在客户端下载您的zip 文件。

    public FileSystemResource downloadFile(HttpServletResponse response) {
        File file = new File("BASE_PATH/MY_ZIP_FILE.zip");
        response.setContentType("application/zip");      
        response.setHeader("Content-Disposition", "attachment; filename="+"MY_ZIP_FILE.zip");
        return new FileSystemResource(file); 
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多