429 使用 aws-sdk 为 s3 对象生成预签名 url 时请求过多
429 Too many requests when generating presigned urls for s3 objects using aws-sdk
我有一个应用程序,它是一个数字资产管理系统。它显示缩略图。我将这些缩略图设置为使用预签名 urls: https://docs.aws.amazon.com/AmazonS3/latest/dev/ShareObjectPreSignedURLJavaSDK.html 的 AWS S3 提供。这段代码一直有效,直到我更改了通过请求处理的项目数。该应用程序有 select 个离子,分别为 25、50、100、200。如果我 select 100 或 200,该过程将失败并显示 "Error: com.amazonaws.AmazonServiceException: Too Many Requests (Service: null; Status Code: 429; Error Code: null; Request ID: null)"
目前流程如下:
执行搜索 > 运行 每个对象键通过一种方法 returns 为该对象预签名 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 端可配置的解决方案。否则,现在我正在考虑为此添加一个过程以批量生成 urls。
问题与生成预签名 URLs 无关。这些都是在不与服务交互的情况下完成的,因此不可能对其进行速率限制。预签名 URL 使用 HMAC-SHA 算法向服务证明拥有凭证的实体已授权特定请求。 HMAC-SHA 的单向(不可逆)特性允许这些 URL 完全在代码为 运行 的机器上生成,无需服务交互。
但是,重复获取凭据似乎很可能是异常的实际原因——而且您似乎一遍又一遍地不必要地这样做。
这是一个昂贵的操作:
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withRegion(clientRegion)
.withCredentials(new ContainerCredentialsProvider())
.build();
每次您再次调用此方法时,都必须再次获取凭据。这实际上是您达到的极限。
仅构建您的 s3client
一次,并重构 s3SignedUrl()
以期望传入该对象,以便您可以重用它。
除了解决 429
错误之外,您应该会看到显着的性能改进。
我有一个应用程序,它是一个数字资产管理系统。它显示缩略图。我将这些缩略图设置为使用预签名 urls: https://docs.aws.amazon.com/AmazonS3/latest/dev/ShareObjectPreSignedURLJavaSDK.html 的 AWS S3 提供。这段代码一直有效,直到我更改了通过请求处理的项目数。该应用程序有 select 个离子,分别为 25、50、100、200。如果我 select 100 或 200,该过程将失败并显示 "Error: com.amazonaws.AmazonServiceException: Too Many Requests (Service: null; Status Code: 429; Error Code: null; Request ID: null)"
目前流程如下: 执行搜索 > 运行 每个对象键通过一种方法 returns 为该对象预签名 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 端可配置的解决方案。否则,现在我正在考虑为此添加一个过程以批量生成 urls。
问题与生成预签名 URLs 无关。这些都是在不与服务交互的情况下完成的,因此不可能对其进行速率限制。预签名 URL 使用 HMAC-SHA 算法向服务证明拥有凭证的实体已授权特定请求。 HMAC-SHA 的单向(不可逆)特性允许这些 URL 完全在代码为 运行 的机器上生成,无需服务交互。
但是,重复获取凭据似乎很可能是异常的实际原因——而且您似乎一遍又一遍地不必要地这样做。
这是一个昂贵的操作:
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withRegion(clientRegion)
.withCredentials(new ContainerCredentialsProvider())
.build();
每次您再次调用此方法时,都必须再次获取凭据。这实际上是您达到的极限。
仅构建您的 s3client
一次,并重构 s3SignedUrl()
以期望传入该对象,以便您可以重用它。
除了解决 429
错误之外,您应该会看到显着的性能改进。