Фон
Я пытаюсь использовать S3 в качестве большого слоя кэширования «бесконечность» для некоторых «довольно» статичных XML-документов.Я хочу убедиться, что клиентское приложение (которое будет работать на тысячах машин одновременно и запрашивать XML-документы много раз в час) загружает эти XML-документы только в том случае, если их содержимое изменилось с момента последней загрузки их клиентским приложением.
Подход
В Amazon S3 мы можем использовать для этого HTTP ETAG.По умолчанию для объектов Amazon S3 ETAG установлен в хэш MD5 объекта.
Затем мы можем указать MD5-хэш XML-документа в свойстве GetObjectRequest.ETagToNotMatch
.Это гарантирует, что когда мы делаем вызов AmazonS3.GetObject
(или в моем случае асинхронную версию AmazonS3.BeginGetObject
и AmazonS3.EndGetObject
), что если запрашиваемый документ имеет тот же хэш MD5, который содержится в GetObjectRequest.ETagToNotMatch
, тогда S3 автоматическивозвращает HTTP-код состояния 304 (NotModified), а фактическое содержимое XML-документа не загружено.
Проблема
Проблема, однако, заключается вчто при вызове AmazonS3.GetObject
(или его асинхронном эквиваленте) API Amazon .Net фактически видит код состояния HTTP 304 (NotModified) как ошибку и повторяет запрос get три раза, а затем, наконец, выдает Amazon.S3.AmazonS3Exception: Maximum number of retry attempts reached : 3
.
Очевидно, что я мог бы изменить эту реализацию на использование AmazonS3.GetObjectMetaData
, а затем сравнить ETAG и использовать AmazonS3.GetObject
, если они не совпадают, но тогда есть два запроса к S3 вместо одного, когда файл устарел.Я бы предпочел иметь один запрос независимо от того, нужно ли загружать документ XML или нет.
Есть идеи?Это ошибка или я что-то упустил?Есть ли какой-нибудь способ, которым я могу уменьшить количество повторных попыток до одного и «обработать» исключение (хотя я чувствую себя «противно» по этому маршруту).
Реализация
Я использую AWS SDK для .NET (версия 1.3.14).
Вот моя реализация (немного уменьшена, чтобы она была короче):
public Task<GetObjectResponse> DownloadString(string key, string etag = null) {
var request = new GetObjectRequest { Key = key, BucketName = Bucket };
if (etag != null) {
request.ETagToNotMatch = etag;
}
var task = Task<GetObjectResponse>.Factory.FromAsync(_s3Client.BeginGetObject, _s3Client.EndGetObject, request, null);
return task;
}
Затем я вызываю этонапример:
var dlTask = s3Manager.DownloadString("new one", "d7db7bc318d6eb9222d728747879b52e");
var responseTasks = new[]
{
dlTask.ContinueWith(x => _log.Error("Error downloading string.", x.Exception), TaskContinuationOptions.OnlyOnFaulted),
dlTask.ContinueWith(x => _log.Warn("Downloading string was cancelled."), TaskContinuationOptions.OnlyOnCanceled),
dlTask.ContinueWith(x => _log.Info(string.Format("Done with download: {0}", x.Result.ETag)), TaskContinuationOptions.OnlyOnRanToCompletion)
};
try {
Task.WaitAny(responseTasks);
} catch (AggregateException aex) {
_log.Error("Error while processing download string.", aex);
}
_log.Info("Exiting...");
Затем выдается такой файл журнала:
2011-10-11 13:21:20,376 [11] INFO Amazon.S3.AmazonS3Client - Received response for GetObject (id 2ee99002-d148-4572-b19b-29259534f48f) with status code NotModified in 00:00:01.6140812.
2011-10-11 13:21:20,385 [11] INFO Amazon.S3.AmazonS3Client - Request for GetObject is being redirect to https://s3.amazonaws.com/x/new%20one.
2011-10-11 13:21:20,789 [11] INFO Amazon.S3.AmazonS3Client - Retry number 1 for request GetObject.
2011-10-11 13:21:22,329 [11] INFO Amazon.S3.AmazonS3Client - Received response for GetObject (id 2ee99002-d148-4572-b19b-29259534f48f) with status code NotModified in 00:00:01.1400356.
2011-10-11 13:21:22,329 [11] INFO Amazon.S3.AmazonS3Client - Request for GetObject is being redirect to https://s3.amazonaws.com/x/new%20one.
2011-10-11 13:21:23,929 [11] INFO Amazon.S3.AmazonS3Client - Retry number 2 for request GetObject.
2011-10-11 13:21:26,508 [11] INFO Amazon.S3.AmazonS3Client - Received response for GetObject (id 2ee99002-d148-4572-b19b-29259534f48f) with status code NotModified in 00:00:00.9790314.
2011-10-11 13:21:26,508 [11] INFO Amazon.S3.AmazonS3Client - Request for GetObject is being redirect to https://s3.amazonaws.com/x/new%20one.
2011-10-11 13:21:32,908 [11] INFO Amazon.S3.AmazonS3Client - Retry number 3 for request GetObject.
2011-10-11 13:21:40,604 [11] INFO Amazon.S3.AmazonS3Client - Received response for GetObject (id 2ee99002-d148-4572-b19b-29259534f48f) with status code NotModified in 00:00:01.2950718.
2011-10-11 13:21:40,605 [11] INFO Amazon.S3.AmazonS3Client - Request for GetObject is being redirect to https://s3.amazonaws.com/x/new%20one.
2011-10-11 13:21:40,621 [11] ERROR Amazon.S3.AmazonS3Client - Error for GetResponse
Amazon.S3.AmazonS3Exception: Maximum number of retry attempts reached : 3
at Amazon.S3.AmazonS3Client.pauseOnRetry(Int32 retries, Int32 maxRetries, HttpStatusCode status, String requestAddr, WebHeaderCollection headers, Exception cause)
at Amazon.S3.AmazonS3Client.handleHttpResponse[T](S3Request userRequest, HttpWebRequest request, HttpWebResponse httpResponse, Int32 retries, TimeSpan lengthOfRequest, T& response, Exception& cause, HttpStatusCode& statusCode)
at Amazon.S3.AmazonS3Client.getResponseCallback[T](IAsyncResult result)
2011-10-11 13:21:40,635 [10] INFO Example.Program - Exiting...
2011-10-11 13:21:40,638 [19] ERROR Example.Program - Error downloading string.
System.AggregateException: One or more errors occurred. ---> Amazon.S3.AmazonS3Exception: Maximum number of retry attempts reached : 3
at Amazon.S3.AmazonS3Client.pauseOnRetry(Int32 retries, Int32 maxRetries, HttpStatusCode status, String requestAddr, WebHeaderCollection headers, Exception cause)
at Amazon.S3.AmazonS3Client.handleHttpResponse[T](S3Request userRequest, HttpWebRequest request, HttpWebResponse httpResponse, Int32 retries, TimeSpan lengthOfRequest, T& response, Exception& cause, HttpStatusCode& statusCode)
at Amazon.S3.AmazonS3Client.getResponseCallback[T](IAsyncResult result)
at Amazon.S3.AmazonS3Client.endOperation[T](IAsyncResult result)
at Amazon.S3.AmazonS3Client.EndGetObject(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endMethod, TaskCompletionSource`1 tcs)
--- End of inner exception stack trace ---
---> (Inner Exception #0) Amazon.S3.AmazonS3Exception: Maximum number of retry attempts reached : 3
at Amazon.S3.AmazonS3Client.pauseOnRetry(Int32 retries, Int32 maxRetries, HttpStatusCode status, String requestAddr, WebHeaderCollection headers, Exception cause)
at Amazon.S3.AmazonS3Client.handleHttpResponse[T](S3Request userRequest, HttpWebRequest request, HttpWebResponse httpResponse, Int32 retries, TimeSpan lengthOfRequest, T& response, Exception& cause, HttpStatusCode& statusCode)
at Amazon.S3.AmazonS3Client.getResponseCallback[T](IAsyncResult result)
at Amazon.S3.AmazonS3Client.endOperation[T](IAsyncResult result)
at Amazon.S3.AmazonS3Client.EndGetObject(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endMethod, TaskCompletionSource`1 tcs)<---