URLSession 系列第五篇:如何精准监控 HTTPS 请求性能,优化必备!

891 阅读4分钟

这里每天分享一个 iOS 的新知识,快来关注我吧

前言

在前几篇文章中,我们探讨了 URLSession 的基本使用和安全性。这一篇文章将重点介绍如何使用 URLSession 统计一个 HTTPS 网络请求各个阶段的耗时,以帮助我们更好地了解网络请求的性能表现,然后才能进一步的优化。

在阅读这篇文章之前,建议你先阅读我们前几篇关于 URLSession 的文章,这样你会更容易理解本文的内容。

URLSession 系列第一篇:iOS 开发者必须掌握的 URLSession 使用指南

URLSession 系列第二篇:手把手教你封装超实用的网络工具类

URLSession 系列第三篇:告别回调地狱,掌握 Completion Handlers 和 async/await!

URLSession 系列第四篇:彻底防止中间人攻击,确保网络安全!

统计网络请求各个阶段的耗时

在分析网络请求的性能时,我们通常关心以下几个阶段的耗时:

  1. DNS 解析时间:从开始 DNS 解析到 DNS 解析完成的时间。

  2. TCP 连接时间:从开始建立 TCP 连接到连接完成的时间。

  3. TLS 握手时间:从开始 TLS 握手到握手完成的时间。

  4. 请求发送时间:从请求开始发送到请求发送完成的时间。

  5. 服务器处理时间:从请求发送完成到服务器开始响应的时间。

  6. 响应接收时间:从响应开始接收到响应接收完成的时间。

接下来我们逐一介绍如何使用 URLSession 统计这些阶段的耗时。

使用 URLSessionTaskMetrics

从 iOS 10 开始,URLSession 提供了 URLSessionTaskMetrics 类,用于统计网络请求的性能数据。

可以通过实现 URLSession 的委托方法 urlSession(_:task:didFinishCollecting:),来获取这些性能数据。

具体实现步骤

创建一个 URLSessionTaskMetricsLogger 类

首先,我们创建一个 URLSessionTaskMetricsLogger 类,用于记录和打印网络请求的各个阶段耗时。

import Foundation

class URLSessionTaskMetricsLogger: NSObject, URLSessionTaskDelegate {
    func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
        for transactionMetric in metrics.transactionMetrics {
            // 打印各个阶段的耗时
            if let fetchStartDate = transactionMetric.fetchStartDate, // 开始请求时间
               let domainLookupStartDate = transactionMetric.domainLookupStartDate, // DNS 解析开始时间
               let domainLookupEndDate = transactionMetric.domainLookupEndDate, // DNS 解析结束时间
               let connectStartDate = transactionMetric.connectStartDate, // TCP 连接开始时间
               let connectEndDate = transactionMetric.connectEndDate, // TCP 连接结束时间
               let secureConnectionStartDate = transactionMetric.secureConnectionStartDate, // TLS 握手开始时间
               let secureConnectionEndDate = transactionMetric.secureConnectionEndDate, // TLS 握手结束时间
               let requestStartDate = transactionMetric.requestStartDate,  // 请求发送开始时间
               let requestEndDate = transactionMetric.requestEndDate, // 请求发送结束时间
               let responseStartDate = transactionMetric.responseStartDate, // 服务器处理开始时间
               let responseEndDate = transactionMetric.responseEndDate { // 服务器处理结束时间
                
                let dnsTime = domainLookupEndDate.timeIntervalSince(domainLookupStartDate)
                let connectTime = connectEndDate.timeIntervalSince(connectStartDate)
                let tlsTime = secureConnectionEndDate.timeIntervalSince(secureConnectionStartDate)
                let requestTime = requestEndDate.timeIntervalSince(requestStartDate)
                let serverProcessingTime = responseStartDate.timeIntervalSince(requestEndDate)
                let responseTime = responseEndDate.timeIntervalSince(responseStartDate)
                
                print("DNS 解析耗时: \(dnsTime) seconds")
                print("TCP 连接时间: \(connectTime) seconds")
                print("TLS 握手时间: \(tlsTime) seconds")
                print("请求发送时间: \(requestTime) seconds")
                print("服务端处理时间: \(serverProcessingTime) seconds")
                print("服务器响应时间: \(responseTime) seconds")
            }
        }
    }
}

修改 NetworkManager 类

接下来,我们在上一期的代码基础上修改一下 NetworkManager 类,并将 URLSessionTaskMetricsLogger 作为其委托代理。

import Foundation

class NetworkManager {
    // 创建一个单例 NetworkManager 对象
    static let shared = NetworkManager()
    
    // 一个共享的 URLSession 对象
    private let session: URLSession
    
    // 防止其他对象创建 NetworkManager 对象,所以将 init() 方法私有化
    private init() {
        let configuration = URLSessionConfiguration.default
        // 设置代理为 URLSessionTaskMetricsLogger 对象
        self.session = URLSession(configuration: configuration, delegate: URLSessionTaskMetricsLogger(), delegateQueue: nil)
    }
}

在这个代码示例里,URLSessionTaskMetricsLogger 被设置为 URLSession 的代理,以便在网络请求完成时收集和打印各个阶段的耗时。

didFinishCollecting 方法会在网络请求完成时被调用,我们在这个方法中计算并打印各个阶段的耗时。

统计到的耗时数据可以上传到我们的 APM 平台,以便进一步分析和优化网络请求的性能。

使用示例

然后我们随便调用一个接口,看看使用 NetworkManager 进行 HTTPS 请求是否能够正常统计各个阶段耗时:

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        Task {
            await post()
        }
    }
    func post() async {
        let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
        let body = """
            {
                "title": "标题",
                "body": "这是一个新的帖子",
                "userId": 1
            }
            """.data(using: .utf8)
        do {
            let data = try await NetworkManager.shared.postAsync(url: url, body: body)
            if let responseString = String(data: data, encoding: .utf8) {
                print("Response: \(responseString)")
            }
        } catch {
            print("Error: \(error)")
        }
    }
}

在这个示例中,我们发送了一个 HTTPS 请求,并使用 URLSessionTaskMetricsLogger 收集和打印各个阶段的耗时。

运行之后可以在控制台看到对应的打印:

DNS 解析耗时: 0.013000011444091797 seconds
TCP 连接时间: 1.1759999990463257 seconds
TLS 握手时间: 0.4500000476837158 seconds
请求发送时间: 9.703636169433594e-05 seconds
服务端处理时间: 0.6955169439315796 seconds
服务器响应时间: 0.004144072532653809 seconds
Response: {
  "title": "标题",
  "body": "这是一个新的帖子",
  "userId": 1,
  "id": 101
}

总结

在这篇文章中,我们介绍了如何使用 URLSession 统计一个 HTTPS 网络请求各个阶段的耗时。希望对你有所帮助,能够在实际项目中应用这些技术来监控和优化网络请求的性能。下一篇文章准备继续探讨 URLSession 的其他高级用法,敬请期待!

这里每天分享一个 iOS 的新知识,快来关注我吧

本文同步自微信公众号 “iOS新知”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!