如何恢复 iOS 中的超时操作 NSOperationQueue?

How to resume time out operations NSOperationQueue in iOS?

我已经在应用中成功实现了NSOperationQueue

我有一个操作队列,其中可能有 1000 个如下所示的 NSOperations。

@interface Operations : NSOperation

@end

@implementation Operations

- (void)main
{
    NSURL *url = [NSURL URLWithString:@"Your URL Here"];

    NSString *contentType = @"application/json";
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];
    [request addValue:contentType forHTTPHeaderField: @"Content-Type"];
    NSError *err = nil;

    NSData *body = [NSJSONSerialization dataWithJSONObject:postVars options:NSJSONWritingPrettyPrinted error:&err];

    [request setHTTPBody:body];
    [request addValue:[NSString stringWithFormat:@"%lu", (unsigned long)body.length] forHTTPHeaderField: @"Content-Length"];

     [request setTimeoutInterval:60];

     NSHTTPURLResponse *response = nil;
     NSError *error = nil;

     NSData *resData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
}

@end

现在对于该队列,我一次添加所有 1000 个操作。 我添加如下操作。

Operations *operation = [[Operations alloc]init];
[downloadQueue addOperation:operation];

现在发生的时间间隔是 60,因为 [request setTimeoutInterval:60]

所以想想 60 秒后,如果 1000 次操作中的 300 次操作完成,那么其他 700 次操作将抛出请求超时错误。

那么遇到这种情况我该怎么办

我可以恢复失败的操作吗?或者我应该再次操作并添加到队列中。

还有比这个更好的机制吗?

您可以实现NSURLConnectionDelegate方法connection:didFailWithError:来检查同步请求是否失败,如果错误是请求超时。然后在委托回调中重试连接。

我应该指出,Apple 强烈鼓励使用 NSURLSession 而不是 NSURLConnection,因为 NSURLConnection 中的许多方法现已弃用。

我最初的想法是,是的,只是为超时请求创建新操作并将它们扔回队列中。它只是感觉更简单,因为您已经有了为您的请求添加这些操作的逻辑。然而:

  1. 注意不要陷入无限循环。如果其中一个由于某种原因无限期地失败,您的队列将继续运转。我会保留一个失败计数,以便您知道在有限次数的尝试后停止重试请求。

  2. 如果您知道大量数据总是会失败,请考虑以某种方式对它们进行批处理。一种选择是依赖链。例如。您可以尝试添加 200 个操作,一个 "wait" NSOperation,接下来的 200 个,另一个 "wait" NSOperation,另一个 "wait" 操作,等等。让第一个等待操作依赖于前 200 个要求。然后让下一个 200 依赖于第一个等待操作,依此类推:

    • [第 1 批次:前 200 个请求]
    • [等待 1:等待批次 1 的所有操作]
    • [第2批:下200个,每个等待wait 1]
    • [等待 2:等待所有批次 2 的操作]
    • [第3批:下200个,每个等待wait 2]
    • 等等
  3. 基本上像 2,但不是 "wait" 操作,而是 "done" 操作。假设您有一个包含 1000 个请求的数组要发送:将 200 个放入队列,然后 "done" op 取决于这 200 个。当 "done" op 运行时(根据定义,在这 200 个完成之后),然后它可以从数组中拉出下一个 200 并将它们扔进去(加上一个新的 "done" 操作)。

(甚至可以考虑让 wait/done 操作 "pause" 几秒钟,让服务器喘口气)

换句话说,对于#2 和#3,我说的是 "blast 200 at once, wait, then blast the next 200, wait, etc."(200 对我来说是任意的,你比我更清楚什么是最适合你的情况)

第一点是——不要让他们同时运行!如果您尝试同时 运行 甚至 10 个请求,您可以轻松地淹没移动数据连接。因此,首先,将队列的 maxConcurrentOperationCount 设置为 5.

仅此一项就可能会大大减少您的问题,您可能不需要做任何其他事情。

您还应该真正考虑使用 NSURLSession,这样您还可以考虑删除操作队列并使用 HTTPMaximumConnectionsPerHost 来控制同时发出的请求数。

如果您仍然失败,那么其他答案讨论的 'recursive type' 选项之一是可行的解决方案。我会向操作子类添加一个尝试计数,以便于跟踪,如果失败,则检查它失败了多少次,并决定是否创建操作的新副本并将其添加到队列或发出警报.