13

许多(如果不是大多数)Web 服务对客户端都有速率限制。Delicious 表示客户端每秒可以发出一个请求;Twitter 对每个端点都有限制;我确信 Facebook、Flickr 和 Foursquare 都有自己的想法。

您可以使用NSOperationQueue.

但是,您如何限制应用程序每秒仅发出一个请求?

我查看了 Apple、AFNetworking、ASINetwork 和其他一些示例代码,但似乎没有一个可以解决这个问题。这对我来说似乎很奇怪。我承认我可能遗漏了一些非常明显的东西......

一些参数:

  • 假设我有一个NSOperationQueue网络操作并且请求是一个NSOperation(我想也可能是一个 GCD 队列,但这是我一直在使用的)
  • 队列中的每个请求使用相同的速率限制
  • 我正在寻找 iOS 中的解决方案,但一般的想法可能会有用

可能的解决方案:

  • sleep中的语句NSOperation(它是一个队列/线程,所以这不会阻塞其他任何东西)
  • NSTimer在里面NSOperation
  • performSelector:NSOperation我修补了 ASINetworking 以使用这种方法,尽管我没有使用它并且没有将更改推送到上游)
  • 启动/停止队列(使用 KVO?)以确保不超过速率限制
  • 特殊的“睡眠” NSOperation。这将是下一个网络操作将依赖的任务
  • 完全忽略速率限制,当您收到“超出速率限制”错误响应时暂停一下

这些看起来都很乱。休眠的操作可能会阻止“优先级”队列的形式。启动/停止队列似乎很脆弱。无视限制是粗鲁的。

需要明确的是,我已经解决了这个问题。但解决方案似乎“凌乱”且有些脆弱。我想知道是否有更好、更清洁的选择。

想法?

4

4 回答 4

6
@implementation SomeNSOperationSubClass {
    BOOL complete;
    BOOL stopRunLoop;
    NSThread *myThread;
}

-(void) rateLimitMonitor:(NSTimer *)theTimer {
    [theTimer invalidate];
}

-(void) main {
    myThread = [NSThread currentThread];

    NSTimer *myTimer = [NSTimer timerWithTimeInterval:1 target:self  selector:@selector(rateLimitMonitor:) userInfo:nil repeats:NO];
    [[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSDefaultRunLoopMode];

    [self doAsyncThing];

    while ((!stopRunLoop || [myTimer isValid]) && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
    complete = YES;
}

-(void) internalComplete {
    stopRunLoop = YES;
}

-(void) setComplete {
    [self performSelector:@selector(internalComplete) onThread:myThread withObject:nil waitUntilDone:NO];
}

-(BOOL) isFinished {
    return complete;
}

@end

在你的异步回调中

    [myNSOperationSubClass setComplete];
于 2012-11-05T13:41:27.890 回答
2

iOS 13 中的新增功能是内置的。通过 Combine 框架的debounce操作符传递您的通信触发器,一切就绪。

于 2019-10-25T23:40:01.923 回答
0

这是对 Edwin 提供的几乎可行的解决方案的修复

- (void)main {
    for (double delay = 0.0; delay < 10.0; delay+=1) {
        [self networkCallWithDelay:delay];
    }
}

- (void)networkCallWithDelay:(double)delay {

      dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

    // Your asynchronous network call goes here
    });
}
于 2019-10-11T12:10:55.157 回答
-1

可能的解决方案,假设您已经使用 NSOperation 以外的其他技术实现了网络模块。

使用 GCD 来解决这个问题。以下代码为每个网络调用引入了 1 秒的延迟。密切关注参数为popTime

- (void)main {

        for (NSInteger index = 0; index < 10; index++) {
            [self networkCallWithDelay:1*index];
        }
    }

// 你的网络代码放在这里

- (void)networkCallWithDelay:(double)delay {

    double delayInSeconds = delay / 10.0f;

    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

        // Your asynchronous network call goes here
    });
}
于 2016-03-31T22:48:59.337 回答