【Android】移动端接入Cronet实践

移动端接入Cronet实践

    • QUIC协议
    • 获取Chromium源码
    • 编译Cronet
      • Android / iOS builds
      • Desktop builds (targets the current OS)
      • Running the ninja files
      • 生成的文件
    • 使用Cronet
      • Android Debug Log
        • See VLOG(1) and VLOG(2) logging:
        • See VLOG(1) logging:
        • See NO (only FATAL) logging:
      • Network Log
    • GN语法
    • Cronet request lifecycle
    • 参考资料

QUIC协议

QUIC, a multiplexed stream transport over UDP 是Chromium使用的通信协议,是基于UDP实现的类似于 TCP+TLS+HTTP/2 的协议。也是HTTP 3.0的设计方案。有兴趣的话大家可参考文档: Playing with QUIC

Chromium项目是开源的,The Chromium Projects(http://dev.chromium.org/chromium-projects) 文档详细介绍了Chromium项目的实现原理,以及如何获取源码并进行编译。

Cronet 库是Chrome使用的移动端网络库。支持 HTTP、HTTP/2 以及 QUIC 协议。支持 Android 和 iOS 平台。 其编译工具是 gn 和 ninja,类似于 cmake 与 make 的关系。 下面介绍 Cronet 库的编译及编译注意事项。

获取Chromium源码

可以参考官方文档:Checking out and building Chromium for Mac

获取源码之前,首先需要下载安装 depot_tools 工具。在一个适当的目录下clone depot_tools包:

$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git

depot_tools的路径(最好是绝对路径,~需替换为$HOME)加进环境变量PATH中,假设 depot_tools 工程在/path/to/depot_tools目录下:

export PATH=$PATH:/path/to/depot_tools

如果从来没有下载过Chromium的代码的话,为源码创建一个文件夹并下载源码:

mkdir chromium && cd chromium
fetch --no-history chromium       # 可能花费30min或几小时,依网络速度而不同

–no-history 可以节省代码下载时间,它忽略仓库的历史信息;整个代码量较大,约 14G,且需要,1M 左右的速度需要 20~30 分钟。若中间拉取失败,可以执行 gclient sync 继续拉取, 拉取结束后,该目录会生成一个 src 目录,包含 cronet 源码。

获取源码是很漫长的过程,Chromium项目的源码有8G。我花了2个半小时才完成。中途遇到了download_from_google_storage.py 下载文件没反应的问题,参考了Chromium的源码获取与编译(2018-06-06)

命令行,需要使用 http proxy 配置:

export http_proxy=http://127.0.0.1:8118 
export https_proxy=http://127.0.0.1:8118 

// git代理,我没有使用
git config --global http.proxy 'socks5://127.0.0.1:1080' 
git config --global https.proxy 'socks5://127.0.0.1:1080' 

启动 privoxy 转换 socks5 为 http proxy:

privoxy --no-daemon /usr/local/etc/privoxy/config

编译Cronet

Cronet的源码位于 src/components/cronet 目录,官方编译流程可参考Cronet build instructions,编译环境要求:

  • Linux:python 2.7.5,及 jdk 8,较高版本由于接口不兼容需避免使用。
  • Mac: python 2.7.5,jdk 8,以及 xcode。 进入 chromium/src 目录,执行下面命令

Android / iOS builds

$ ./components/cronet/tools/cr_cronet.py gn --out_dir=out/Cronet   # 生成 ninja 文件

如果主机是linux,build的是Android的库。如果主机是MacOS,build的是ios库。以下命令在Mac上会生成 cronet 静态库,目录 obj/components/cronet/ios/libcronet*.a

Desktop builds (targets the current OS)

gn gen out/Cronet

Running the ninja files

输入 ninja 文件执行编译,必须指定为 cronet_package

$ ninja -C out/Cronet cronet_package

这个命令会编译 cronet 模块,及其依赖的所有模块,包括base,crypto,boringssl,protobuf,url等。编译Cronet库,最终文件可以在out/Cronet/cornet中寻找。

生成的文件

iOS 库:

  • out/MyCronet/obj/components/cronet/ios/libcronet.a, 大小为 89M;
  • out/MyCronet/obj/components/cronet/ios/libcronet_static.a, 大小为 17M;
  • out/MyCronet/obj/components/cronet/ios/libcronet_deps_complete.a,大小为 1G。

Android 库:

  • out/Cronet-android/lib.java/components/cronet/android/ 所有 jar 包在此目录下,一般不用;
  • out/Cronet-android/cronet/ 需要使用cronet库的java API的so、及jar包都在此目录下
  • out/Cronet-android/libcronet.77.0.3825.0.so,strip 后的库,6M;
  • out/Cronet-android/lib.unstripped,未 strip 的库在此目录下。50.9M;
  • out/Cronet-android/gen/components/cronet/android/cronet_jni_registration.h,该文件便是自动生成的JNI头文件

使用Cronet

Android Debug Log

C++层的日志可以通过 adb logcat 输出. 但是默认只显示 FATAL 级别的日志。如果想要更改日志输出级别,可以通过以下命令打开chromium日志输出:

See VLOG(1) and VLOG(2) logging:

$ adb shell setprop log.tag.CronetUrlRequestContext VERBOSE

See VLOG(1) logging:

$ adb shell setprop log.tag.CronetUrlRequestContext DEBUG 

See NO (only FATAL) logging:

$ adb shell setprop log.tag.CronetUrlRequestContext NONE

Network Log

NetLog 是 Chromium 网络模块的日志:NetLog: Chrome’s network logging system。可以使用下面两行代码操作 dump 出 chromium 网络模块的日志:

CronetEngine.startNetLogToFile()
CronetEngine.stopNetLog()

Chromium网络模块的日志输出内容是 JSON 格式的,所以需要主动调用 stopNetLog() 方法保证 JSON日志的完整性。

导出的log文件可以使用 https://netlog-viewer.appspot.com/#import 分析。如果打不开,可以使用 Chromium NetLog dump viewer 这个Chrome扩展程序。

GN语法

参考 GN Quick Start guide 和 GN Language and Operation,建议大家先了解下target的概念,因为gn命令操作的基本上都是target。

// 列出指定构建目录下所有的targets
$ gn ls out/Default 

// 查看当前环境的配置参数
$ gn args --list out/Default

// 显示关于一个给定target或config的信息。
// usage:  gn desc  
$ gn desc out/Default net

// 查找两个taregets之间的依赖路径
$ gn path out/Default //base //net --all

// 查找反向的依赖(也就是依赖此target的其他targets)
$ gn refs out/Default/ //net

// 检查头文件依赖的有效性
$ gn check out/Default/ //net

Cronet request lifecycle

[外链图片转存失败(img-Oj5Ub8tT-1564461659657)(/gallery/android_commoncronet-lifecycle.svg)]

$ ninja -help
usage: ninja [options] [targets...]

if targets are unspecified, builds the 'default' target (see manual).

options:
  --version  print ninja version ("1.8.2")

  -C DIR   change to DIR before doing anything else
  -f FILE  specify input build file [default=build.ninja]

  -j N     run N jobs in parallel [default=6, derived from CPUs available]
  -k N     keep going until N jobs fail [default=1]
  -l N     do not start new jobs if the load average is greater than N
  -n       dry run (don't run commands but act like they succeeded)
  -v       show all command lines while building

  -d MODE  enable debugging (use -d list to list modes)
  -t TOOL  run a subtool (use -t list to list subtools)
    terminates toplevel options; further flags are passed to the tool
  -w FLAG  adjust warnings (use -w list to list warnings)

参考资料

  • Cronet build instructions - Google chromium官方文档 Cronet源码
  • QUIC协议初探-iOS实践
  • 蘑菇街 App Chromium 网络栈实践
  • Cronet 库编译指南
  • Employing QUIC Protocol to Optimize Uber’s App Performance - Uber Engineering Blog
  • Cronet request lifecycle
  • QQ 空间已在生产环境中使用 QUIC 协议
  • Cronet should provide Native API for use on Android and iOS

你可能感兴趣的:(Android,Linux)