嘉宾介绍
蘑菇街移动端资深开发工程师,曾就职于腾讯,百度,微软。专注移动端浏览器内核移植,开发以及优化。现在负责蘑菇街移动端跨平台组件开发。
为什么需要自有网络库
首先要介绍为什么需要一个自有的网络库,在应用开发过程中,
蘑菇街目前自有网络栈是基于Chromium网络库改造而来。



同时Chromium网络库对网络协议支持完善,
-
协议支持
目前Chromium网络库支持HTTP,HTTPS,
SPDY,QUIC,HTTP2.0等协议, 在目前所有网络库中所支持的协议是比较完全的, 并且会随着标准的演进,继续更新。 -
易用性
Chromium网络库中的主要接口是URLRequest 和 URLRequestContext,接口简单明了,
非常易于接入,但是由于接口都是异步接口, 在希望使用同步调用的时候,需要进行一些封装。 -
扩展性
Chromium网络库安装网络层次进行了代码划分,
因此可以根据需求在不同网络层进行扩展,满足业务需求。例如: 可以方便的扩展域名解析过程,TSL加密过程。 -
跨平台
Chromium网络库代码使用C++编写,
有非常强的跨平台能力, 但是由于Chromium有自己编译体系, 因此移植各个平台需要重新编写编译脚本。
网络库结构

当URLRequest被上层调用,而启动请求的时候,
当URLRequestHttpJob被创建后,
HttpNetworkTransaction使用HttpNe
最后进行套接字的建立。
基于自有网络路能解决的问题
有了自有网络库之后,
-
DNS劫持
DNS劫持是移动网络经常遇见的问题,
通常方案是采用HTTP协议访问自有DNS服务器,获取域名, IP映射,在访问域名的时替换成IP进行访问, 但是在访问HTTPS服务的时候,无法直接替换, 这个时候自有网络库就能发挥能力,再实现网络库的域名解析时, 不使用系统的域名解析过程,而是使用自己现实的域名解析方案, 从而获取正确域名解析,并且可以将这个过程提前, 避免在建连过程中访问域名解析服务,从而提高连接速度。 蘑菇街的网络库中就在网路库的HostResolverImpl
类中添加了ExternalResolver,通过ExternalResolver将链接所对应的IP返回, 如果ExternalResolver所执行的HTTP DNS失败之后,会采用正常DNS解析。 
-
代理转发
在电商类应用中,为了适用业务的快速迭代,会使用混合开发,
这样就是使用的系统WebView, 然后系统WebView有自己网络库实现, 因此很多针对网络库的定制和优化讲无法使用。解决这类问题, 可以使用基于自有网络库实现的代理服务, 将WebView的网络请求代理到自有网络库上,再进行转发。 蘑菇街在处理系统WebView请求的时候,
为系统的WebView设置代理,将请求发送至本地端口。 同时在网络库中实现了一个Http Proxy Server,能转发所监听端口的http,https请求, 所有接收到的http,https请求, 可以经过自己的网络库转发出去,这样所有自有网络库的修改, 优化都可以生效。 -
网络调试
网络调试是网络开发过程中一个非常棘手的事情,
自定义网络库有着非常大的灵活性, 在自定义网络库的过程中可以实现Chrome Dev Tool的协议与Chrome浏览器进行通信, 这样就能通过Chrome浏览器的Dev Tool进行网络调试,能直观的看到网络数据,以及耗时等信息。 蘑菇街的网络库接口封装形式与HttpURLConnectio
n一致,这样基于facebook提供的stetho开源库, 使用其中的com.facebook.stetho. urlconnection这个包, 将stetho接入的自己的网络库以实现Android与Chr ome浏览器的通信。 -
自定义协议
HTTP协议在使用过程中有着不少缺陷,例如:
HTTP协议非长连接,每次请求需要重新握手, 这是一个非常消耗时间的过程。 为了解决HTTP在之前设计过程中的不足之处, 出现了很多解决方案,如SDPY,HTTP2.0等。 但是此类的在部署,以及标准话过程中并不完善, 因此自定义协议是更符合业务需求的。例如: 蘑菇街针对现有业务场景,对TLS进行了改造, 更换SSL加密算法,将RSA跟换为ECDHE, ECDHE加密算法较RSA速度更快。
自有网络库实现过程
Chromium网络库剥离
Chromium的网络库虽然非常强大,
-
base:Chromium基础类库
-
nss:加密库
-
icu:Unicode支持库
-
zlib:压缩解压库
-
protobuf:Protocol Buffers
-
modp_b64:base64库
-
brotli:brotli压缩算法库
-
url_lib:url解析库
针对不同平台,需要建立不同的编译工程,例如Android iOS,在Chromium的编译过程中,
-
编译过程问题
其原因是单独编译出来的网络库与openssl库中有同名函数冲
突,解决这个问题需要修改openssl, 通过在头文件中通过宏定义修改函数名,替换掉库中所有同名函数。 -
运行问题
通过Chromium编译系统编译出来的iOS端网络库,
会存在无法运行的情况, 其原因是xcode编译与Chromium的编译有冲突, 解决这个问题,需要根据Chromium网络库的编译文件, 生成xcode工程进行编译。
相对于iOS平台,Android平台的编译相对简单,
网络库封装接入
在使用Chromium网络库过程中,
以Android平台为例,
对于Chromium网络库是一个异步网络库,
包大小问题解决
Chromium网络库编译封装完成之后,
蘑菇街网络库架构

蘑菇街网络库被分为三层,最低层为协议支持层,提供对基本协议,
Q:使用自定义网络库有哪些弊端?
A:包大小会增加,但是可以以动态加载方式解决。初始接入成本较高,需要改动底层代码,但是后期回报也会很高。
Q:请问技术开源吗?
A:chromium整个代码是开源的,我们自己的改动暂时没有开源,未来可能会考虑将自己代码开源出来
Q:支持本地缓存吗?无网可以访问吗?
A:支持本地缓存,并且我们讲本地缓存作为单独模块独立出来,可以提供给应用端其他模块使用。无网络情况下是不支持访问的。
Q:整套方案涉及到的库加起来,客户端体积增加有多大?
A:整个方案下来的库加起来增加了3m左右。iOS上经过stripe,以及手工剥离无用代码,整个网络库也是在3m左右。
Q:在iOS 平台,系统7及以下不支持动态库,是降级到系统库还是有别的办法?
A:在iOS上是不支持动态加载,整个网络库会直接链接到应用中。
Q:选择这种方案的初衷是什么,比起其他三方网络库有什么特别,就本次分享看来更优之处主要是跨平台意见方便用chrome调试,还有其他的目的么?
A:这个方案初衷是希望提升网络性能,并且能有更高的灵活性。对于其他第三方网络栈,chromium的网络栈有更多的协议支持,更高性能cache,还有预期功能,比如http预链接,dns预取等功能,不仅仅是方便调试。
Q:以前没接触这快,想咨询一下,这部分内容能应用到WebView的优化吗?
A:WebView优化有很多方面,如果有自己能修改内核的话,能做的就非常多,比如网络,渲染性能等等。如果是使用系统WebView的话,可以单独剥离Chromium网络栈优化之后,和我们做的那样使用chromium网络栈实现代理服务,通过走自己的网络栈来优化网络。
Q:为了减小让网络库的大小对应用的体积的影响,使用了动态加载机制,蘑菇街自有网络库在线下载,动态安装。-下载之后不也增加了应用的大小吗?并且也会增加网络请求,那么这么做的好处又是什么呢?
A:应用大小在动态加载之后会增加,但是对于应用的安装包来说是不会增加的,好处自然是能获得更好的网络性能,并且能扩展自己应用在的网络栈上面的需求。
Q:总的来说表述的基本上都对,对 TLS 进行改造说的就有点牵强了。TLS 在 Handshake 时有个Prefer Ciphers,这个在Server端可配置.... 优先EECDH即可…那么我的问题来了:蘑菇街有计划在自己业务中使用QUIC这类基于UDP的协议么? 我看你们RTT耗时非常长啊 ,蘑菇街在移动端有对SSL Session Cache或者SSL Session Ticket做过修改或者优化么? 或者已经做了那些优化?
A:我们现在正在调研使用spdy协议,spdy的tls握手过程比较耗时,目前正在尝试优化tls握手过程,希望能做到1-RTT或者0-RTT。
Q:上面提到网络架构有三层,是不是指最下面的协议支持层主要是chrominum项目中的代码,而蘑菇街实现自身需求的部分主要在扩展层中,后期如果chrominum代码版本更新了,只需更新最下面的协议支持层即可?另外扩展层对协议支持层的扩展,主要用什么设计模式实现的?
A:我们目前协议支持层在chromium源码中,主要扩展是在外层,这样会比较方便升级。协议支持层的改动,还是基本chromium代码本身进行修改
Q:因为现在的应用大部分做原生的,只是部分用HTML,所以用的都是系统的webview,现在主要是想对webview优化,加快访问速度,除了单独剥离Chromium网络栈优化,还能给分享一下其他优化点吗?
A:如果不对webview动手的话,能做的优化有限,我们目前使用自己的内核,所以能做系统webview不能做的优化。使用系统webview的话,可以根据需求做predict,prefetch。
Q:直接使用IP发请求是否可以避免dns劫持的问题?
A:直接使用ip时能避免劫持,但是在访问https的时候,需要域名不能直接使用ip,所以我们在域名解析的代码进行了hook插入自己的代码。
Q:启动后从服务器端下载动态库可能存在失败的情况,这时肯定是要用系统网络库,我猜测,蘑菇街app业务层调用的是自定义的一套网络访问接口,下面有基于chromium这一套和用系统接口的两套实现,在动态库没有正确加载时使用系统库的实现,对上层透明,是这样吗?
A:基本是你所说的,但是我们没有使用自定义的网络接口,我们用的还是希望标准的网络接口,我们在中间做了一层封装,会根据情况选择网络库。
由InfoQ主办的GMTC全球移动技术大会将于6月24日在北京召开。来自BAT、携程、滴滴、微博、和社区的技术专家联袂分享,主题包括应用架构、性能优化、动态化、插件化、Swift、React Native、Weex等,为中高级移动开发工程师献上一场技术盛宴!目前8折优惠期间,多人团购更多优惠,欲购从速!
