6月阅读周·WebKit技术内幕 | 资源加载和网络栈
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读五个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》。
当前阅读周书籍:《WebKit技术内幕》。
资源加载和网络栈
WebKit资源加载机制
资源
网络和资源加载是网页的加载和渲染过程中的第一步,也是必不可少的一步。网页本身就是一种资源,而且网页一般还需要依赖很多其他类型的资源,例如图片、视频等。因为资源的加载涉及网络和资源的缓存等机制,而且它们在整个渲染过程中占的比例并不少。本章将介绍WebKit如何获取资源以及如何高效地管理资源。HTML支持的资源主要包括以下类型:
- HTML:HTML页面,包括各式各样的HTML元素。
- JavaScript:JavaScript代码,可以内嵌在HTML文件中,也可以单独的文件存在。
- CSS样式表:CSS样式资源,因为CSS代码除了可以内嵌在HTML文件之外,还可以以单独文件形式存在
- 图片:各种编码格式的图片资源,包括png、jpeg等。当然还有一些特殊的图片资源,例如SVG中所需的图片资源。
- SVG:用于绘制SVG的2D矢量图形表示。
- CSS Shader:支持CSS Shader文件,目前WebKit支持该功能。
- 视频、音频和字幕:多媒体资源及支持音视频的字幕文件(TextTrack)。
- 字体文件:CSS支持自定义字体,CSS3引入的自定义字体文件。
- XSL样式表:使用XSLT语言编写的XSLT代码文件。
资源缓存
资源的缓存机制是提高资源使用效率的有效方法。它的基本思想是建立一个资源的缓存池,当WebKit需要请求资源的时候,先从资源池中查找是否存在相应的资源。如果有,WebKit则取出以便使用;如果没有,WebKit创建一个新的CachedResource子类的对象,并发送真正的请求给服务器,WebKit收到资源后将其设置到该资源类的对象中去,以便于缓存后下次使用。这里的缓存指的是内存缓存。
资源加载器
按照加载器的类型来分,WebKit总共有三种类型的加载器。
第一种,针对每种资源类型的特定加载器,其特点是仅加载某一种资源。例如对于“image”这个HTML元素,该元素需要图片资源,对应的特定资源加载器是ImageLoader类。对于CSS自定义字体,它的特定资源加载器是FontLoader类。这些资源加载器没有公共基类,其作用就是当需要请求资源时,由资源加载器负责加载并隐藏背后复杂的逻辑。加载器属于它的调用者。
第二种,资源缓存机制的资源加载器的特点是所有特定加载器都共享它来查找并插入缓存资源——CachedResourceLoader类。特定加载器先是通过缓存机制的资源加载器来查找是否有缓存资源,它属于HTML的文档对象。
第三种,通用的资源加载器——ResourceLoader类,是在WebKit需要从网络或者文件系统获取资源的时候使用该类只负责获得资源的数据,因此被所有特定资源加载器所共享。它属于CachedResource类,但它同CachedResourceLoader类没有继承关系(这点容易混淆)。
Chromium多进程资源加载
工作方式和资源共享
资源请求有同步和异步两种方式。前面说了ResourceLoader类承担了Browser进程中有关资源的总体管理任务,对于同步和异步两种资源请求方式,ResourceLoader类使用SyncResourceHandle类和AsyncResourceHandle类来向Renderer进程发送状态消息,并接收Renderer进程对这些消息的反馈。
在Chromium中还有很多ResourceHandle的子类,它们的作用各异。
- RedirectToFileResourceHandler:继承自LayeredResourceHandle类,在接收到的数据转给另一个ResourceHandler类的同时,转存到文件。
- StreamResourceHandler:继承自LayeredResourceHandle类,在接收到的数据转给另一个ResourceHandler的同时,转存到数据流。
- CertificateResourceHandler:主要处理证书类的资源请求。
网络栈
WebKit的网络设施
WebKit的资源加载其实是交由各个移植来实现的,所以WebCore其实并没有什么特别的基础设施,每个移植的网络实现是非常不一样的。
从WebKit的代码结构中可以看出,网络部分代码的确是比较少的,它们都在目录“WebKit/Source/WebCore/platform/network”中。主要是一些HTTP消息头、MIME消息、状态码等信息的描述和处理,没有实质的网络连接和各种针对网络的优化。
磁盘本地缓存
为了适应网络资源的本地缓存需求,Chromium的本地磁盘缓存有几个特性或者要求。
- 虽然需要缓存的资源可能很多,但磁盘空间不是无限大的,所以必须要有相应的机制来移除合适的缓存资源,以便加入新的资源。
- 能够确保在浏览器崩溃时不破坏磁盘文件,至少能够保护原先在磁盘中的数据。
- 能够高效和快速地访问磁盘中现有的数据结构,支持同步和异步两种访问方式。
- 能够避免同时存储两个相同的资源。
- 能够很方便地从磁盘中删除一个项,同时可以在操作一个项的时候不受其他请求的影响。
- 磁盘不支持多线程访问,所以需要把所有磁盘缓存的操作放入单独的一个线程。
- 升级版本时,如果磁盘缓存的内部存储结构发生改变,Chromium仍然能够支持老版本的结构。
这些既是本地磁盘缓存的需要,同时也是Chromium的设计目标。
Cookie机制
Cookie是一项很“古老”的技术,因为比较简单易用,所以一直受到广泛的应用。Cookie格式就是一系列的“关键字+值”对,一个简单的例子如下:test1=webkit;test2=chromium;Expires=Sun,30 Oct 2016 21:35:00GMT;Domain=.myweb.com;例子中包括两个自定义的关键字,分别是“test1”和“test2”,它们的值分别为“webkit”和“chromium”。后面的则是预定义的关键字“Expires”和“Domain”,表示的是该Cookie的失效时间和该Cookie对应的域。基于安全性考虑,一个网页的Cookie只能被该网页(或者说是该域的网页)访问。
根据Cookie的时效性可以将Cookie分成两种类型,第一种是会话型Cookie(Session Cookie),这些Cookie只是保存在内存中,当浏览器退出的时候即清除这些Cookie。如果Cookie没有设置失效时间,就是会话型Cookie。第二种是持续型Cookie(Persistent Cookie),也就是当浏览器退出的时候,仍然保留Cookie的内容。该类型的Cookie有一个有效期,在有效期内,每次访问该Cookie所属域的时候,都需要将该Cookie发送给服务器,这样服务器能够有效追踪用户的行为。
高性能网络栈
Chromium的网络模块有两个重要目标,其一是安全,其二是速度。为此,该项目引入了很多WebKit所没有的新技术,这是一个很好的学习对象。
1、DNS预取和TCP预连接(Preconnect)
当用户正在浏览当前网页的时候,Chromium提取网页中的超链接,将域名抽取出来,利用比较少的CPU和网络带宽来解析这些域名或IP地址,这样一来,用户根本感觉不到这一过程。当用户单击这些链接的时候,可以节省不少时间,特别在域名解析比较慢的时候,效果特别明显。
2、HTTP管线化(Pipelining)
HTTP 1.1开始增加了管线化(Pipelining)技术。Chromium当然也支持这一技术,但它需要服务器的支持,两者配合才能实现HTTP管线化。HTTP管线化技术是一项同时将多个HTTP请求一次性提交给服务器的技术,因此无需等待服务器的回复,因为它可能将多个HTTP请求填充在一个TCP数据包内。HTTP管线化需要在网络上传输较少的TCP数据包,因此减少了网络负载。
3、SPDY
Chromium引入了新的机制——SPDY。SPDY就是为了解决网络延迟和安全性问题。根据Google的官方数据,使用SPDY协议的服务器和客户端可以将网络加载的时间减少64%,好消息是,在HTTP2.0的草案中将引入SPDY协议,将其作为基础来编写。
总结
使用网络栈来下载网页和网页中的资源是渲染引擎工作过程的第一步,也是非常消耗时间的步骤。本篇首先介绍网页的资源类型和WebKit的资源加载机制,然后剖析Chromium的多进程资源加载和缓存机制,以及高性能的网络技术。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)