美文网首页
Alamofire 网络请求流程探索2

Alamofire 网络请求流程探索2

作者: 好有魔力 | 来源:发表于2019-08-23 10:25 被阅读0次

本篇承接上一篇 Alamofire发起网络请求初探 来继续探索在网络请求发起过程中涉及的一些有用的细节.

RequestAdapter

RequestAdapter 是一个协议,提供了在发起请求之前,对request进行最后处理的能力,先来看看具体使用:

 SessionManager.default.adapter = Adapter()

 SessionManager.default.request("your url", method: .get, parameters: ["p0":"111","p2":"222"])
            .response { (response) in
                debugPrint(response)
            }

class Adapter:RequestAdapter {
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
        var request = urlRequest
        request.setValue("aaa", forHTTPHeaderField: "aheader")
        return request
    }
}
  • 上面的例子设置了 Adapter , 所有在SessionManager.default发起请求之前,都会来到 Adapter 类adapt 方法中
  • 具体的使用场景可能有:
    添加公共的头信息
    替换为一个全新的请求,但是这个请求的参数你可能要自己进行序列化.来看源代码:
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
   var originalRequest: URLRequest?

     do {
       originalRequest = try urlRequest.asURLRequest()
       let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
          
         //这里传入了 adapter 属性
          let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
          let request = DataRequest(session: session, requestTask: .data(originalTask, task))

          delegate[task] = request

         if startRequestsImmediately { request.resume() }

           return request
        } catch {
            return request(originalRequest, failedWith: error)
        }
}


struct Requestable: TaskConvertible {
        let urlRequest: URLRequest

        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
            do {
               //在这里又传递了 adapt
                let urlRequest = try self.urlRequest.adapt(using: adapter)
                return queue.sync { session.dataTask(with: urlRequest) }
            } catch {
                throw AdaptError(error: error)
            }
        }
    }

extension URLRequest {
func adapt(using adapter: RequestAdapter?) throws -> URLRequest {
      //在这里判断是否设置了 adapter
        guard let adapter = adapter else { return self }
        return try adapter.adapt(self)
    }
}

在使用 adapter 创建了 Task 之后,在后面就发起请求了. 并没有走参数序列化的流程.

Timeline

Alamofire 在得到网络的响应后,会打印有关本次请求的时间信息.

Timeline: { 
"Request Start Time": 588155784.670, 
"Initial Response Time": 588155784.969,
"Request Completed Time": 588155784.972, 
"Serialization Completed Time": 588155784.972,
"Latency": 0.300 secs, 
"Request Duration": 0.302 secs, 
"Serialization Duration": 0.000 secs,
"Total Duration": 0.302 secs 
}

这里有许多有用的信息,比如Total Duration 代表一次请求从发起到接收到响应的总时间,方便观测接口性能.

这些时间的是怎么统计的呢?
  • 统计时间一般都是两个绝对时间点做差,来看看Alamofire是如何做的.
open class Request {
  open func resume() {
        guard let task = task else { delegate.queue.isSuspended = false ; return }
        
       //startTime在 resume 之前被初始化,记录时间点
        if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }

        task.resume()

        NotificationCenter.default.post(
            name: Notification.Name.Task.DidResume,
            object: self,
            userInfo: [Notification.Key.Task: task]
        )
    }


 init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
        self.session = session

        switch requestTask {
        case .data(let originalTask, let task):
            taskDelegate = DataTaskDelegate(task: task)
            self.originalTask = originalTask
        case .download(let originalTask, let task):
            taskDelegate = DownloadTaskDelegate(task: task)
            self.originalTask = originalTask
        case .upload(let originalTask, let task):
            taskDelegate = UploadTaskDelegate(task: task)
            self.originalTask = originalTask
        case .stream(let originalTask, let task):
            taskDelegate = TaskDelegate(task: task)
            self.originalTask = originalTask
        }

        delegate.error = error
        //endTime 在 Request 的父类中进行了初始化
        delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
    }
}

open internal(set) var delegate: TaskDelegate {
        get {
            taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
            return taskDelegate
        }
        set {
            taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
            taskDelegate = newValue
        }
    }

值得注意的是 endTime 的初始化时机, 被放到了 delegate.queue 中.来看看 delegate.queue

open class TaskDelegate: NSObject {

 init(task: URLSessionTask?) {
        _task = task

        self.queue = {
            let operationQueue = OperationQueue()

            operationQueue.maxConcurrentOperationCount = 1
            operationQueue.isSuspended = true
            operationQueue.qualityOfService = .utility

            return operationQueue
        }()
 }
}

可以看到 delegate.queue 是一个串行队列,并且处于挂起状态. 所以想要使 RequestendTime的初始化方法被执行, 需要了解 queue 何时取消挂起. queue取消挂起有两处

open class TaskDelegate: NSObject {
     @objc(URLSession:task:didCompleteWithError:)
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        if let taskDidCompleteWithError = taskDidCompleteWithError {
            taskDidCompleteWithError(session, task, error)
        } else {
            if let error = error {
                if self.error == nil { self.error = error }

                if
                    let downloadDelegate = self as? DownloadTaskDelegate,
                    let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
                {
                    downloadDelegate.resumeData = resumeData
                }
            }
            //取消挂起
            queue.isSuspended = false
        }
    }
}


open class Request {

    open func resume() {
      //取消挂起
       guard let task = task else { delegate.queue.isSuspended = false ; return }

        if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }

        task.resume()

        NotificationCenter.default.post(
            name: Notification.Name.Task.DidResume,
            object: self,
            userInfo: [Notification.Key.Task: task]
        )
    }
}

取消挂起有两个地方:

  • task 完成时
  • 请求将要发起时,如果task创建失败则没有必要统计时间
具体是怎么被打印出来的?
extension DataRequest {
    @discardableResult
    public func response(queue: DispatchQueue? = nil, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self {
        delegate.queue.addOperation {
            (queue ?? DispatchQueue.main).async {
                var dataResponse = DefaultDataResponse(
                    request: self.request,
                    response: self.response,
                    data: self.delegate.data,
                    error: self.delegate.error,
                    timeline: self.timeline
                )

                dataResponse.add(self.delegate.metrics)

                completionHandler(dataResponse)
            }
        }

        return self
 }

// DataRequset 继承 Request
extension Request {
    var timeline: Timeline {
        //这里
        let requestStartTime = self.startTime ?? CFAbsoluteTimeGetCurrent()
        let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
        let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime

        return Timeline(
            requestStartTime: requestStartTime,
            initialResponseTime: initialResponseTime,
            requestCompletedTime: requestCompletedTime,
            serializationCompletedTime: CFAbsoluteTimeGetCurrent()
        )
    }
}

public struct Timeline {
   
    public init(
        requestStartTime: CFAbsoluteTime = 0.0,
        initialResponseTime: CFAbsoluteTime = 0.0,
        requestCompletedTime: CFAbsoluteTime = 0.0,
        serializationCompletedTime: CFAbsoluteTime = 0.0)
    {
        self.requestStartTime = requestStartTime
        self.initialResponseTime = initialResponseTime
        self.requestCompletedTime = requestCompletedTime
        self.serializationCompletedTime = serializationCompletedTime

        self.latency = initialResponseTime - requestStartTime
        self.requestDuration = requestCompletedTime - requestStartTime
        self.serializationDuration = serializationCompletedTime - requestCompletedTime
       //这里
        self.totalDuration = serializationCompletedTime - requestStartTime
    }
}
//这里是打印的格式化
extension Timeline: CustomStringConvertible {
  
    public var description: String {
        let latency = String(format: "%.3f", self.latency)
        let requestDuration = String(format: "%.3f", self.requestDuration)
        let serializationDuration = String(format: "%.3f", self.serializationDuration)
        let totalDuration = String(format: "%.3f", self.totalDuration)

        // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
        // fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
        let timings = [
            "\"Latency\": " + latency + " secs",
            "\"Request Duration\": " + requestDuration + " secs",
            "\"Serialization Duration\": " + serializationDuration + " secs",
            "\"Total Duration\": " + totalDuration + " secs"
        ]

        return "Timeline: { " + timings.joined(separator: ", ") + " }"
    }
}


public struct DefaultDataResponse {
public init(
        request: URLRequest?,
        response: HTTPURLResponse?,
        data: Data?,
        error: Error?,
        timeline: Timeline = Timeline(),
        metrics: AnyObject? = nil)
    {
        self.request = request
        self.response = response
        self.data = data
        self.error = error
        self.timeline = timeline
    }
}

重试机制

有时一些比较重要的接口,如果请求失败.失败时需要重试. Alamofire 也优雅地为我们提供了重试机制.

 SessionManager.default.retrier = Retrier()

 SessionManager.default.request("your url", method: .get, parameters: ["p0":"111","p2":"222"])
            .response { (response) in
                debugPrint(response)
            }

class Retrier {
    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
        print("manager = \(manager)")
        print("request = \(request)")
        print("error = \(error)")
        completion(true,1)
        //在必要时调用,不然会引起无限重试
        completion(false,0)
    }
}
何时重试?
extension SessionDelegate: URLSessionTaskDelegate {
 open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {

        ///上面太长了....

        if let retrier = retrier, let error = error {
            retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
                guard shouldRetry else { completeTask(session, task, error) ; return }

                DispatchQueue.utility.after(timeDelay) { [weak self] in
                    guard let strongSelf = self else { return }

                    let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false

                    if retrySucceeded, let task = request.task {
                        strongSelf[task] = request
                        return
                    } else {
                        completeTask(session, task, error)
                    }
                }
            }
        } else {
            completeTask(session, task, error)
        }
    }
}

}
open class SessionManager {
func retry(_ request: Request) -> Bool {
        guard let originalTask = request.originalTask else { return false }

        do {
            let task = try originalTask.task(session: session, adapter: adapter, queue: queue)

            if let originalTask = request.task {
                delegate[originalTask] = nil // removes the old request to avoid endless growth
            }

            request.delegate.task = task // resets all task delegate data

            request.retryCount += 1
            request.startTime = CFAbsoluteTimeGetCurrent()
            request.endTime = nil

            task.resume()

            return true
        } catch {
            request.delegate.error = error.underlyingAdaptError ?? error
            return false
        }
    }
}
  • open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) 方法中,检测是否发生错误.如果发生错误就调用我们设置的retrier的 'should' 方法决定是否重试.
  • 根据我们设置的延时时间,延时一段时间后调用 SessionMangerfunc retry(_ request: Request) -> Bool 方法执行真正的重试操作

响应验证 (Response Validation)

SessionManager.default.request(urlStr, method: .get, parameters: ["p0":"aaa","p1":"bbb"])
            .response { (response) in
                debugPrint(response)
            }.validate { (request, response, data) -> Request.ValidationResult in
                guard let _ = data else{
                    return .failure(NSError.init(domain: "custom failure 0", code: 10089, userInfo: nil))
                }
                let code = response.statusCode
                if code == 404 {
                    return .failure(NSError.init(domain: "custom failure 1", code: 100800, userInfo: nil))
                }
                return .success
        }

validate 在得到 本次请求的响应 之前被调用,在此处可以验证数据是否符合我们的业务逻辑,并向业务层返回自定义的错误信息.
来看看源码:

extension DataRequest {

@discardableResult
    public func validate(_ validation: @escaping Validation) -> Self {

       //创建一个闭包,在此闭包中调用 validation 参数闭包
        let validationExecution: () -> Void = { [unowned self] in
            if
                let response = self.response,
                self.delegate.error == nil,
                case let .failure(error) = validation(self.request, response, self.delegate.data)
            {
                self.delegate.error = error
            }
        }
       //添加验证闭包
        validations.append(validationExecution)

        return self
    }
}

此方法保存了 validation 闭包. 并且把返回的自定义error信息保存到 self.delegate.error 中 .

何时进行验证?
extension SessionDelegate : URLSessionTaskDelegate {
   open func urlSession(_ session: URLSession, task: URLSessionTask, 
    didCompleteWithError error: Error?) {

            let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
            guard let strongSelf = self else { return }

            strongSelf.taskDidComplete?(session, task, error)

            strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)

            var userInfo: [String: Any] = [Notification.Key.Task: task]

            if let data = (strongSelf[task]?.delegate as? DataTaskDelegate)?.data {
                userInfo[Notification.Key.ResponseData] = data
            }

            NotificationCenter.default.post(
                name: Notification.Name.Task.DidComplete,
                object: strongSelf,
                userInfo: userInfo
            )

            strongSelf[task] = nil
        }

        ///省略...
       
        // Run all validations on the request before checking if an error occurred
        request.validations.forEach { $0() }

        // Determine whether an error has occurred
        var error: Error? = error

        if request.delegate.error != nil {
            error = request.delegate.error
        }

        ///省略...
        completeTask(session, task, error)
   }
}

是在每一次 DataTask 请求数据完成时调用保存的 validation 闭包, 并且把自定义的error信息放到 dataResponse 中最后返回给外界.

相关文章

网友评论

      本文标题:Alamofire 网络请求流程探索2

      本文链接:https://www.haomeiwen.com/subject/ghinsctx.html