Alamofire를 사용한 NSURLSession 동시 요청
테스트 앱에서 이상한 동작이 발생합니다. 동일한 서버에 보내는 동시 GET 요청이 약 50 개 있습니다. 서버는 리소스가 매우 제한된 작은 하드웨어에 내장 된 서버입니다. 각 단일 요청의 성능을 최적화하기 위해 Alamofire.Manager
다음과 같이 하나의 인스턴스를 구성 합니다.
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPMaximumConnectionsPerHost = 2
configuration.timeoutIntervalForRequest = 30
let manager = Alamofire.Manager(configuration: configuration)
요청을 보내면 manager.request(...)
2 쌍으로 발송됩니다 (예상대로 Charles HTTP 프록시로 확인). 하지만 이상한 점은 첫 번째 요청에서 30 초 이내에 완료되지 않은 모든 요청이 동시에 시간 초과로 인해 취소된다는 것입니다 (아직 전송되지 않았더라도). 다음은 동작을 보여주는 그림입니다.
이것은 예상 된 동작이며 요청이 전송되기 전에 시간 초과를받지 않도록하려면 어떻게해야합니까?
감사합니다!
예, 이것은 예상 된 동작입니다. 한 가지 해결책은 요청을 사용자 지정 비동기 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`")
}
}
/*
Copyright (C) 2015 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
An extension to `NSLock` to simplify executing critical code.
From Advanced NSOperations sample code in WWDC 2015 https://developer.apple.com/videos/play/wwdc2015/226/
From https://developer.apple.com/sample-code/wwdc/2015/downloads/Advanced-NSOperations.zip
*/
import Foundation
extension NSLock {
/// Perform closure within lock.
///
/// An extension to `NSLock` to simplify executing critical code.
///
/// - parameter block: The closure to be performed.
func withCriticalScope<T>( block: (Void) -> T) -> T {
lock()
let value = block()
unlock()
return value
}
}
이 패턴의 다른 가능한 변형이 있지만, (a)에 true
대한 반환 을 확인하십시오 asynchronous
. 및 (b) 사용자가 필요한 게시 isFinished
및 isExecuting
개설 된로 KVN을 동시 실행을위한 구성 작업 의 부분 작업 대기열 : 가이드 프로그래밍 동시성 .
참고 URL : https://stackoverflow.com/questions/27021896/nsurlsession-concurrent-requests-with-alamofire
'IT박스' 카테고리의 다른 글
목록에서 여러 요소 선택 (0) | 2020.11.08 |
---|---|
Bash에서 2D 배열을 선언하는 방법 (0) | 2020.11.08 |
Asp.net-mvc 컨트롤러 컨텍스트 모의 (0) | 2020.11.08 |
악명 높은 java.sql.SQLException : 적합한 드라이버가 없습니다. (0) | 2020.11.08 |
페이지 콘텐츠 위에 div 플로팅 (0) | 2020.11.08 |