网络优化

3,907 阅读13分钟

网络基础知识

1、无线网络

千兆级 LTE 指的是蜂窝网络在理论上的速度可以达到光纤级别的 1Gbps(125MB/s)。

关于 5G 的原理:文章

2、Link Turbo

此为手机厂商的网络优化,例如华为最近在荣耀 V20 推出的 Link Turbo 网络聚合加速技术

双通道技术,可以在 WiFi 网络不稳定的时候,自动切换到移动网络。类似的技术有:iPhone 的无限网络助理、小米和一加的自适应 WLAN。

但是双通道技术,同时依然只能使用单一通道,只是会根据网络情况自动切换。而 Link Turbo 硬核的地方在于,可以同时使用两条通道传输数据,而且支持 TCP 和 UDP。其中 TCP 使用开源的 MultiPath TCP(iOS 7 也在引用),而 UDP 则是华为自研的 MultiPath UDP。

Link Turbo 的缺点:

  • 覆盖用户比较少。
  • 需要后台服务器同步支持。

Link Turbo 的意义:

  • 流量越来越便宜,用户会更愿意追求体验。
  • 华为可以直接和云服务商以及 CDN 服务商合作,屏蔽应用后台服务器的改造,降低集成成本。

网络 I/O

1、I/O 模型

Linux 内核会把所有外部设备都看作一个文件来操作。在网络 I/O 中系统对一个 Socket 的读写也会有相应的描述符,称为 socket fd(Socket 描述符)。

整个 I/O 流程分为两个阶段:

等待 Socket 数据准备好。 将数据从内核拷贝到应用进程。

在 《UNIX 网路编程》 中,将 UNIX 网络 I/O 模型分为以下五种。

在开发中,比较常用的有:

  • 阻塞 I/O
  • 非阻塞 I/O
  • 多路复用 I/O

关于 UNIX 网络 I/O 模型,可以参考:

关于网络,存在的几个问题:

多路复用 I/O 一定比阻塞 I/O 更好?

和文件 I/O 一样,最简单的 I/O 并发方式就是多线程 + 阻塞 I/O。如果同时活动的网络连接非常多,使用多路复用 I/O 性能的确会更好。但是对于客户端来说,这个假设不一定成立,对于多路复用 I/O 来说,整个流程会增加大量的 select/epoll 这样的系统调用,不一定比阻塞 I/O 更快。

epoll 一定比 select/poll 要好?

如果同一时间的连接数非常少的情况,select 的性能不会比 epoll 好,但是更多的情况下,epoll 更好。

epoll 使用了 mmap 减少内核到用户空间的拷贝?epoll 并没有使用 mmap 技术,参考 epoll 实现。并且也不会如此设计,因为直接共享内存可能会引发比较大的安全漏洞。

2、数据处理

网络 I/O 也使用了终端,不过网络 I/O 的终端更加复杂一些,同时使用了软中断和硬中断。通过硬中断通知 CPU 有数据来了,但是这个处理会非常轻量,耗时的操作被移到软终端处理函数中慢慢处理。

  • 软终端:通过 /proc/softirqs 文件
  • 硬中断:通过 /proc/interrupts 文件。

网卡收发包的流程:

网络性能评估

1、延迟与带宽

延迟:数据从信息源发送到目的地所需的时间。 带宽:逻辑或物理通信路径最大的吞吐量。

弱网的特点:

2、性能测量

指标:

  • 吞吐量:接收和传输的每秒字节数。
  • 延迟:发送/接收延迟、连接延迟、首包延迟、网络往返时间等。
  • 连接数:每秒的连接数。
  • 错误:丢包计数、超时等。

Linux 提供了大量的网络性能分析工具,比较适用 Android 的有以下几种。

可以直接查看 /proc/net,它里面包含了许多网络统计信息的文件。例如 Android 的 TrafficStats 接口,就是利用 /proc/net/xt_qtaguid/stats 和 /proc/net/xt_qtaguid/iface_stat_fmt 文件来统计应用的流量信息。

移动端优化

网络优化的核心三问题:

除了这三个问题,还需要关心:耗电、流量。

整个网络请求,主要分几个步骤:

  • DNS 解析。通过 DNS 服务器,拿到对应域名的 IP 地址。比较关注:DNS 解析耗时情况、运营商 LocalNDS 的劫持、DNS 调度等问题。
  • 创建连接。和服务器建立连接,包括 TCP 三次握手、TLS 密钥协商等工作。多个 IP/端口该如何选择、是否要使用 HTTPS、能否可以减少甚至省下创建连接的时间,这些问题是优化的关键。
  • 发送/接收数据。关键点:组装数据、发送数据、接收数据、解析数据,主要关注如何根据网络状况,将带宽利用好,怎样快速检测到网络延迟,在弱网下如何调整包大小等问题。
  • 关闭连接。关注:主动关闭和被动关闭两种情况。

一个网络库的核心作用主要有以下三点:

  • 统一编程接口。
  • 全局网络控制。统一的网络调度、流量监控以及容灾管理等。
  • 高性能。关注:速度、CPU、内存、I/O 的使用,以及失败率、崩溃率、协议兼容性等方面。

比较好的网络库:

  • OkHttp
  • Chromium 的 Cronet
  • 微信的 Mars

商业软件的应用:

  • Chromium:蘑菇街、头条、UC 浏览器。
  • Mars:微信、拼多多、虎牙、链家、美丽说。

对比:

对于不用 OkHTTP,主要原因在于不支持跨平台。对于大型应用来说,跨平台是非常重要的。

对于 Mars,它是一个跨平台的 Socket 层解决方案,并不支持完整的 HTTP 协议,所以 Mars 从严格意义上来说,并不是一个完整的网络库。但是它在弱网和连接上做了大量的优化,并且支持长连接。参考:Wiki

Chromium 没有太大的缺点,并且还可以享受 Google 后续网络优化的成功,类似 TLS 1.3、QUIC 支持等。但是它针对弱网没有做太多定制的华油,也不支持长连接。通常基于 Chromium 的二次开发,都是为了解决这两个问题。

大网络平台 对于大公司,不满足于客户端优化,还会在服务端上做双端优化。

1、HTTPDNS

DNS 的解析是我们网络请求的第一项工作,默认使用运营商的 LocalDNS 服务,这块的耗时,在 3G 网络下可能是 200~300ms,4G 网路也需要 100ms。

LocalDNS 慢不是最大的问题,还存在一些其他的问题:

  • 稳定性。UDP 协议,无状态。容易域名劫持,每年至少几百万个域名被劫持,一年至少十次大规模事件。
  • 准确性。LocalNDS 调度经常不准确。
  • 及时性。运营商会修改 DNS 的 TTL。导致 DNS 修改生效延迟。
  • HTTPDNS 就是为了解决 LocalDNS 的这些问题。

参考百度的 《DNS 优化》

2、连接复用

创建连接需要经过:

  • TCP 三次握手
  • TLS 密钥协商

建立连接的代价非常大,这里的优化思路主要是复用连接,这样不用每次请求都建立连接。

连接复用主要是利用 HTTP 协议里的 keep-alive,并且 Http 2.0 的多路复用则可以进一步提升连接复用率。它复用的这条连接支持同时处理多条请求,所有请求都可以并发在这条连接上进行。

HTTP 2.0 的多路复用的问题:

  • 同一条 Http 2.0 连接,只支持同一个域名。
  • 后端需要为了支持 HTTP 2.0 额外改造。

为了解决这两个问题,通常会在统一接入层做改造,接入层将数据转换到 HTTP/1.1 再转发到对应域名的服务器。

HTTP 2.0 的多路复用,本质上依然是同一条 TCP 连接,如果所有的域名的请求都集中在某一条连接中,在网路拥塞的时候,容易出现 TCP 队首阻塞问题。

对于客户端网络库,OkHttp 和 Chromium 对于 HTTP/2.0 的连接,同一个域名只会保留一条连接。如遇到第三方请求,特别是文件下载以及视频播放等场景,可能会遇到对方服务器单连接限速的问题,此处可以通过修改网络库,或者简单禁用掉 HTTP/2.0 协议解决。

3、压缩与加密

压缩可以减少传输的数据量,对于 HTTP 请求来说,数据主要包括三部分:

  • 请求 URL
  • 请求 Header
  • 请求 Body

对于 Header,HTTP 2.0 本身支持头部压缩技术,因此需要压缩的主要是 URL 和 请求 body。

压缩的优化主要思路,是把重复的内容都优化掉。对于 URL,一般会带很多公共参数,这些参数大部分是不变的,这样不变的参数客户端只要上传一次即可。

对于请求 Body 来说,一方面是数据通信协议的选择,在网络传输中目前最流行的两种数据序列化的方式是 JSON 和 Protocol Buffers。Protocol Buffers 在使用上会复杂一些,但是数据压缩率、序列化和反序列化速度上有很大的优势。

另外一方面是压缩算法的选择:

其中,如果 Z-standard 通过业务数据样本训练处合适的字典,是目前压缩率表现最好的算法。

针对特殊的数据,还有一些针对性的压缩方法,例如图片有 webp、hevc、SharpP 等压缩率更高的格式。除此之外,还有基于 AI 的图片超清化也是一大神器。

安全

基于 HTTPS 和 HTTP/2 通道,已经有了 TLS 加密。参考《TLS 协议分析》

HTTPS 的优化思路:

  • 连接复用率。通过多个域名共用同一个 HTTP/2 连接、长连接等方式提升连接复用率。

  • 减少握手次数。TLS 1.3 可以实现 0—RTT 协商。0-RTT 在 TLS 1.3 之前已经被大厂所使用,例如微信的 mmtls、Facebook 的 fizz、阿里的 SlightSSL。

  • 性能提升。使用 ecc 证书代替 RSA,服务端签名的性能可以提升 4~10 倍,但是客户端校验性能降低了约 20 倍,从 10 微秒级降低到 100 微秒级。另外一方面可以通过 Session Ticket 会话复用,节省一个 RTT 耗时。

如果客户端设置了代理,TLS 加密的数据可以被揭开并可能被利用,此时可以在客户端使用“证书锁定”。

还有一些方案,可能是需要用钱堆出来,比如部署跨国的专线、加速点、多 IDC 就近接入等,除此之外,使用 CDN 服务P2P技术也是比较常见的手段。

QUIC 和 IPv6

QUIC 最早在 2013 年被 Google 实现,在 2018 年,基于 QUIC 的思想,又重写了一套 QUIC 协议,被 HTTP 确认为 HTTP 3.0 标准。

QUIC 可以简单理解为:HTTP 2.0 + TLS 1.3 + UDP。

QUIC 的优势:

  • 灵活控制拥塞协议。可以直接使用 Google 的 BBR 算法。
  • “真”连接复用。客户端切换网络时,不需要重连。

QUIC 现存的问题:

  • 创建连接成功率低。UDP 的穿透性,NAT 局域网路由、交换机、防火墙等会禁止 UDP 443 通信,目前 QUIC 在国内建连的成功率大约在 95% 左右。
  • 运营商支持。运营商针对 UDP 通道支持不足,表现不稳定,例如 QoS 限速丢包,有些小运营商甚至不支持 UDP 包。

大厂的 QUIC 实践:腾讯微博阿里

IPv6 在中国一直非常沉寂,2017 年 IPv6 支持报告,中国只有 0.38% 的用户使用 IPv6。

2018 年,使用 IPv6 的中国用户已经增长到 3.15%,参考:《2018 年度 ipV6报告

IPv6 一定是今后的去世,推进 IPv6 后,大量的 IP 地址意味着可以告别各种 NAT、P2P、QUIC 的连接问题,阿里云和腾讯云,都做了大量 IPv6 的支持 工作。

微信客户端怎样应对弱网络

阿里以及日活网管通道架构演进

阿里巴巴 HTTP 2.0 实践及无线通信协议的演进之路

移动端监控

1、如何监控网络

插桩的方式,360 开源的性能工具 ArgusAPM 就是利用 Aspect 切换插桩,实现监控系统和 OkHttp 网络库的请求。

系统网络库的插桩实现,可以参考 TraceNetTrafficMonitor,主要利用 Aspect 的切面功能,关于 OkHttp 的拦截器可以参考 OkHttp3Aspect,它会更加简单一些,因为 OkHttp 本身有代理机制。

@Pointcut("call(public okhttp3.OkHttpClient build())")
public void build() {
}

@Around("build()")
public Object aroundBuild(ProceedingJoinPoint joinPoint) throws Throwable {
    Object target = joinPoint.getTarget();
    if (target instanceof OkHttpClient.Builder &&    Client.isTaskRunning(ApmTask.TASK_NET)) {
        OkHttpClient.Builder builder = (OkHttpClient.Builder) target;
        builder.addInterceptor(new NetWorkInterceptor());
    }
    return joinPoint.proceed();
}

插桩的缺点,主要在于不全面,非系统和 OkHttp 网络库,以及 Native 代码的网络请求,都无法被监控到。

Native Hook,相关方法:

连接相关:connect 发送数据相关:send 和 sendto 接受数据相关:recv 和 recvfrom

Socket 建连的堆栈如下:

java.net.PlainSocketImpl.socketConnect(Native Method)
java.net.AbstractPlainSocketImpl.doConnect
java.net.AbstractPlainSocketImpl.connectToAddress
java.net.AbstractPlainSocketImpl.connect
java.net.SocksSocketImpl.connect
java.net.Socket.connect
com.android.okhttp.internal.Platform.connectSocket
com.android.okhttp.Connection.connectSocket
com.android.okhttp.Connection.connect

统一网络库。

2、如何监控流量

监控流量,可以通过 TrafficStats 类,它是 Android Api 8 加入的接口,用于获取整个手机或者某个 UID 从开机算起的网路流量。参考 Facebook 的开源库 network-connect-class

getMobileRxBytes()        // 从开机开始 Mobile 网络接收的字节总数,不包括 Wifi
getTotalRxBytes()         // 从开机开始所有网络接收的字节总数,包括 Wifi
getMobileTxBytes()        // 从开机开始 Mobile 网络发送的字节总数,不包括 Wifi
getTotalTxBytes()         // 从开机开始所有网络发送的字节总数,包括 Wifi
// stats 接口提供各个 uid 在各个网络接口(wlan0, ppp0 等)的流量信息
/proc/net/xt_qtaguid/stats
// iface_stat_fmt 接口提供各个接口的汇总流量信息
proc/net/xt_qtaguid/iface_stat_fmt

TrafficStats 的工作原理,是利用 Linux 内核的统计接口,/proc/net/xt_qtaguid/stats 和 /proc/net/xt_qtaguid/iface_stat_fmt 对这两个接口进行读取和分析。Android 7.0 之后,已经不让开发者直接去读取 stats 文件,只能通过 TrafficStats 获取自己应用的流量信息。

手机的网络测试模式:

  • iPhone:“3001#12345#”
  • Android:“##4636##”

对于厂商,可以拿到更多的网络信息:

  • 网卡驱动层信息。如射频参数,用来判断 WiFi 的新号强度;网卡数据包队列长度,用来判断网络是否拥塞。
  • 协议栈信息,获取数据包发送、接受、时延和丢包信息。

大网络平台监控

网络问题的监控存在的问题:

  • 实时性。容易流失现场。
  • 复杂性。不同国家地区、运营商等都有可能不太。
  • 链路长。整个请求链路非常长,任何环节都可能出现问题。

监控主要从几个方面考量:

  • 客户端监控。时延、维度、错误。
  • 接入层监控。实时性、可靠性。
  • 监控报警。实时监控、离线监控。