AFNetworking请求操作 - 创建一个漏斗/瓶颈队列单例

时间:2014-11-01 12:10:56

标签: ios objective-c ios7 afnetworking nsoperation

设置

我的程序中有两个区域 - branch 1branch 2 - 其中网络请求是异步进行的,每个区域一次有1个并发的GET请求。请求每次发送1个,因为服务器对任何向服务器发出请求的用户都有几毫秒的宽限期。一次运行1个并发请求旨在帮助宽限期。

已经进行了操作,如果任何分支中的任何请求失败 - 再次重复请求就有失败的安全性。

问题:

当我彼此分开运行这些分支时,即不是同时运行时,服务器很高兴。但是,只要我允许两个操作同时发生,服务器就会抛出429错误,这是错误,让用户知道there are too many requests coming in at any one time。然后发生的是,一半的请求失败,然后由于故障安全,请求再次被发送以进行处理,直到它们全部完成。 代表用户的数据包浪费时间和资源。

方案

Branch 1发出10个请求(通常是数千个请求,一次处理一个,但为了简单起见,我们将其保持在10)。 这10个请求将很快同时处理1个请求,并返回适当的数据对象。

现在,如果我们再次运行branch 1操作 - 同时发出10个请求,然后添加branch 2's个10个请求,这将创建20个请求操作 - 两倍 - 前往服务器,这将导致大约一半来自分支1和2的请求失败,然后启动故障保护以重新发送这些请求操作。号泣。

问题

是否有办法将这些请求操作从我的应用程序中流出,无论这两个分支位于我的应用程序中的哪个位置,这样我们就可以控制和瓶颈请求操作到服务器,以便服务器赢得'只有一半的请求失败才会受到轰炸?

可能的解决方案

我的一个想法就是创建一个单独的单例,它可以作为一个队列以及所需的瓶颈,这将允许我在我的代码库中的任何地方添加请求操作 - 因此我认为单身人士会是这是一个好主意 - 然后一旦它意识到一个请求操作已被添加到队列中,然后让该队列在时间运行1个并发请求。

我认为这对我的目的很有用,我只是不知道如何处理请求的完成块,因为来自Branch 1的请求操作与请求操作的完成块不同来自Branch 2

你会如何管理?我们能做些什么吗?


编辑1 - 我目前正在做什么

在分支1中 - 使用AFHTTPOperation:

1)对于每个进程,我创建一个for循环,迭代10次,将10 AFHTTPRequestOperation s添加到名为multipleOperations类型为NSMutableArray的本地数组中。

2)然后我将multipleOperations数组添加到batchOfRequestOperations方法,如下所示:

NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:muiltipleOperations
                       progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations){
//Do some stuff here for each iteration
} 
completionBlock:^(NSArray*operations){
//Call the method that repeats this process again.
}];

3)然后我将操作添加到mainQueue中,如此

[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];

我现在想知道第一分支中的这个过程是否已将其设置为1个并发,无法看到它是:\

在分支2中使用NSURLSessionDataTask(刚刚意识到,如果它们都使用相同的子类,那么它会更容易吗?lol

我注意到我正在使用创建AFHTTPSessionManager的{​​{1}} ..它不会在for循环中运行,而是调用自己的方法,以便在请求执行时每次发送另一个请求自己的完成块。因此,只要分支2中的先前请求完成,它将永远保持自我调用。这就是我知道它同时只运行1个请求的方式。那很好。

有没有让它更清洁?

我在哪里使用NSURLSessionDataTasks :(有人可以告诉我如何正确地完成工作吗?

谢谢。

1 个答案:

答案 0 :(得分:1)

根据您的编辑,我可以看到您可能想要更改的一些内容。首先,是的,如果您使用AFHTTPOperation s或NSURLSessionDataTask s,它将更容易 ,但不能同时使用两者。

如果您要使用AFHTTPOperation,请不要对[NSOperationQueue mainQueue]上的操作进行排队。原因是,这是主要的UI队列,您不希望您的网络操作阻止它。你应该做的是创建自己的NSOperationQueue并将其并发性设置为1。

NSOperationQueue *networkQueue = [[NSOperationQueue alloc] init];
networkQueue.maxConcurrentOperationCount = 1;

使用单身人士的想法将该队列存储在您可以从应用程序中的任何位置访问的地方,这是确保通过同一队列发送所有网络操作的好方法。

话虽如此,我建议NSURLSessionDataTask改为使用AFHTTPSessionManager 。 AFNetworking的文档说明:

  

针对iOS 7或Mac OS X 10.9或更高版本的开发人员   我们鼓励广泛使用Web服务进行子类化   AFHTTPSessionManager,提供返回共享的类方法   身份验证和其他配置可以使用的单一对象   在整个应用程序中共享。

这听起来像是一个完成你想要的好方法。基本上,您将使用类方法创建AFHTTPSessionManager的子类,该方法创建使用自定义NSURLSessionConfiguration初始化的实例。您可以将NSURLSessionConfiguration配置为适合您的应用,但您对此特定问题感兴趣的主要属性是HTTPMaximumConnectionsPerHost

  

此属性确定最大同时数   基于此的会话中的任务与每个主机建立的连接   配置。

设置为1后,您可以让AFHTTPSessionManager(实际上,NSURLSession)担心确保在任何给定时间只向您的服务器发出一个请求。

这些方面的东西:

@interface AppHTTPSessionManager : AFHTTPSessionManager

+ (instancetype)appSession;

@end

@implementation AppHTTPSessionManager

+ (instancetype)appSession {
    static AppHTTPSessionManager *_appSession = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        configuration.HTTPMaximumConnectionsPerHost = 1;
        _appSession = [[AppHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
    });
    return _appSession;
}

@end

然后,每当您想要进行网络通话时,请务必从NSURLSessionDataTask创建[AppHTTPSessionManager appSession]。如果这样做,那些任务应该自动限制为一次向服务器发出一个请求。