首页 > 解决方案 > 429 使用 aws-sdk 为 s3 对象生成预签名 URL 时请求过多

问题描述

我有一个应用程序,它是一个数字资产管理系统。它显示缩略图。我将这些缩略图设置为与 AWS S3 预签名 URL 一起提供:https ://docs.aws.amazon.com/AmazonS3/latest/dev/ShareObjectPreSignedURLJavaSDK.html 。这段代码正在工作,直到我更改通过请求处理的项目数量。应用程序有 25、50、100、200 的选项。如果我选​​择 100 或 200,进程将失败并显示“错误:com.amazonaws.AmazonServiceException:请求过多(服务:null;状态代码:429;错误代码:null ; 请求 ID: null)"

现在的过程如下:执行搜索 > 通过返回该对象的预签名 url 的方法运行每个对象键。

我们通过 Elastic Container Service 运行这个应用程序,它允许我们通过 ContainerCredentialsProvider 提取凭证。

相关代码供审核:

String s3SignedUrl(String objectKeyUrl) {
    // Environment variables for S3 client.
    String clientRegion = System.getenv("REGION");
    String bucketName = System.getenv("S3_BUCKET");

    try {
        // S3 credentials get pulled in from AWS via ContainerCredentialsProvider.
        AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
                .withRegion(clientRegion)
                .withCredentials(new ContainerCredentialsProvider())
                .build();

        // Set the pre-signed URL to expire after one hour.
        java.util.Date expiration = new java.util.Date();
        long expTimeMillis = expiration.getTime();
        expTimeMillis += 1000 * 60 * 60;
        expiration.setTime(expTimeMillis);

        // Generate the presigned URL.
        GeneratePresignedUrlRequest generatePresignedUrlRequest =
                new GeneratePresignedUrlRequest(bucketName, objectKeyUrl)
                        .withMethod(HttpMethod.GET)
                        .withExpiration(expiration);

        return s3Client.generatePresignedUrl(generatePresignedUrlRequest).toString();

    } catch (AmazonServiceException e) {
        throw new AssetException(FAILED_TO_GET_METADATA, "The call was transmitted successfully, but Amazon " +
                "S3 couldn't process it, so it returned an error response. Error: " + e);
    } catch (SdkClientException e) {
        throw new AssetException(FAILED_TO_GET_METADATA, "Amazon S3 couldn't be contacted for a response, or " +
                "the client couldn't parse the response from Amazon S3. Error: " + e);
    }
}

这是我们处理项目的部分:

// Overwrite the url, it's nested deeply in maps of maps.
    for (Object anAssetList : assetList) {
        String assetId = ((Map) anAssetList).get("asset_id").toString();
        if (renditionAssetRecordMap.containsKey(assetId)) {
            String s3ObjectKey = renditionAssetRecordMap.get(assetId).getThumbObjectLocation();
            ((Map) ((Map) ((Map) anAssetList)
                    .getOrDefault("rendition_content", new HashMap<>()))
                    .getOrDefault("thumbnail_content", new HashMap<>()))
                    .put("url", s3SignedUrl(s3ObjectKey));
        }
    }

任何指导将不胜感激。希望有一个简单且有望在 AWS 端可配置的解决方案。否则,现在我正在考虑为此添加一个过程以批量生成 url。

标签: javaamazon-web-servicesamazon-s3aws-sdkhttp-status-code-429

解决方案


该问题与生成预签名 URL 无关。这些都是在不与服务交互的情况下完成的,因此不可能对它进行速率限制。预签名 URL 使用 HMAC-SHA 算法向服务证明拥有凭证的实体已授权特定请求。HMAC-SHA 的单向(不可逆)特性允许这些 URL 完全在运行代码的机器上生成,无需服务交互。

但是,重复获取凭据似乎很可能是异常的实际原因——而且您似乎一遍又一遍地不必要地这样做。

这是一个昂贵的操作:

    AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
            .withRegion(clientRegion)
            .withCredentials(new ContainerCredentialsProvider())
            .build();

每次您再次调用它时,都必须再次获取凭据。这实际上是你达到的极限。

只构建s3client一次,然后重构s3SignedUrl()以期望传入该对象,以便您可以重用它。

除了解决429错误之外,您还应该看到显着的性能改进。


推荐阅读