《WebKit技术内幕》知识提炼 —— 资源加载和网络栈

WebKit 资源加载机制

资源

HTML 支持的资源主要包括:HTML、JavaScript、CSS、图片、SVG、CSS Shader、视频、音频、字幕、字体、XSL样式表等

这些资源在 WebKit 中都有不同的类表示,公共基类是 CachedResource。其中 HTML文本的类型为 MainResource,对应的资源类型叫 CachedRawResource类。

资源缓存

基本思想是建立一个缓存池,优先从缓存池中读取数据。这里的所说的缓存池是内存缓存。WebKit 从资源池中查找资源的关键词是URL,只要URL不同,就被认为是两个不同的资源

资源加载器

分为三种:

  • 针对每种资源类型的特定加载器,仅加载某一种资源,并且没有公共基类,加载器属于它的调用者。如 ImageLoader 属于 HTMLImageElement
  • 缓存机制加载器 CachedResourceLoader 所有特定加载器都共享它来查找并插入缓存资源,属于 HTML 文档的对象
  • 通用资源加载器 ResourceLoader类,WebKit 需要从网络或者文件中获取资源的时候使用,只负责获取资源数据

过程

资源加载过程

例子:有一个 “img” 元素,“src”是一个有效 URL 地址,当 HTML 解释器解析到该元素的该属性,WebKit 会创建一个 ImageLoader 对象来加载该资源,ImageLoader创建一个缓存资源请求CachedResourceRequest,并调用CachedResourceLoader 查找缓存资源,如果命中缓存则返回给调用者,如果没有命中则创建一个资源请求ResourceRequest ,并且调用通用资源加载器加载资源,具体到下面的 ResourceHandleInternal ,依赖于每个 WebKit 移植的实现策略。

存在默写资源会阻塞主线程渲染过程,当前的主线程渲染被阻塞时,WebKit 会启动另外一个线程去遍历后面的 HTML 网页,收集需要的资源 URL,并可以并发下载这些资源。

资源的生命周期

资源池使用 LRU(Least Recently Used 最近最少使用)算法,并且在这个基础上添加了协商缓存。如果命中缓存,那么发送一个 HTTP 请求给服务器,说明资源在本地的一些信息,如资源更新时间,服务器根据信息判断,如果没有更新,则返回状态码 304,那么直接使用原资源;否则下载最新资源。

Chromium 多进程资源加载

Renderer 进程在网页的加载过程中需要获取资源,但由于安全性(沙箱模型打开的时候,Renderer进程是没有权限获取资源的)和效率上(资源可以共享),Renderer 进程的资源获取实际上是通过进程间通信将任务交给 Browser 进程来完成。

网络栈

WebKit 的资源加载的优化其实是交由各个移植来实现的,WebCore 没有什么也别的基础设施,每个移植的网络实现是非常不一样的。

网络栈的基本组成

除了 HTTP 协议、DNS 解析等,还包含了 Chromium 为了减少网络时间而引入的新技术,例如 SPDY,QUIC

网络栈结构
网络栈调用过程
  1. URLRequest 类被调用时,会根据 URL 的 “scheme”(协议类型,如:“http://”,“file://”等) 来决定要创建什么类型的请求,Chromium 使用工厂模式处理不同类型的请求,例如 “http://” 类型则会使用 URLRequestJobManager 创建一个 URLRequestJob 类,具体使用哪个工厂则是一个责任链模式,优先判断是否是用户自定义的 “scheme” 。
  2. URLRequestJob 被创建后,先从 Cookie 管理器中获取与该 URL 相关的信息,之后使用 HttpTransactionFactory 对象创建 HttpTransaction 对象开启一个 Http 连接的事务。如果请求对应的回复已经在磁盘缓存中,那么 Chromium 无需再建立 HttpTransaction
  3. HttpNetworkTransaction 使用 HttpNetworkSession 类来管理会话。通过 HttpStreamFactory 对象来建立 TCP Socket 连接。之后 HttpStreamFactory 创建 HttpStream 对象,来处理对象和网络之间数据的读写。
  4. Chromium 中与服务器建立连接的套接字是 SteamSocket 类,它是一个抽象类,再 POSIX 系统和 Windows 系统上有不同实现。
域名解析(DNS)

Chromium 使用 HostResolverImpl 类来解析域名,具体调用的是 “getaddrinfo()”,是一个阻塞式函数,所以使用单独的线程处理。为了考虑效率,使用 HostCache 类来保存解析后的域名,还有 DNS 预解析机制。

磁盘本地缓存

要求:

  • 要有相应的机制来移除合适的缓存资源
  • 浏览器崩溃时不破坏磁盘文件
  • 可以通过同步或异步的方式高效快速的访问
  • 避免同时存储相同的资源
  • 操作一个项的时候不受其他请求影响
  • 磁盘不支持多线程访问,磁盘的缓存操作要放到单独的线程
  • 支持老版结构

实现上主要有两个类,Backend(整个磁盘缓存) 和 Entry(表中的表项)。至少需要一个索引文件和四个数据文件。索引文件用来索引,数据文件又称块文件。

索引文件
包括一个索引头部和索引地址;头部用来表示该索引文件的信息(索引文件版本号、索引项数量、文件大小等信息);索引地址表保存各个表项对应的索引地址,直接将文件映射到内存地址。从内存地址可以找到数据文件,数据文件也是一个文件头加上后面的块文件,每个块的大小是固定的,当超过 512 字节的时候会为其分配多个块。但最多不超过四个,超过通常会用单独的文件存储。如果一个表项要分配四个块,那么是和块索引位置是对齐的(起始块的位置是4的倍数)

表项结构
分为两个部分,第一部分标记自己,包括元数据信息和自身内容。另一部分经常发生变动,主要为表项的回收算法服务,保存了回收算法所需的信息(LRU回收算法)。

高性能网络栈

DNS 预取和 TCP 预连接

一次 DNS 查询约 60~120ms,而 TCP 的三次握手也大约几十毫秒

DNS 预取:利用现有的 DNS 机制,提前解析网页中可能的连接。不是使用前面提到的 Chromium 网络栈,而是直接利用系统的域名解析机制,不会阻碍当前网络栈的工作,针对多个域名采取并行处理的方式,每个域名的解析由一个新线程处理,结束后退出。网页开发者可以显示指定哪些域名来让 Chromium 解析,使用方法:

  • 路漫漫其修远兮 吾将上下而求索 周凡杨 学习 思索
    王国维在他的《人间词话》中曾经概括了为学的三种境界古今之成大事业、大学问者,罔不经过三种之境界。“昨夜西风凋碧树。独上高楼,望尽天涯路。”此第一境界也。“衣带渐宽终不悔,为伊消得人憔悴。”此第二境界也。“众里寻他千百度,蓦然回首,那人却在灯火阑珊处。”此第三境界也。学习技术,这也是你必须经历的三种境界。第一层境界是说,学习的路是漫漫的,你必须做好充分的思想准备,如果半途而废还不如不要开始。这里,注
  • Hadoop(二)对话单的操作 朱辉辉33 hadoop
    Debug: 1、 A = LOAD '/user/hue/task.txt' USING PigStorage(' ') AS (col1,col2,col3); DUMP A; //输出结果前几行示例: (>ggsnPDPRecord(21),,) (-->recordType(0),,) (-->networkInitiation(1),,)
  • web报表工具FineReport常用函数的用法总结(日期和时间函数) 老A不折腾 finereport报表工具web开发
    web报表工具FineReport常用函数的用法总结(日期和时间函数)   说明:凡函数中以日期作为参数因子的,其中日期的形式都必须是yy/mm/dd。而且必须用英文环境下双引号(" ")引用。   DATE DATE(year,month,day):返回一个表示某一特定日期的系列数。 Year:代表年,可为一到四位数。 Month:代表月份。
  • c++ 宏定义中的##操作符 墙头上一根草 C++
    #与##在宏定义中的--宏展开 #include <stdio.h> #define f(a,b) a##b #define g(a)   #a #define h(a) g(a) int main() {       &nbs
  • 分析Spring源代码之,DI的实现 aijuans springDI源代码
    (转) 分析Spring源代码之,DI的实现 2012/1/3 by tony                 接着上次的讲,以下这个sample [java]  view plain copy print
  • for循环的进化 alxw4616 JavaScript
    // for循环的进化 // 菜鸟 for (var i = 0; i < Things.length ; i++) { // Things[i] } // 老鸟 for (var i = 0, len = Things.length; i < len; i++) { // Things[i] } // 大师 for (var i = Things.le
  • 网络编程Socket和ServerSocket简单的使用 百合不是茶 网络编程基础IP地址端口
      网络编程;TCP/IP协议   网络:实现计算机之间的信息共享,数据资源的交换   协议:数据交换需要遵守的一种协议,按照约定的数据格式等写出去   端口:用于计算机之间的通信      每运行一个程序,系统会分配一个编号给该程序,作为和外界交换数据的唯一标识 0~65535   查看被使用的
  • JDK1.5 生产消费者 bijian1013 javathread生产消费者java多线程
    ArrayBlockingQueue:        一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列检索操作则是从队列头部开始获得元素。 ArrayBlockingQueue的常用方法:
  • JAVA版身份证获取性别、出生日期及年龄 bijian1013 java性别出生日期年龄
            工作中需要根据身份证获取性别、出生日期及年龄,且要还要支持15位长度的身份证号码,网上搜索了一下,经过测试好像多少存在点问题,干脆自已写一个。 CertificateNo.java package com.bijian.study; import java.util.Calendar; import
  • 【Java范型六】范型与枚举 bit1129 java
    首先,枚举类型的定义不能带有类型参数,所以,不能把枚举类型定义为范型枚举类,例如下面的枚举类定义是有编译错的   public enum EnumGenerics<T> { //编译错,提示枚举不能带有范型参数 OK, ERROR; public <T> T get(T type) { return null;
  • 【Nginx五】Nginx常用日志格式含义 bit1129 nginx
    1. log_format 1.1 log_format指令用于指定日志的格式,格式:   log_format name(格式名称) type(格式样式)   1.2 如下是一个常用的Nginx日志格式:   log_format main '[$time_local]|$request_time|$status|$body_bytes
  • Lua 语言 15 分钟快速入门 ronin47 lua 基础
    - - 单行注释 - - [[      [多行注释] - - ]]   - - - - - - - - - - - 1. 变量 & 控制流 - - - - - - - - - - num = 23 - - 数字都是双精度 str = 'aspythonstring'
  • java-35.求一个矩阵中最大的二维矩阵 ( 元素和最大 ) bylijinnan java
    the idea is from: http://blog.csdn.net/zhanxinhang/article/details/6731134 public class MaxSubMatrix { /**see http://blog.csdn.net/zhanxinhang/article/details/6731134 * Q35 求一个矩阵中最大的二维
  • mongoDB文档型数据库特点 开窍的石头 mongoDB文档型数据库特点
    MongoDD: 文档型数据库存储的是Bson文档-->json的二进制 特点:内部是执行引擎是js解释器,把文档转成Bson结构,在查询时转换成js对象。 mongoDB传统型数据库对比    传统类型数据库:结构化数据,定好了表结构后每一个内容符合表结构的。也就是说每一行每一列的数据都是一样的    文档型数据库:不用定好数据结构,
  • [毕业季节]欢迎广大毕业生加入JAVA程序员的行列 comsci java
        一年一度的毕业季来临了。。。。。。。。      正在投简历的学弟学妹们。。。如果觉得学校推荐的单位和公司不适合自己的兴趣和专业,可以考虑来我们软件行业,做一名职业程序员。。。      软件行业的开发工具中,对初学者最友好的就是JAVA语言了,网络上不仅仅有大量的
  • PHP操作Excel – PHPExcel 基本用法详解 cuiyadll PHPExcel
    导出excel属性设置//Include classrequire_once('Classes/PHPExcel.php');require_once('Classes/PHPExcel/Writer/Excel2007.php');$objPHPExcel = new PHPExcel();//Set properties 设置文件属性$objPHPExcel->getProperties
  • IBM Webshpere MQ Client User Issue (MCAUSER) darrenzhu IBMjmsuserMQMCAUSER
    IBM MQ JMS Client去连接远端MQ Server的时候,需要提供User和Password吗? 答案是根据情况而定,取决于所定义的Channel里面的属性Message channel agent user identifier (MCAUSER)的设置。 http://stackoverflow.com/questions/20209429/how-mca-user-i
  • 网线的接法 dcj3sjt126com
    一、PC连HUB (直连线)A端:(标准568B):白橙,橙,白绿,蓝,白蓝,绿,白棕,棕。 B端:(标准568B):白橙,橙,白绿,蓝,白蓝,绿,白棕,棕。 二、PC连PC (交叉线)A端:(568A): 白绿,绿,白橙,蓝,白蓝,橙,白棕,棕; B端:(标准568B):白橙,橙,白绿,蓝,白蓝,绿,白棕,棕。 三、HUB连HUB&nb
  • Vimium插件让键盘党像操作Vim一样操作Chrome dcj3sjt126com chromevim
    什么是键盘党? 键盘党是指尽可能将所有电脑操作用键盘来完成,而不去动鼠标的人。鼠标应该说是新手们的最爱,很直观,指哪点哪,很听话!不过常常使用电脑的人,如果一直使用鼠标的话,手会发酸,因为操作鼠标的时候,手臂不是在一个自然的状态,臂肌会处于绷紧状态。而使用键盘则双手是放松状态,只有手指在动。而且尽量少的从鼠标移动到键盘来回操作,也省不少事。 在chrome里安装 vimium 插件
  • MongoDB查询(2)——数组查询[六] eksliang mongodbMongoDB查询数组
    MongoDB查询数组 转载请出自出处:http://eksliang.iteye.com/blog/2177292 一、概述  MongoDB查询数组与查询标量值是一样的,例如,有一个水果列表,如下所示: > db.food.find() { "_id" : "001", "fruits" : [ "苹
  • cordova读写文件(1) gundumw100 JavaScriptCordova
    使用cordova可以很方便的在手机sdcard中读写文件。 首先需要安装cordova插件:file 命令为: cordova plugin add org.apache.cordova.file 然后就可以读写文件了,这里我先是写入一个文件,具体的JS代码为: var datas=null;//datas need write var directory=&
  • HTML5 FormData 进行文件jquery ajax 上传 到又拍云 ileson jqueryAjaxhtml5FormData
    html5 新东西:FormData  可以提交二进制数据。 页面test.html <!DOCTYPE> <html> <head> <title> formdata file jquery ajax upload</title> </head> <body> <
  • swift appearanceWhenContainedIn:(version1.2 xcode6.4) 啸笑天 version
      swift1.2中没有oc中对应的方法: + (instancetype)appearanceWhenContainedIn:(Class <UIAppearanceContainer>)ContainerClass, ... NS_REQUIRES_NIL_TERMINATION;  解决方法: 在swift项目中新建oc类如下: #import &
  • java实现SMTP邮件服务器 macroli java编程
    电子邮件传递可以由多种协议来实现。目前,在Internet 网上最流行的三种电子邮件协议是SMTP、POP3 和 IMAP,下面分别简单介绍。   ◆ SMTP 协议   简单邮件传输协议(Simple Mail Transfer Protocol,SMTP)是一个运行在TCP/IP之上的协议,用它发送和接收电子邮件。SMTP 服务器在默认端口25上监听。SMTP客户使用一组简单的、基于文本的
  • mongodb group by having where 查询sql qiaolevip 每天进步一点点学习永无止境mongo纵观千象
    SELECT cust_id, SUM(price) as total FROM orders WHERE status = 'A' GROUP BY cust_id HAVING total > 250 db.orders.aggregate( [ { $match: { status: 'A' } }, { $group: {
  • Struts2 Pojo(六) Luob. POJOstrust2
    注意:附件中有完整案例 1.采用POJO对象的方法进行赋值和传值 2.web配置 <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee&q
  • struts2步骤 wuai struts
    1、添加jar包 2、在web.xml中配置过滤器 <filter>        <filter-name>struts2</filter-name>        <filter-class>org.apache.st