gpt4 book ai didi

swift - NSURLSession 与 Alamofire 的并发请求

转载 作者:IT王子 更新时间:2023-10-29 04:57:54 24 4
gpt4 key购买 nike

我的测试应用程序出现了一些奇怪的行为。我有大约 50 个同时发送到同一台服务器的 GET 请求。该服务器是资源非常有限的一小块硬件上的嵌入式服务器。为了优化每个请求的性能,我配置了一个 Alamofire.Manager 实例,如下所示:

let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPMaximumConnectionsPerHost = 2
configuration.timeoutIntervalForRequest = 30
let manager = Alamofire.Manager(configuration: configuration)

当我使用 manager.request(...) 发送请求时,它们会成对发送 2 个(正如预期的那样,已使用 Charles HTTP 代理进行检查)。奇怪的是,所有在第一个请求后 30 秒内未完成的请求都会同时因为超时而被取消(即使它们尚未发送)。这是展示行为的插图:

concurrent request illustration

这是预期的行为吗?我如何确保请求在发送之前不会超时?

非常感谢!

最佳答案

是的,这是预期的行为。一种解决方案是将您的请求包装在自定义的异步 NSOperation 子类中,然后使用操作队列的 maxConcurrentOperationCount 来控制并发请求数而不是 HTTPMaximumConnectionsPerHost 参数。

最初的 AFNetworking 在将请求包装在操作中做得非常出色,这使得这变得微不足道。但是 AFNetworking 的 NSURLSession 实现从未这样做过,Alamofire 也没有。


您可以轻松地将 Request 包装在 NSOperation 子类中。例如:

class NetworkOperation: AsynchronousOperation {

// define properties to hold everything that you'll supply when you instantiate
// this object and will be used when the request finally starts
//
// in this example, I'll keep track of (a) URL; and (b) closure to call when request is done

private let urlString: String
private var networkOperationCompletionHandler: ((_ responseObject: Any?, _ error: Error?) -> Void)?

// we'll also keep track of the resulting request operation in case we need to cancel it later

weak var request: Alamofire.Request?

// define init method that captures all of the properties to be used when issuing the request

init(urlString: String, networkOperationCompletionHandler: ((_ responseObject: Any?, _ error: Error?) -> Void)? = nil) {
self.urlString = urlString
self.networkOperationCompletionHandler = networkOperationCompletionHandler
super.init()
}

// when the operation actually starts, this is the method that will be called

override func main() {
request = Alamofire.request(urlString, method: .get, parameters: ["foo" : "bar"])
.responseJSON { response in
// do whatever you want here; personally, I'll just all the completion handler that was passed to me in `init`

self.networkOperationCompletionHandler?(response.result.value, response.result.error)
self.networkOperationCompletionHandler = nil

// now that I'm done, complete this operation

self.completeOperation()
}
}

// we'll also support canceling the request, in case we need it

override func cancel() {
request?.cancel()
super.cancel()
}
}

然后,当我想发起 50 个请求时,我会这样做:

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2

for i in 0 ..< 50 {
let operation = NetworkOperation(urlString: "http://example.com/request.php?value=\(i)") { responseObject, error in
guard let responseObject = responseObject else {
// handle error here

print("failed: \(error?.localizedDescription ?? "Unknown error")")
return
}

// update UI to reflect the `responseObject` finished successfully

print("responseObject=\(responseObject)")
}
queue.addOperation(operation)
}

这样,这些请求将受到 maxConcurrentOperationCount 的限制,我们不必担心任何请求超时。

这是一个示例 AsynchronousOperation 基类,它负责与异步/并发 NSOperation 子类关联的 KVN:

//
// AsynchronousOperation.swift
//
// Created by Robert Ryan on 9/20/14.
// Copyright (c) 2014 Robert Ryan. All rights reserved.
//

import Foundation

/// Asynchronous Operation base class
///
/// This class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer
/// a concurrent NSOperation subclass, you instead subclass this class which:
///
/// - must override `main()` with the tasks that initiate the asynchronous task;
///
/// - must call `completeOperation()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
/// necessary and then ensuring that `completeOperation()` is called; or
/// override `cancel` method, calling `super.cancel()` and then cleaning-up
/// and ensuring `completeOperation()` is called.

public class AsynchronousOperation : Operation {

private let stateLock = NSLock()

private var _executing: Bool = false
override private(set) public var isExecuting: Bool {
get {
return stateLock.withCriticalScope { _executing }
}
set {
willChangeValue(forKey: "isExecuting")
stateLock.withCriticalScope { _executing = newValue }
didChangeValue(forKey: "isExecuting")
}
}

private var _finished: Bool = false
override private(set) public var isFinished: Bool {
get {
return stateLock.withCriticalScope { _finished }
}
set {
willChangeValue(forKey: "isFinished")
stateLock.withCriticalScope { _finished = newValue }
didChangeValue(forKey: "isFinished")
}
}

/// Complete the operation
///
/// This will result in the appropriate KVN of isFinished and isExecuting

public func completeOperation() {
if isExecuting {
isExecuting = false
}

if !isFinished {
isFinished = true
}
}

override public func start() {
if isCancelled {
isFinished = true
return
}

isExecuting = true

main()
}

override public func main() {
fatalError("subclasses must override `main`")
}
}

/*
Abstract:
An extension to `NSLocking` to simplify executing critical code.

Adapted from Advanced NSOperations sample code in WWDC 2015 https://developer.apple.com/videos/play/wwdc2015/226/
Adapted from https://developer.apple.com/sample-code/wwdc/2015/downloads/Advanced-NSOperations.zip
*/

import Foundation

extension NSLocking {

/// Perform closure within lock.
///
/// An extension to `NSLocking` to simplify executing critical code.
///
/// - parameter block: The closure to be performed.

func withCriticalScope<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}

此模式还有其他可能的变体,但只要确保您 (a) 为 asynchronous 返回 true; (b) 您按照 Concurrency Programming Guide: Operation Queues为并发执行配置操作 部分所述发布必要的isFinishedisExecuting KVN| .

关于swift - NSURLSession 与 Alamofire 的并发请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27021896/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com