当 API returns "too many requests" 错误时,在 C# 中多次重试同一任务

Retry the same task multiple times in C# when API returns "too many requests" error

我有一个在同步上下文中通过重试执行异步任务的通用方法。

public static T RunWithRetries<T>(Task<T> task)
{
   while(...) // 3 attempts
   {
       try 
       {
          task.GetAwaiter().GetResult();
       }
       catch 
       {
         // sleep & retry later in case of some exceptions, for example 429
       }
   }
}

然后我将任何方法从异步 API 传递到 运行,就像这样。

SyncHelper.RunWithRetries(externalAPI.UploadAsync(fileRequest, fileStream));

问题是除非在请求期间发生异常并且我们需要重试,否则它可以正常工作。如果发生错误,所有后续重试也会抛出相同的异常。所以,我的问题是

  1. 发生这种情况是因为 fileStream 对象吗?它在 using 语句中,所以它肯定不会被处理掉。第一次上传尝试后的流位置会不会有问题?
  2. 重试同一个Task对象是否正常?我应该改变我做事的方式吗?

Is this happening because of the fileStream object? It's in the using statement, so it's not being disposed for sure. Can the stream position after first upload attempt be a problem?

是的,这是你的问题之一。每当读取流时,其 Position 不会自动重置为 0。如果您尝试重新读取它,那么它不会读取任何内容,因为该位置位于流的末尾。

因此,您必须每次都创建一个新流,或者 rewind the stream to the beginning

Is it normal that the same Task object is being retried? Should I change the way I'm doing it to something better?

只要任务完成(有特定结果或异常),然后重新await-ing 或检索它的Result 将不会触发重新执行。它将简化 return 值或异常。

因此,您必须为每次重试尝试创建一个新任务。为此,您可以在 RunWithRetries

中预期 Func<Task<T>>
public static T RunWithRetries<T>(Func<Task<T>> issueRequest)
{
    ...
    issueRequest().GetAwaiter().GetResult();

}

从调用方来看,它看起来像这样:

RunWithRetries(() => externalAPI.UploadAsync(fileRequest, new FileStream(...)));
//or
RunWithRetries(() => { fileStream.Position = 0; externalAPI.UploadAsync(fileRequest, fileStream); });