7. 代理
当用户设置代理时,用户代理依赖以下类来处理。
图 4-17 不仅描述上面这些类,同时也描述了 Chromium 中获取网络代理的过程。图中数据表示获取网络代理的次序,其中的分支 3.1 和 4.1 分别表示简单的代理设置和代理脚本设置的处理过程。
8. 磁盘本地缓存
如果没有磁盘缓存,当用户访问网页的时候,每次浏览器都要需要从网站下载网页,图片、js 等资源,这其实费力又不讨好。解决这一问题的方法就是将之前浏览器下载的资源保存下来,存到磁盘中,以备今后使用。当然,资源是有时效性的,也会变得不再有效,所以需要有相应的退出机制来解决这一问题。目前大多数浏览器都有磁盘缓存机制,因为缓存机制确实能够提高网页的加载速度。
8.1 特性
为了适应网络资源的本地缓存需求, Chromium 的本地磁盘缓存有几个特性或者要求。
- 虽然需要缓存的资源可能很多,但磁盘空间不是无限大的,所以必须要有相应的机制来移除合适的缓存资源,以便加入新的资源。
- 能够确保在浏览器崩溃时不破坏文件,至少能够保护原先在磁盘中的数据。
- 能够高效和快速地访问磁盘中现有的数据结构,支持同步和异步两种访问方式。
- 能够避免同时存储两个相同的资源。
- 能够很方便地从磁盘中删除一个项,同时可以在操作一个项的时候不受其他请求的影响。
- 磁盘不支持多线程访问,所以需要把所有磁盘缓存的操作放入单独的一个线程。
- 升级版本时,如果磁盘缓存的内部存储结构发生改变, Chromium 仍然能够支持老版本的结构。
8.2 结构
内部结构主要有个类:Backend 和 Entry 。 Backend 类表示整个磁盘缓存,是所有针对磁盘缓存操作的主入口,表示的是一个缓存表。Entry 类指的是表中的表项。缓存通常是一个表,对于整个表的操作作用在 Backend 类中,包括创建表中的一个个项,每个项由关键字来唯一确定,这个关键字注是资源的 URL。而对项目内的操作包括读写等都是由 Entry 类来处理。
9. Cookie 机制
Cookie 格式就是一系列的 “关键字+值” 对,一个简单的例子如下:
test1 = webkit; test2 = chromium, Expires = Sun, 30 Oct 2016 21:35:00 GMT; Domain = .myweb.cm;
例子中包括两个自定义的关键字,分别是 “test1” 和 “test2” ,它们的值分别为 “webkit” 和 “chromium” 。后面的则是预定义的关键字 “Expires” 和 “Domain”,表示的是该 Cookie 的失败时间和该 Cookie 对应的域。基于安全性考虑,一个网页的 Cookie 只能被该网页(或者说是该域的网页)访问。
根据 Cookie 的时效性可以将 Cookie 分成两种类型,第一种是会话型 Cookie (Session Cookie)。如果 Cookie 没有设置失效时间,就是会话型 Cookie。第二种是持续型 Cookie (Persistent Cookie),也就是当浏览器退出的时候,仍然保留 Cookie 的内容。该类型的 Cookie 有一个有效期,在有效期内,每次访问该 Cookie 所属域的时候,都需要将该 Cookie 发送给服务器,这样服务器能够有效追踪用户的行为。
Chromium 中支持 Cookie 的机制也较为简单和清晰,如图 4-23 所示的是 Chromium 所设计和使用的主要类及其关系。CookieMonster 是Cookie 机制中最重要的类,实际上相当于 Cookie 管理器。
它包括几个作用:
第一是实现 CookieStore 的接口,它是对外的接口,调用者可以设置和获得 Cookie;
第二是报告各种 Cookie 的事件,例如更新信息等,主要使用 Delegate 类,
第三是 Cookie 对象的集合,也就是 CanonicalCookie 的集合,每个 CanonicalCookie 对象都是保存在内存中的,当需要存储到磁盘的时候使用 PersistentCookieStore 类,具体由 SQLitePersistentCookieStore 类负责实际的存储动作。
10. 安全机制
HTTP 是一种使用明文来传输数据的应用层协议。构建在 SSL 之上的 HTTPS 提供了安全的网络传输机制,现已被广泛应用于网络上。典型的是电子商务、银行支付方面的应用。基本上所有的浏览器都支持该协议, Chromium 当然也不例外。
不仅如此,Chromium 也支持一种新的标准,这就是 HSTS (HTTP Strict TransportSecurity)。该协议能够让网络服务器声明它只支持 HTTPS 协议,所以浏览器能够理解服务器的声明,发送基于 HTTPS 的连接和请求。通常情况下,浏览器用户不会输入 “scheme(http://)”,浏览器的补充功能通常会加入该 “scheme” ,但是,服务器可能需要输入 ”https://“ 。在这样子的情况下,该协议就显得非常有用。一般情况下,服务在返回的消息头中加入以下信息表明它支持该标准:
Strict-Transport-Security: max-age=16070400; includeSubDomains
11. 高性能网络栈
Chromium 的网络模块有两个重要目标,其一是安全,其二是速度。为此,该项目引入了很多 WebKit 所没有的新技术。
11.1 DNS 预取和 TCP 预连接(Preconnect)
一次 DNS 查询的平均时间大概是 60 ~ 120ms 之间或者更长,而 TCP 的三次握手时间大概也是几十毫秒或者更长。为了有效减少这段时间,Chromium 引入了 DNS 预取和 TCP 预连接,它们都是由 Chromium 的 ”Predictor“ 机制来实现的
。
首先是 DNS 预取技术。它的主要思想是利用现有的 DNS 机制,提前解析网页中可能的网络连接。具体来讲,当用户正在浏览当前网页的时候,Chromium 提取网页中的超链接,将域名抽取出来,利用比较少的 CPU 和网络带宽来解析这些域名或者 IP 地址,这样一来,用户根本感觉不到这一过程。当用户单击这些链接的时候,可以节省不少时间,特别在域名解析比较慢的时候,效果特别明显。
DNS 预取技术是利用系统的域名解析机制,好处是它不会阻碍当前网络栈的工作。DNS 预取技术针对多个域名采取并行处理的方式,每个域名的解析须由新开启的一个线程来处理,结束后此线程即退出。
当然, DNS 预取技术不仅应用于网页中的超链接,当用户在地址栏中输入地址后,候选项同输入的地址很匹配的时候,在用户敲下回车键获取网页之前, Chromium 已经开始使用 DNS 预取技术解析该域名了。
Chromium 使用追踪技术来获取用户从什么网页跳转到另外一个网页。可以利用这些数据,一些启发式规则和其他一些暗示来预测用户下面会单击什么超链接,当有足够的把握时,它便先 DNS 预取,更进一步,还可以预先建立 TCP 连接。听起来够智能的吧,是的。但是这对用户的隐私是一个极大的挑战,它甚至能预测你单击什么超链接。
同 DSN 预取技术一样,追踪技术不会应用于网页中的超链接,当用户在地址栏中输入地址,如候选项同输入的地址很匹配,则在用户敲击下回车键获取该网页之前,Chromium 就已经开始尝试建立 TCP 连接了。
11.2 HTTP 管线化(PipeLining)
很多时候,服务器和浏览器通话是按顺序来的,浏览器发送一个请求给服务器,等到服务器的回复后,才会发送另外一个请求。弊端是效率极差。
HTTP 1.1 开始增加了管线化技术,Chromium 也支持这一技术,但它需要服务器的支持两者配合才能实现 HTTP 管线化。HTTP 管线化技术是一项同时将多个 HTTP 请求一次性提交给服务器的技术,因此无需等待服务器的回复,因为它可能将多个 HTTP 请求填充在一个 TCP 数据包内。HTTP 管线化需要在网络上传输较少的 TCP 数据包,因此减少了网络负载。
图4-24 描述了 HTTP 管线化技术是如何传送请求和回复的。
请求结果的管线化使得 HTML 网页加载时间动态提升,特别是在具体有高延迟的连接环境下。在速度较快的网络连接环境下,提速可能不是很明显。因为这些请求还是有明显的先后顺序的。管线化机制需要通过永久连接(Persistent Connection)完成,并且只有 GET 和 HEAD 等请求可以进行管线化,使用场景有很大的限制。
11.3 SPDY
SPDY 就是为了解决网络延迟和安全性问题。根据 Google 的官方数据,使用 SPDY 协议的服务器和客户端可以将网络加载的时间减少 64。
SPDY 协议是一种新的会话层协议,因为网络协议 是一种栈式结构,它被定义在 HTTP 协议和 TCP 协议之间,SPDY 协议的核心思想 是多数复用,仅使用一个连接来传输一个网页中的众多资源。
11.4 QUIC
QUIC 是一种新的网络传输协议,主要目标是改进 UDP 数据协议的能力。同 SPDY 建立在传输层之上不同,QUIC 所要解决的问题就是传输层的传输效率,并提供了数据的加密,所以,SPDY 可以在 QUIC 之上工作。
12. 实践:高效的资源使用策略
WebKit 和 Chromium 为了高效率地下载资源,设计出了各种各样的策略和新技术。
## 12.1 DNS 和 TCP 连接
DNS 和 TCP 连接占用大量的时间,所以为了高效地加载网页,网页开发者可以从以下方面着手改变以减少这一部分的时间。
- 减少链接的重定向。有些网页中使用了大量重定向,可能还会有很多次重定向,还不仅要求浏览器建立多次链接,同时还需要多次 DNS 解析,这会阻碍 DNS 预取技术的应用,应该尽量避免。
- 利用DNS预取机制。网页的开发者当然知道需要链接的 URL,为了让浏览器也知道这些链接,开发者可以指定需要预取的 URL。
- 搭建支持 SPDY 协议的服务器,当然指的是那些需要使用 HTTPS 协议的网站。
- 避免错误的链接请求。有些网页中包含了一些失效的链接,当浏览器试图获取该链接对应的资源的时候,就会占用网络资源。
12.2 资源的数量
我们也可以通过减少网页中所需的资源数量来改善网页的加载:
- 在 HTML 网页中内嵌小型的资源,也就是当资源比较小的时候,可以将它们直接放在网页中,可能的资源如 CSS、JavaScript 和图片等。比如图片 用 webpack 直接打包成 base64。
- 合并一些资源,例如 CSS、JavaScript 和图片。常见的就是一些网页中大量使用的小图片,可以将它们合并成一张大的图片以供使用。
12.3 资源的数据量
对于每个资源而言,通过减少它的数据量来提高网页的加载速度:
- 使用浏览器本地磁盘缓存机制。因为 HTTP 协议支持资源的失效机制,通过对资源设置适当的失效期来减少浏览器对资源的重复获取。
- 启用资源的压缩技术。例如,对于图片而言,可以使用 zip 压缩技术,然后在 HTTP 消息头中说明该资源经过压缩,这样可以有效减少网络传输的数据量。