【问题标题】:NSURLSession multiple concurrent requests refused by Exchange serverNSURLSession 多个并发请求被 Exchange 服务器拒绝
【发布时间】:2017-04-13 13:04:11
【问题描述】:

我已将 NSURLConnection 代码替换为 NSURLSession 以使用 EWS Exchange 服务器获取数据。我的应用程序进行了多个并发 API 调用。它工作正常,但现在,当我使用 NSURLSession 时,我的一些 API 调用得到正确的响应,而一些从交换服务器得到错误如下:

{
"s:Envelope" =     {
    "s:Body" =         {
        "m:GetItemResponse" =             {
            "m:ResponseMessages" =                 {
                "m:GetItemResponseMessage" =                     {
                    ResponseClass = Error;
                    "m:DescriptiveLinkKey" =                         {
                        text = 0;
                    };
                    "m:Items" =                         {
                    };
                    "m:MessageText" =                         {
                        text = "An internal server error occurred. The operation failed., Cannot open mailbox /o=First Organization/ou=Exchange Administrative Group(FYDIBOHF23SPDLT)/cn=Recipients/cn=00037FFEE6F0D3D2.";
                    };
                    "m:MessageXml" =                         {
                        "t:Value" =                             (
                                                            {
                                Name = InnerErrorMessageText;
                                text = "Too many concurrent connections opened.";
                            },
                                                            {
                                Name = InnerErrorResponseCode;
                                text = ErrorTooManyObjectsOpened;
                            },
                                                            {
                                Name = InnerErrorDescriptiveLinkKey;
                                text = 0;
                            }
                        );
                    };
                    "m:ResponseCode" =                         {
                        text = ErrorInternalServerError;
                    };
                };
            };
            "xmlns:m" = "http://schemas.microsoft.com/exchange/services/2006/messages";
            "xmlns:t" = "http://schemas.microsoft.com/exchange/services/2006/types";
        };
        "xmlns:xsd" = "http://www.w3.org/2001/XMLSchema";
        "xmlns:xsi" = "http://www.w3.org/2001/XMLSchema-instance";
    };
    "s:Header" =         {
        "h:ServerVersionInfo" =             {
            MajorBuildNumber = 1034;
            MajorVersion = 15;
            MinorBuildNumber = 11;
            MinorVersion = 1;
            xmlns = "http://schemas.microsoft.com/exchange/services/2006/types";
            "xmlns:h" = "http://schemas.microsoft.com/exchange/services/2006/types";
            "xmlns:xsd" = "http://www.w3.org/2001/XMLSchema";
            "xmlns:xsi" = "http://www.w3.org/2001/XMLSchema-instance";
        };
    };
    "xmlns:s" = "http://schemas.xmlsoap.org/soap/envelope/";
};

显然问题是同时连接太多。

我的代码流程是:

我的HTTPRequest.m有一个方法

- (void)fetchData
{
    dispatch_async(dispatch_get_main_queue(), ^{

        sessionAlive = sessionAlive + 1;

        NSLog(@"sessionCount: %ld", (long)sessionAlive);

        NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];

        NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfiguration
                                                                     delegate:self
                                                               delegateQueue:nil];

        NSURLSessionDataTask *dataTask = [defaultSession dataTaskWithRequest:request];

        [dataTask resume];
    });
}

// Some NSURLSession delegates methods here

-(void)URLSession:(NSURLSession *)session
     dataTask:(NSURLSessionDataTask *)dataTask
   didReceiveData:(NSData *)data
{
    [self.data appendData:data];
}

-(void)URLSession:(NSURLSession *)session
             task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    if (error)
    {
        self.failureBlock ? self.failureBlock(error) : nil;
    }
    else
    {
        NSData *data;

        if (self.data)
        {
            data = [NSData dataWithData:self.data];
        }

        self.successBlock ? self.successBlock(self.redirectLocation, data) : nil;
    }

    [session finishTasksAndInvalidate]; // We must release the session, else it holds strong referance for it's delegate (in our case EWSHTTPRequest).
                                        // And it wont allow the delegate object to free -> cause memory leak
}

我正在同时下载类似这样的电子邮件:

dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

dispatch_async(backgroundQueue, ^{

foreach (NSString *emailId in emalilIDArray)
    {
     HTTPRequest *request = [HTTPRequest alloc] init];
      [request fetchData];
    }
});

我认为我的问题是我在每次通话时都在进行会话,但我不知道看到其他方式。

如果我不能使用共享实例会话,因为我需要将会话的委托与每个 HTTPRequest 对象关联以处理响应。

有什么建议或更好的方法吗?

【问题讨论】:

    标签: ios concurrency exchangewebservices nsurlsession


    【解决方案1】:

    为了尽量减少架构更改,我可能会建议使用单例来管理会话,但代理对相关对象的委托调用。换句话说:

    1. 使用NSURLRequest 调用该单例类的方法并让它创建任务;也将您的 HTTPRequest 对象传递给该方法,以便它知道该调用谁来获取结果。
    2. 在单例类中,将新创建的NSURLSession[Data|Download|Upload]Task 对象作为键存储在字典中。
    3. 将您的HTTPRequest 对象存储为相应的值。
    4. 编写一大堆每个任务的委托方法,每个方法在字典中查找任务并在正确的HTTPRequest 对象上调用相同的委托方法。
    5. 任务完成后,将其从字典中删除以释放HTTPRequest 对象。

    或者,如果所有 HTTPRequest 对象真的在做的是提供一堆身份验证处理方法并积累数据,您可以考虑创建一个单例类,它接受一个 NSURLRequest 对象和一个块,然后运行它获取数据后阻塞。

    就此而言,如果您不以增量方式处理数据,您还可以考虑将该块直接作为任务的completionHandler 块传递,并让会话本身处理数据累积。

    【讨论】:

      【解决方案2】:

      您可以使用任意数量的会话,但如果您与我提供的服务器的连接过多:

      它需要在一些单例中为您的请求提供共享操作队列,但是 考虑使用 NSOperationQueue 和 NSOperations,而不是使用 GCD 的主要或全局操作队列。 NSOperationQueue 可以限制同时执行的任务数。

      var operationCount: Int 当前队列中的操作数。

      您可以限制同时连接的数量。 据我所知,dispatch_get_global_queue 没有这种可能性,因为您只是使用外部现有队列并且无法为其分配限制。

      所以不是

      dispatch_async(backgroundQueue, ^{...
      

      您可以使用相同的块创建 NSBlockOperation 并将此操作添加到 NSOperationQueue。如果您添加的任务多于“操作计数”,则只会发出“操作计数”的请求。所有其他人都将等待其中一项已执行的任务完成。

      这不是完整的代码,而是你可以尝试的想法

       NSOperationQueue *backgroundOperationQueue = [[NSOperationQueue alloc] init];
       backgroundOperationQueue.operationCount = 5;
       NSOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:yourblock];
      [backgroundOperationQueue addOperation:blockOperation];
      

      如果您更喜欢 GCD,请考虑使用串行队列,它一次只允许一个任务。

      dispatch_queue_t queue;
      queue = dispatch_queue_create("com.example.MyQueue", NULL);
      

      【讨论】:

        猜你喜欢
        • 2017-01-31
        • 1970-01-01
        • 1970-01-01
        • 2023-03-27
        • 2023-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多