从Amazon S3读取Servlet的速度很慢

时间:2016-07-29 10:33:06

标签: java amazon-web-services servlets amazon-s3

我需要一个servlet来从Amazon S3服务器返回文件。只有服务器具有要访问的凭据,S3存储桶不公开。我无法改变这一点。有人告诉我使用数据流,但它们太慢了。 为了测试,我有一个带有缩略图的小项目,当你点击它时,它打开一个带有完整图像的新标签。 5mb图像需要大约一分钟才能加载。那很慢。

从S3读取并返回数据流的函数:

public void downloadDirectlyFromS3(String s3Path, String fileName, HttpServletResponse response) {
    AmazonS3 s3Client = new AmazonS3Client(new ProfileCredentialsProvider());
    s3Client.setEndpoint(S3ENDPOINT);

    S3Object s3object = s3Client.getObject(new GetObjectRequest(s3Path, fileName));

    byte[] buffer = new byte[5 * 1024 * 1024];

    try {
        InputStream input = s3object.getObjectContent();
        ServletOutputStream output = response.getOutputStream();
        for (int length = 0; (length = input.read(buffer)) > 0;) {
            output.write(buffer, 0, length);
        }
        output.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

3 个答案:

答案 0 :(得分:3)

有两件事可能是引起问题的原因。

public void downloadDirectlyFromS3(String s3Path, String fileName, HttpServletResponse response) {
    AmazonS3 s3Client = new AmazonS3Client(new ProfileCredentialsProvider()); // 1. new client for each request
    s3Client.setEndpoint(S3ENDPOINT);

    S3Object s3object = s3Client.getObject(new GetObjectRequest(s3Path, fileName)); //may return null if not found

    byte[] buffer = new byte[5 * 1024 * 1024];

    try {
        InputStream input = s3object.getObjectContent(); // 2. input stream is never closed
        ServletOutputStream output = response.getOutputStream();
        for (int length = 0; (length = input.read(buffer)) > 0;) {
            output.write(buffer, 0, length);
        }
        output.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

我要做的第一个更改是为整个应用程序创建一个客户端并重用它。这可能是造成您问题的主要原因。 AWS客户端被认为是线程安全的,并且可以同时被多个请求使用。客户端处理连接池和重用,这将有助于加快多个请求的速度。

第二个更改是正确关闭资源。 input永远不会关闭,output不会因异常而关闭。考虑使用try-with-resources。

try(InputStream input = s3object.getObjectContent(); ServletOutputStream output = response.getOutputStream();) {

} catch (FileNotFoundException e) {
    e.printStackTrace(); // never thrown. s3object will be null
} catch (IOException e) {
    e.printStackTrace(); // consider using a logger for exceptions
}

此外,根据javadocs,当未找到对象时,s3object将为null,因此您不必检查FileNotFoundException。

另一个考虑因素是端点似乎是硬编码的。如果应用程序在ec2实例上运行,并且您的开发计算机配置正确,则只需使用defaultClient。

AmazonS3 s3Client = AmazonS3ClientBuilder.defaultClient();

构建器将为您查找端点。

应用程序关闭时,请考虑调用s3Client.shutdown()

有关更多信息,我发现this有用。

答案 1 :(得分:2)

答案 2 :(得分:1)

我找到了答案。问题是记录器。我们使用log4j并将其设置为debug,因此流的所有跟踪都是在控制台中编写的。 万一发生在其他人身上,这里是他们说在生产中应该避免的链接: https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/java-dg-logging.html#verbose-wire-logging

我也开始使用TransferManager作为saravanakumar v说,似乎稍快一点。

例如,我已经下载了这个ip-to-country.bin,看看调试时的示例响应

2017-05-23 12:06:21,770 Wire (Wire.java:86) DEBUG - http-outgoing-0 << "[0xf0][0x1]BGY[0xb][0x0][0x1]ITY[0xb][0x10][0x1]I"
2017-05-23 12:06:21,824 Wire (Wire.java:72) DEBUG - http-outgoing-0 << "RY[0xb] [0x1]CHY[0xb]0[0x1]DEY[0xb]@[0x1]KZY[0xb]P[0x1]ITY[0xb]`[0x1]ESY[0xb][0x80][0x1]PLY[0xb][0xa0][0x1]GEY[0xb][0xb0][0x1]TJY[0xb][0xc0][0x1]DEY[0xb][0xd0][0x1]CHY[0xb][0xe0][0x1]CZY[0xc][0x0][0x1]GBY[0xc][0x10][0x1]ESY[0xc] [0x1]RUY[0xc]0[0x1]SKY[0xc]@[0x1]RUY[0xc]P[0x1]UZY[0xc]`[0x1]RUY[0xc]p[0x1]MDY[0xc][0x80][0x1]ITY[0xc][0x90][0x1]GBY[0xc][0xa0][0x1]ITY[0xc][0xc0][0x1]UAY[0xc][0xe0][0x1]SAY[0xc][0xf0][0x1]RUY[\r][0x0][0x1]NOY[\r] [0x1]HUY[\r]0[0x1]FRY[\r]@[0x1]DEY[\r]P[0x1]ESY[\r]`[0x1]HUY[\r]p[0x1]ESY[\r][0x80][0x1]GBY[\r][0xa0][0x1]DEY[\r][0xb0][0x1]ATY[\r][0xc0][0x1]DEY[\r][0xd0][0x1]RUY[\r][0xe0][0x1]SEY[0xe][0x0][0x1]NOY[0xe][0x10][0x1]RUY[0xe] [0x1]ESY[0xe]0[0x1]RUY[0xe]@[0x1]CHY[0xe]P[0x1]NGY[0xe]`[0x1]AZY[0xe]p[0x1]DEY[0xe][0x80][0x1]GBY[0xe][0x90][0x1]DEY[0xe][0xb0][0x1]GBY[0xe][0xc0][0x1]RUY[0xe][0xd0][0x1]HRY[0xe][0xe0][0x1]ATY[0xe][0xf0][0x1]RUY[0xf][0x0][0x1]ATY[0xf][0x10][0x1]RUY[0xf] [0x1]ESY[0xf]0[0x1]RUY[0xf]@[0x1]GBY[0xf]P[0x1]FRY[0xf]`[0x1]MTY[0xf]p[0x1]GBY[0xf][0x80][0x1]RUY[0xf][0xa0][0x1]EUY[0xf][0xb0][0x1]KZY[0xf][0xc0][0x1]RUY[0xf][0xd0][0x1]ITY[0xf][0xe0][0x1]BEY[0xf][0xf0][0x1]SEY[0x10][0x0][0x1]FRY[0x10][0x10][0x1]RUY[0x10] [0x1]BEY[0x10]0[0x1]GBY[0x10]@[0x1]MKY[0x10]`[0x1]DKY[0x10]p[0x1]ATY[0x10][0x80][0x1]RSY[0x10][0x90][0x1]ESY[0x10][0xa0][0x1]DEY[0x10][0xb0][0x1]CZY[0x10][0xc0][0x1]SEY[0x10][0xd0][0x1]GBY[0x10][0xe0][0x1]CYY[0x10][0xf0][0x1]ESY[0x11][0x0][0x1]NOY[0x11][0x10][0x1]DEY[0x11] [0x1]PLY[0x11]0[0x1]BGY[0x11]@[0x1]SEY[0x11]P[0x1]LTY[0x11]`[0x1]RSY[0x11]p[0x1]RUY[0x11][0x80][0x1]NLY[0x11][0x90][0x1]TRY[0x11][0xa0][0x1]RUY[0x11]