【问题标题】:Large File Download causing GC overhead limit exceeded大文件下载导致超出 GC 开销限制
【发布时间】:2019-04-19 19:26:19
【问题描述】:

我有两个服务,第一个是 frontend_service,第二个是 backend_service,我从 backend_service 获取大文件并尝试通过 frontend_service 转发给用户 response.getBodyAsStream() 但这会导致 frontend_service 中出现“java.lang.OutOfMemoryError:GC 开销限制超出”。

后端服务代码:

`

public static Result downloadLargeFile(String filePath){
   File file = new File(filePath);
   InputStream inputStream = new FileInputStream(file);
   return ok(inputStream);
}

`

前端服务代码:

`

  public static F.Promise<Result> downloadLargeFile(String filePath) {
       //this will call backend_service downloadLargeFile method.
       String backEndUrl = getBackEndUrl(filePath);
       return getInputStream(backEndUrl);
    }

`

`

public static Promise<Result> getInputStream(String url) {
            return WS.url(url).get().map(
                    response -> {
                        InputStream inputStream =  response.getBodyAsStream();
                        return ok(inputStream);
                    }
            );
}

`

我尝试了here 建议的解决方案,方法是从 inputStream 一次读取几个字节并在 frontend_service 中创建 tmp 文件并将 tmp 文件作为 frontend_service 的输出发送。

`

    public static Promise<Result> getInputStream(String url) {
            return WS.url(url).get().map(
                    response -> {
                        InputStream inputStream = null;
                        OutputStream outputStream = null;
                        try {
                            inputStream =  response.getBodyAsStream();
                            //write input stream to tmp file
                            final File tmpFile = new File("/tmp/tmp.txt");
                            outputStream = new FileOutputStream(tmpFile);

                            int read = 0;
                            byte[] buffer = new byte[500];
                            while((read = inputStream.read(buffer)) != -1){
                                outputStream.write(buffer, 0 , read);
                            }
                            return ok(tmpFile);
                        } catch (IOException e) {
                            e.printStackTrace();
                            return badRequest();
                        } finally {
                            if (inputStream != null) {inputStream.close();}
                            if (outputStream != null) {outputStream.close();}
                        }
                    }
            );

`

上面的代码也抛出了 java.lang.OutOfMemoryError。我正在尝试 1 GB 的文件。

【问题讨论】:

  • reading few bytes at a time from inputStream 是我能想到的唯一解决方案。你能发布一下出了什么问题吗?
  • 我编辑了问题。
  • 我怀疑是return ok(tmpFile); 导致了这个错误。你可以返回其他东西来测试这个理论吗? (可能是文件系统中的较小文件)。
  • 感谢您的建议。我试过ok("test") 但同样的错误(java.lang.OutOfMemoryError)。看起来 inputStream 正在等待保存整个数据,而不是移动到下一个执行并将数据提供给 outputStream 以进行写入。

标签: java playframework playframework-2.3


【解决方案1】:

我没有“手下”的实现,所以我会写算法。

1。 Play 在WS 下使用AsyncHttpClient。你需要得到它,或者按照https://www.playframework.com/documentation/2.3.x/JavaWS#Using-WSClient中的描述创建它

2。然后,你需要实现AsyncCompletionHandler,就像在类https://static.javadoc.io/org.asynchttpclient/async-http-client/2.0.0/org/asynchttpclient/AsyncHttpClient.html的描述中一样

3。在AsyncCompletionHandler类的onBodyPartReceived方法中,需要将body部分推送到分块播放响应。此处描述的更改回复:https://www.playframework.com/documentation/2.3.x/JavaStream#Chunked-responses

附:

类似解决方案的讨论,但方向相反 - 通过“前端”(播放 2)服务流式上传到“后端”(亚马逊)服务: https://groups.google.com/d/msg/asynchttpclient/EpNKLSG9ymM/BAGvwl0Wby8J

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-20
    • 2011-05-21
    • 2017-02-24
    • 2017-12-27
    • 2013-07-13
    • 2018-03-29
    • 2012-04-02
    相关资源
    最近更新 更多