首页 > 解决方案 > 大文件下载导致超出 GC 开销限制

问题描述

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

backend_service 的代码:

`

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

`

frontend_service 的代码:

`

  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);
                    }
            );
}

`

我通过从 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 文件。

标签: javaplayframeworkplayframework-2.3

解决方案


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

1. PlayAsyncHttpClient使用WS. 您需要获取它,或者按照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、在类的onBodyPartReceived方法中AsyncCompletionHandler,需要将body部分推送到chunked play响应中。此处描述的更改响应:https ://www.playframework.com/documentation/2.3.x/JavaStream#Chunked-responses

附言

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


推荐阅读