网络技术作为互联网应用赖以存在的技术基础,速度与安全永远是其核心使命,本次 WWDC 的网络类 topic 涵盖内容基本还是围绕这两个点来展开。本次 WWDC 网络类 session 在基础网络技术上譬如新协议、新算法方面着墨并不多;也未提出新的类似 NSURLSession / Network.framework 之类的新网络组件。站在应用视角,本次 WWDC 网络类 session 可分为两大类:
无线网络体验优化实践在系统层面的标准化;
本地网络应用的权限管控增强。
在第一类议题中,我们看到很多已经在手淘中的类似实践,或标准或自研,说明手淘在网络技术的开发与应用上还是较为深入和前沿的,基本走在全球业界前列。根据我们手淘的业务特点,笔者重点关注第一类 session,并简单探讨该新技术可以我们带来什么样启发和变化。
使用加密 DNS
DNS 解析是网络的连接的第一步,这里提到的"加密 DNS"是什么、它解决什么问题?
解决什么问题
一是传统 Local DNS 的查询与回复均基于非加密 UDP,我们常见的 DNS 劫持问题
二是 Local DNS Server 本身不可信,或者本地 Local DNS 服务不可用。
其实针对 DNS 解析过程中以上两个问题,在实践中早就有了解决方案,就是 HTTPDNS, 各大云厂商也都有成熟产品售卖,那苹果这里的加密 DNS 与我们的现有 HTTPDNS 有何不同呢?
现有 HTTPDNS 有两个很大的问题:
一是对业务的侵入性,即如果某个网络连接需要使用 HTTPDNS 的能力,首先他需要集成服务商提供的 SDK, 引入相应的 Class,然后修改网络连接的阶段的代码;
二是面临各种技术坑,比如 302 场景的 IP 直连处理、WebView 下 IP 直连如何处理 Cookie、以及 iOS 上的老大难的 SNI 问题等,这些都需要业务开发者付出极大的努力和尝试。
iOS 14 上的 Encrypted DNS 功能很好的解决了现有 HTTPDNS 的存在的问题。
规范与标准
iOS 14 开始系统原生支持两种标准规范的 Encrypted DNS, 分别是 DNS over TLS 与 DNS over HTTPS.
具体协议标准可以参见:rfc7858 (DoT) 、rfc8484 (DoH)
如何实现
iOS 14 提供了两种设置加密 DNS 的方法。第一种方式是选择一个 DNS 服务器作为系统全局所有 App 默认的 DNS 解析器,如果你提供的是一个公共 DNS 服务器,你可以使用 NEDNSSettingsManager API 编写一个 NetworkExtension App 完成系统全局加密 DNS 设置。或者如果你使用 MDM(Mobile Device Management)管理设备的企业设置;你可以推送一个包含 DNSSettings paload 的 profile 文件完成加密 DNS 设置。
使用 NetworkExtension 设置系统域全局 DNS 服务器示例代码:
上述代码首先通过 NEDNSSettingsManager 加载配置,加载成功后,创建一个基于 DoH 协议的 NEDNSOverHTTPSSettings 实例,对其配置 DNS IP 地址和域名,DNS IP 地址是可选配置的。然后将 NEDNSOverHTTPSSettings 实例配置到 NEDNSSettingsManager 共享实例的 dnsSettings 属性,最后保存配置。
一条 DNS 配置包括 DNS 服务器地址、DoT/DoH 协议、一组网络规则。网络规则确保 DNS 设置兼容不同的网络。因为公共 DNS 服务器是无法解析本地网络的私有域名,比如只有企业 WiFi 网络内的 DNS 服务器可以解析员工访问的私有域名,这类情况就需要手动指定网络规则兼容企业 WiFi 网。
网络规则可以针对不同网络类型定义行为,比如蜂窝网、WiFi、或者具体的 WiFi SSID。在匹配的网络下,你可以禁用配置的全局 DNS 设置,或者对私有域名不使用 DNS 设置。
而在一些情况下,兼容性会自动处理。比如强制门户网络(captive portal), 手机在连接上某个 WiFi 的时候,自动弹出一个页面输入账号密码才能连接网络。这种情况下系统域全局 DNS 配置会做例外处理。相类似的,对于 VPN 网络,在 VPN 隧道内的解析将使用 VPN 的 DNS 设置,而不使用系统域 DNS 配置。
网络规则设置示例代码:
上述代码设置了三个网络规则,第一个规则表示 DNS 配置应该在 SSID="MyWorkWiFi"的 WiFi 网络生效,但对私有企业域名 enterprise.example.net 不开启。第二个规则表示规则在蜂窝网下应该被禁止使用;第三个 NEOnDemandRuleConnect 表示 DNS 配置应该默认开启;因为配置 DNS 是系统支持的,所以在编写 NetworkExtension App 时不需要实现 Extension 程序,只需要在 Network Extensions 中勾选 DNS Settings 选项。
运行 NetworkExtension App,DNS 配置将会被安装到系统,为了让 DNS 配置生效,需要前往设置->通用->VPN & Network->DNS 手动启用。
一些网络可能会通过策略阻止你使用加密的 DNS 服务器。这些网络尝试分析 DNS 查询请求来过滤流量。对于此类网络,系统会被标记隐私警告提示,在该网络下的网络连接将会失败。
第二种方式是针对单个 App 的所有连接或部分连接启用加密 DNS。
如果你只想为你的 App 使用加密 DNS,而非涉及整个系统域。你可以适配 Network framework 的 PrivacyContext,对你的整个 App 开启加密 DNS,或者仅对某一连接开启。不管使用的是 URLSessionTask,或 Network framework 连接或 getaddrinfo 的 POSIX API,这种方式都有效。
对单个连接使用加密 DNS 示例代码:
验证请求是否使用加密 DNS:
如果你想在 App 范围内使用加密 DNS,你可以配置默认的 PrivacyContext;App 内发起的每个 DNS 解析都会使用这个配置。不管是 URLSessionTask,还是类似 getaddrinfo 的底层 API。
现有实践对比及启发
与现有实践对比
上文已经提到过 HTTPDNS 产品,手淘的域名解析,采用的是类似于 HTTPDNS 原理,但更加复杂的调度方案。但是相比于这种系统层面的标准化方案,解决了现有实践中的两大难题:
对业务的侵入性:业务必须修改现有网络连接的阶段的代码;
交互的标准兼容性不足:IP 直连下的 302 问题、Cookie 问题、SNI 问题等
带来的变化
一是在应用内部,我们可以对业务透明提供提供全局的域名调度或者域名兜底解析能力, 不再是只有用到特定组件或 SDK 才可以。二是苹果放开系统级别 DNS 接管后,用户设备上的服务相比原 Local DNS 的“中立性”如何管控?对特定业务是否甚至会造成恶化?如果对外部应用的这种“中立性”疑问或担心确实存在,则这种系统标准化的加密 DNS 对手淘这种大型应用则是必选项。
受限网络中推送
解决什么问题
当向 iOS 设备推送消息时,业务服务器需要将消息先发送给 APNS 服务器,APNS 服务器再将消息转换为通知 payload 推送给目标设备。如果设备所在的 WiFi 网络没有连接互联网或者当前网络受限,比如在游艇、医院、野营地这些地方,设备没有与 APNS 服务器建立有效连接,APNS 消息投递将会失败。
对于一些 App 来说,接收推送消息是 App 的一项非常重要的功能,即使在没有互联网连接的情况下也需要持续稳定工作。为了满足这一需求,iOS 14 中增加了本地推送连接(Local Push Connectivity)API,这是 NetworkExtension 家族中新的 API。本地推送连接 API 允许开发者创建自己的推送连接服务,通过开发一个 App Extension,在指定的 WiFi 网络下可以直接与本地业务服务器通信。
上图中,App Extension 主要负责保持与本地业务服务器之间的连接,以及接收业务服务器发来的通知。因为没有了 APNS,开发者需要为业务服务器与 App Extension 定义自己的通信协议。主 App 需要配置具体在哪个 WiFi 网络下使用本地推送连接。当 App 加入到指定的 WiFi 网络,系统会拉起 App Extension, 并在后台持续运行。当 App 断开与指定 WiFi 网络的连接,系统将停止 App Extension 运行。
如何实现
本地推送连接对那些推送功能非常重要,而设备所在网络受限的场景非常适合。而对于常规的推送需求,依然推荐使用 PushKit 或 UserNotification API 处理 APNS 推送消息。每台设备和 APNS 服务器之间只建立一条 APNS 连接,设备上所有 App 都公用这一条连接,所以 APNS 非常省电。
APNS 与本地推送连接对比:
新的本地推送连接 API 由两个类组成:NEAppPushManager 和 NEAppPushProvider。NEAppPushManager 在主 App 中使用,主 App 使用 NEAppPushManager 创建一个配置,配置中指定具体哪个 WiFi 网络下运行本地推送连接。你可以使用 NEAppPushManager 加载/移除/保存配置。NEAppPushProvider 则在 App Extension 使用。在 App Extension 中实现一个 NEAppPushProvider 的子类,子类中需要覆盖生命周期管理方法,这些方法在 App Extension 运行和停止时被调用。
App Extension 主要处理两类推送,一类是常规的推送通知,一类是 VoIP 呼叫通知。如果是常规的推送通知,App Extension 收到消息后,可以使用 UserNotification API 构造一个本地推送显示推送信息。如果是 VoIP 呼叫通知,App Extension 使用 NEAppPushProvider 类将呼叫信息报告给系统。如果此时主 App 不在运行,系统将唤醒主 App,并将消息投递给它,最后主 App 再使用 CallKit API 显示呼叫界面。
下面是在主 App 中使用 NEAppPushManager 的示例代码:
上述代码创建了一个 NEAppPushManager 实例,并配置实例的各个属性值。matchSSIDs 表示在指定的 WiFi 网络下才启用本地推送连接。providerBundleIdentifier 表示 App Extension 的包名,providerConfiguration 是传给 App Extension 的一些配置,在 App Extension 内可以通过 NEAppPushProvider 的 providerConfiguration 属性获取。isEnabled 表示使用这个配置开启本地推送连接。最后调用 saveToPreferences 方法持久化配置。下面是 App Extension 实现 NEAppPushProvider 子类的示例代码:
系统调用 start(completionHandler:)方法启动 App Extension,在这个方法内 App Extension 与本地业务服务器建立连接。当 App Extension 停止运行,系统调用 stop(with:)方法, 在这个方法内 App Extension 断开与业务服务器的连接。handleIncomingVoIPCall(callInfo:)方法在收到 VoIP 呼叫时被调用,在方法内 App Extension 调用 reportIncomingCall(userInfo:)将该事件上报给系统。随后系统将会唤醒主 App,并将呼叫信息传递给主 App。主 App 处理系统传入的呼叫信息示例代码:
以上代码在 AppDelegate 的 didFinishLaunchingWithOptions 方法内使用 NEAppPushManager 加载所有配置,并设置每个 NEAppPushManager 示例的代理属性。系统会在主线程中将接收到呼叫信息通过 didReceiveIncomingCallWithUserInfo 方法投递给主 App。主 App 必须通过 CallKit API 将呼入信息上报给 CallKit 展示呼叫界面。
价值场景
如果只考虑推送本身,对于手淘或者大部分消费类应用来说,笔者认为价值并不大,因为此类 APP 不可能在一个封闭的本地网络里去部署资源来提供服务能力。这里一个可应用的点在于:设备一旦进入特定网络环境,触发 App Extension, 进而唤起主 App,应用可在后台完成一定事务。因为 iOS App 一直缺乏后台服务能力,这种特定网络环境的触发唤醒,极大的补充了这一能力。
现代网络技术的应用
苹果在这次 WWDC 中,把一些较新的网络技术,对应用的体验提升,做了一个简单综述,包括 IPv6、HTTP/2、TLS1.3, MTCP、以及 HTTP/3。这些技术在手淘基本都有涉及,有些是已经是大规模部署、有些是正在逐步推进中。对各个应用来说,如果已经在应用这些技术了,则云端均尽可能标准化,便于进一步推广和复用。这里简单的对苹果的综述做一个搬运:
IPv6
苹果根据最新统计,苹果全球设备 TCP 连接占比中,IPv6 占比 26%,IPv4 占比 74%,其中 74%的占比中有 20%是因为服务端没有开启 IPv6 支持。在建连时间方面,由于减少了 NAT 使用,提高了路由效率,IPv6 的建连时间比 IPv4 快 1.4 倍。开发者只需使用 URLSession 和 Network.framework API,IPv6 网络适配将自动支持。
以上是苹果的数据。阿里巴巴集团从 18 年开始大力推进 IPv6 的建设,目前我们在 IPv6 整体应用规模上在业界是属于头部大厂。但根据我们应用的实际效果数据,以及业界友商的应用数据,性能提升并不明显。以及工信部的 IPv6 建设目标来看,性能提升也不是 IPv6 建设的目标,只要达到 IPv4 同等水平即可。IPv6 的意义就在解决 IPv4 地址空间枯竭的问题,更多的在规模、安全,而不是性能提升。就企业而言,例如可以降低 IPv4 地址的购买费用;就国家而言,IPv6 突破了 IPv4 中国境内无 DNS 根结点的风险。IPv6 目前阶段在国内尚处于发展中,基础运营商覆盖、家用网络接入设备支持、应用服务的支持,正在快速发展中。
HTTP/2
HTTP/2 的多路复用特性使得对同一服务器的多个请求复用到单个连接上,不必等待前一个请求响应结束才能发送下一个请求,不仅节省了时间也提升了性能。头部压缩特性提升了带宽利用率,通过简化消息内容,从而降低消息的大小。根据最新统计,在 Safari 中 HTTP2 Web 流量占比 79%,HTTP/2 比 HTTP/1.1 快 1.8 倍。如果服务端支持 HTTP/2,URLSession 将默认使用 HTTP/2。
在手淘业务中,全面应用 HTTP2 已经有三四年之久,其使用规模比苹果统计的结果要高的多,取得了巨大的体验提升收获。手淘的核心流量分为两类:API 请求 MTOP 于图片 CDN 请求,这两类的 HTTP2 的流量占比约超过 98%,基本实现核心流量全部长连化。但当前手淘的 HTTP2 技术在设备支持之前即大规模应用,协议栈完全自研,为进一步提升建联速度,又做了一些预置证书的优化。下一步将逐步使基础网络依赖标准化平台能力,降低对私有协议栈的依赖,可降低包大小以及提升产品复用体验。
TLS1.3
TLS1.3 通过减少一次握手减少了建连时间,通过形式化验证(Formal Verification)与减少被错误配置的可能性,提高了通信安全。从 iOS 13.4 开始,TLS1.3 默认在 URLSession 和 Network.framework 开启。根据最新统计,在最新的 iOS 系统上,大约 49%的连接使用 TLS1.3。使用 TLS1.3 比使用 TLS1.2 建连时间快 1.3 倍。如果服务端支持 TLS1.3,URLSession 将默认使用 TLS1.3。
目前手淘的 HTTP/2 的 TLS version 仍然还是基于 TLS1.2,不过为了解决低版本 TLS 的性能之殇,手淘网络通过预置证书与自定义 SSL 握手流程,创新自研了 slight-ssl 协议,实现了 0rtt 的效果。TLS 1.3 标准在系统层面的全面支持,对应用来说是一个明确的技术趋势信号,我们的网络协议需尽快标准化,对网络管道两端来说,客户端应用层网络接口需全面升级到 NSURLSession 或 Network.framework, 实现对系统标准能力的应用;云端也许全面支持 TLS1.3,可不依赖端侧 SDK 即可提供更新安全、高效、标准的网连接。
MultiPath TCP
MultiPath TCP 允许在一条 TCP 链路中建立多个子通道。当设备切换网络时,单个 TCP 连接依然可以继续使用。苹果的 Apple Music 与 Siri 服务都使用了 MultiPath TCP。Apple Music 在使用 MultiPath TCP 之后,音乐播放卡顿次数减少了 13%,卡顿持续时间减少了 22%。开启 MultiPath TCP 需要客户端和服务端都支持才能生效,服务端支持 MultiPath TCP 可参考:http://multipath-tcp.org/
其实手淘在这方面也有类似的优化尝试:多网卡:同时通过 Wi-Fi 与蜂窝网连接目标服务器,提升数据传输速度。其技术原理与 MTCP 不一样,但也是想在上层起到类似作用:通过多路连接,提升数据交换带宽。业界也有类似的产品,例如华为的 LinkTurbo。
HTTP/3
HTTP/3 是下一代 HTTP 协议,它是构建在新的 QUIC 传输协议之上,QUIC 协议内建了对 TLS1.3 的支持,并提供了与 HTTP/2 一样的多路复用功能,并进一步减少了队头阻塞的发生,让单个请求或相应的丢失不影响到其他请求。使用 QUIC 的 HTTP/3 还具有较高的保真度信息,以提供改进的拥塞控制和丢包恢复。同时也包括内建的移动性支持,这样网络切换不会导致正在进行的操作失败,可以无缝在不同网络之间切换。不过 HTTP/3 目前还处于草案阶段,iOS 14 和 MacOS Big Sur 包括了一个对使用 URLSession 的 HTTP/3 的实验预览支持,这个功能可以在开发者设置中开启。同时 Safari 的 HTTP/3 支持也可在开发者设置中开启。
在手淘中,我们的 QUIC 应用应该会早于苹果系统先行支持,目前已经在灰度中。
本文转载自公众号淘系技术(ID:AlibabaMTT)。
原文链接:
评论