1 网络资源
资源加载器
网页加载中需要获取的资源文件很多,有HTML,JavaScript,CSS,图片,SVG,CSS Shader,视频音频字幕,字体文件,XSL样式表等。
不同资源有不同的加载器,主要分为三类:
- 特定资源加载器:处理特定资源请求,如ImageLoader FontLoader
- 缓存资源加载器:从内存中获取资源,CachedResourceLoader
- 通用资源加载器:从网络或硬盘中获取资源,ResourceLoader
同步与异步
HTML解析时,可能会碰到很多资源需要加载。这个时候,CSS和PNG等资源会开启异步线程去加载,不会打断HTML解析。
而JS文件则会阻塞HTML解析,因为JS可能会改变DOM结构。虽然WebKit使用了预取技术来优化这个问题,但还是建议将js文件放在HTML的末尾。
缓存机制
Webkit使用了一个资源缓存池来缓存资源。以URL为key,就算资源内容完全相同,URL不同也会认为是两个不同的资源。
每次资源请求时,先在缓存池中寻找资源,未命中才发起http等请求。这里也会遇到大多数缓存策略碰到的问题,资源生命周期和新鲜度检测机制。
由于缓存池大小受限,故需要将一些资源移出缓存池,Webkit中采用的LRU算法,最近最少使用的会被移除。
由于网络资源可能被更改,故需要检测资源是否还是新鲜的。这里采用了http协议中对于cache的处理。首先根据服务端返回的max-age或expires字段来判断资源是否有效。如果超出这个时间段,则发起if-modified-since请求。服务端可能有三种返回:
- 304 not modified,资源未更改,不用重新下载了
- 200 ok,资源更改了,并且将新的资源回传了
- 404 not found,资源被删除了
2 资源加载过程
以img src=”https://baidu.com/imags/1.png” />为例,大概流程如下
- HTML解析到img的src属性时,创建一个ImageLoader对象,并开启另一个线程
- ImageLoader创建一个加载资源的CachedResourceRequest
- 创建一个CachedResourceLoader对象,先从memoryCache中查找缓存,命中则结束
- 未命中,则创建ResourceRequest和ResourceLoader,从disk或网络上获取资源。
- 资源获取后经过ResourceHandle处理,得到结果返回给ImageLoader
3 Chromium资源加载
Chromium的Renderer进程中不加载资源,交给Browser进程代理。这样的好处有
- 可以利用沙箱模型,提高安全性
- 资源加载统一由Browser进程发起,使得多个页面共享资源变得很easy
- 由Browser进程统一管理资源,避免多个页面发起多个http请求而使浏览器负载过大
资源加载管理器
Chromium中利用ResourceScheduler类来管理资源加载,它会根据request的优先级来调度URLRequest。对于优先极高,同步请求和具有SPDY能力的服务器,会优先调度。
4 网络栈
Chromium通过URLRequest进行网络资源请求,它的主要流程如下
- 根据scheme来判断资源类型,创建不同的URLRequestJob,对于http://,创建URLRequestHttpJob
- URLRequestHttpJob创建后,从cookie中获取与该URL相关的用户信息
- HttpCache先从本地磁盘中查找资源,命中则直接结束
- 未命中,则发起http请求。HttpCache创建HttpTransaction对象,开启一个Http连接事务
- HttpNetworkTransaction使用HttpNetworkSession管理连接会话,建立TCP socket
- 创建HttpStream对象,处理网络数据流的读取
- 创建套接字StreamSocket,其中的SSLSocket支持HTTPS请求。
域名解析
Chromium采用HostResolveImpl来做域名解析。域名解析是阻塞式的,故会重新开启一个线程。得到的DNS会由HostCache保存起来。
磁盘缓存
Chromium中有两个类来描述磁盘缓存。Backend代表了缓存表,可以快速查找缓存位置。Entry代表了缓存表中表项,以URL为key。读写表项都由Entry类来处理。同样使用LRU算法来回收缓存空间。
cookie
用于在浏览器端保存数据,H5出现后,还可以利用LocalStorage来保存数据。cookie是一连串的key: value对,如下面例子:
test1=webkit;test=chromium;Expires=Sun,30 oct 2016
- 1
- 2
根据保存位置不同,分为两类
- 会话型,保存在内存中,浏览器退出会清除
- 持久型,保存在磁盘中,浏览器退出不会清除
5 高性能网络栈
Chromium做了很多策略来优化网络资源加载,主要有下面这些。
DNS预取
DNS未获取之前,http请求不可能发送到相应服务器。可见域名解析会阻塞资源加载。Chromium采取了DNS预取机制来优化这个问题。当HTML展现后,浏览器会利用少量资源,针对HTML中的URL链接提前做DNS解析。这样当用户点击链接时,已经得到了服务器的DNS了。
多个超链接,每个会开启一个单独的线程,并发执行。另外,当用户在浏览器地址栏中输入URL时,浏览器也会根据之前历史URL来做相关匹配,然后做DNS预取。
TCP预连接
与DNS预取类似,浏览器也可以提前对HTML中的超链接进行TCP连接。当用户点击时,TCP就已经连接好了。这也会加快网络资源获取速度。
HTTP管线化pipelining
在HTTP1.1中,加入了管线化的支持,pipelining技术建立在keep-alive前提下,需要服务器支持。传统方式下,上一次的response得到后,才能发起下一次的request。而pipelining技术则可以将多个request按照先后次序一起发送到服务端,而不必等待上一次的response。这需要服务端按照次序返回response。
SPDY
定义在HTTP层和TCP层的协议,属于会话层。它的特点如下
- 采用多路复用技术,一个连接可以传输网页中众多资源。
- 根据资源特性和优先级,可以调整资源请求的优先级。比如JS文件下载优先级就比较高
- 利用压缩技术,减小包大小
- 服务器在发送网页内容时,可以尝试发送一些信息给浏览器,告诉后面可能还有那些资源,浏览器可以提前知道并决定是否需要下载。更极端的是,服务器可以主动将这些未请求的资源发给浏览器。