一、网络耗时流程及优化方案结论
发起请求 -> 域名DNS解析 -> TCP三次握手 ( -> TLS握手 -> ) -> request -> response -> json解析 -> 业务
1、过程中的优化方案
| 序号 | 阶段 | 耗时根源 | 优化方案 |
|---|---|---|---|
| 1 | 发起请求 | 每个接口都要建立连接 | 前端配置想要的数据,后台通过一个接口返回 如商品详情页,请求[Base,Detai,Images]:商品基础信息+详情+图片 其他减少请求参数的body数据。 |
| 2 | 域名DNS解析 | DNS解析过程不受控制,⽆法保证解析到最快的IP | 方案1:IP直连 点击链接可跳转到下文介绍 HTTP(HTTPS)请求,底层基于TCP连接,需要有IP和端口信息。 ⾃⼰做域名解析⼯作,通过HTTP请求后台去拿到域名对应的IP地址。 方案2:HTTPDNS 阿里云的DNS SDK:AlicloudPDNS、AlicloudHTTPDNS 附:iOS14原生加密DNS方案 |
| 3 | TCP三次握手 | TCP:面向连接的协议 UDP:无连接协议 TCP 比 UDP 要慢 |
方案1:用HTTP实现长连接 点击链接可跳转到下文介绍 方案2:使用HTTP/3.0 采用了基于 UDP 的 QUIC 协议来替代传统的 TCP 协议 HTTP协议各版本使用情况 |
| 4 | TLS握手 | —— | —— |
| 5 | request | 服务端排查是否有耗时逻辑 | |
| 6 | response | 数据体积大 | 删减冗余数据,开启gzip,数据分页、Protobuf、WebP等方式进行优化 Protobuf:见我的另一篇文章 protobuf的安装.md 分页:见我的另一篇文章 分页规范.md |
| 7 | json解析 | 解析耗时 | 采用解析效率更高的库,如 YYModel |
| 8 | 业务 | —— | —— |
上述优化方案的参考文档来源于:
2、其他优化方案
2.1、弱网优化:针对不同的网络,做不同超时,重试设置,以及取不同质量的图片数据。
2.2、缓存(必备:API缓存+可考虑加:Last-Modified/ETag)
见我的另一篇文章:
发布的内容,优先写入数据库,视频切片使用另一个请求异步获取。
二、网络耗时分布统计
问:怎么计算耗时?(此场景可不需要发起者数组,则不需要对请求的结果进行关联,也就不用涉及怎么对应请求的结果和请求本身)
答:在 得到请求结果Response 中获取 请求时候的Header
1 | class ResponseModel { |
1、ResponseDateModel dateModel; // 这次结果的时间
2、List
1、网络请求各阶段的耗时(iOS)
1 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)) |
三、有发起者数组时候,怎么对应请求的结果和请求本身
问:如果有发起者数组,怎么对应请求的结果和请求本身?
举例:如果我有一个dataModels数组,元素属性包含url和请求结果,且有些元素可能是对相同url进行的请求,那应该怎么保证得到的结果正确返回给dataModel?场景比如我对同一个视频进行了多次重复的下载。
举例:如果一个 URL 在短时间内被请求多次,需要在回调中对最终返回的 data 进行正确匹配到调用者的操作吗?即多个相同 URL 请求同时发送,如何不混淆 data?举例:如果我有一个dataModels数组,元素属性包含url和请求结果,且有些元素可能是对相同url进行的请求,那应该怎么保证得到的结果正确返回给dataModel?
在这种情况下,你需要正确地将每个请求的返回数据匹配到 dataModels 中的正确元素,即确保相同 URL 的不同请求不会混淆回调数据。
关键问题
1. 多个请求的 URL 可能相同,但请求对象不同。你不能仅通过 url 进行匹配。
2. 请求的返回顺序不确定,需要确保数据匹配到正确的 dataModel。
3. 并发请求管理,防止不同请求间的数据覆盖或错误匹配。
解决方案
使用 taskIdentifier 作为唯一标识符,在 dataModels 中维护一个字典 taskID -> dataModel,确保每个任务返回的数据都能匹配到正确的 dataModel。
示例代码
1 | import Foundation |
📌 方案解析
1. 在 dataModels 里为每个请求对象绑定 taskIdentifier:
• 每个 dataModel 存储自己的 taskID,用于匹配请求。
2. 在 taskMap 中存储 taskID -> dataModel 的映射:
• 确保回调返回的数据能准确找到对应的 dataModel。
3. 使用 DispatchQueue.async(flags: .barrier) 确保线程安全:
• 读写 taskMap 是并发安全的,避免竞态条件。
问:如果在回调里直接使用 model.result = data ,而不是获取到dataModel后设置dataModel.result = data,会怎么样?
答:如果在回调里直接使用 model.result = data,而不是通过 taskMap 获取 dataModel 后再设置 dataModel.result = data,可能会出现 错误的数据匹配 或 数据竞争 的问题。
Next:第3节:弱网优化空间探索
附1:HTTP协议各版本使用情况
| 类型 | 发布时间与现状 | |
|---|---|---|
| HTTP/1.1 | 在1997年被标准化,是目前互联网上广泛使用的HTTP协议版本 | |
| HTTP/2.0 | 于2015年发布,也是目前互联网上广泛使用的HTTP协议版本 | 不是对HTTP/1.1的重写,而是在性能方面的改进 |
| HTTP/3.0 | 目前尚未成为主流,还在逐步推广和应用中 目前使用量 据 W3Techs 统计约30% |
是基于Google的QUIC协议开发 |
附2:DNS
1、DNS介绍
1.1、DNS劫持/DNS污染前后
未劫持 VS 劫持
1.2、DNS劫持模拟
-
1、Charles 在 Tools 中设置 DNS Spoofing
2、修改终端设备的代理
1.3、如何检测 DNS 劫持
- iOS 用户推荐 iNetTools
- Android 用户推荐 LanDroid
-
ping 一个不存在的 IP 地址却仍获得解析,则 DNS 很可能已被黑客入侵。
2、DNS的优化
方案1:IP直连
方案2:HTTPDNS
2.1、IP直连
为什么大家会选择直接使用 IP 来进行连接呢?它具有多方面的优势:
- 防劫持,可以绕过运营商 LocalDNS 解析过程,避免域名劫持,提高网络访问成功率
- 降低延迟,DNS 解析是一个相对耗时的工作,跳过这个过程可以降低一定的延迟
- 精准调度,运营商解析返回的节点不一定是最优的,自己获取 IP 可以基于自己的策略来获取最精准的、最优的节点
对于获取 IP,我了解到的是两种方案,
一种是直接接入腾讯或者阿里的 HTTPDNS 服务,在发起请求的时候是通过 HTTPDNS 获取 IP,然后直接使用 IP 来进行业务访问;一种是内置 Server IP,可以在启动等阶段由服务端下发域名和 IP 的对应列表,客户端来进行缓存,发起网络请求的时候直接根据缓存 IP 来进行业务访问。
附3:用HTTP实现长连接
长连接:长连接的特点是一旦通过三次握手建立链接之后,该条链路就一直存在,而且该链路是一种双向的通行机制,适合于频繁的网络请求,避免 Http 每一次请求都会建立链接和关闭链接的操作,减少浪费,提高效率。
在首部字段中设置Connection:keep-alive 和Keep-Alive: timeout=60,表明连接建立之后,空闲时间超过60秒之后,就会失效。如果在空闲第58秒时,再次使用此连接,则连接仍然有效,使用完之后,重新计数,空闲60秒之后过期。
设置HTTP长连接,无过期时间:
在首部字段中只设置Connection:keep-alive,表明连接永久有效。
其他文章:tcp长短连接、http长短连接、心跳包、tcp KeepAlive保活机制
- TCP 是 HTTP 的默认传输层协议:HTTP 通常运行在 TCP 之上,因为 TCP 提供了可靠的、面向连接的服务,确保数据按顺序、完整无误地从源传送到目的地。这对于 Web 浏览器和服务器之间的通信非常重要,因为它们需要确保请求和响应的完整性。
End
其他参考文档:
项目 :iOS-Monitor-Platform 中的网络部分 https://github.com/aozhimin/iOS-Monitor-Platform?tab=readme-ov-file#network