网络栈和资源加载

211 阅读7分钟

网络栈和资源加载

此内容为笔者学习和自我总结,有错误的地方请谅解和指出,笔者定会及时更正。学习的路上,一起互助前行。

这里,我们讲一下chrome中有关资源的加载。从用户输入url到chrome浏览器加载资源的过程,以及怎么资源进行高效的管理。

  1. 先介绍下资源加载的基础设施
    1)HTML资源支持的类型。包括:
    a. HTML:HTML页面,包括各种各样的HTML元素。
    b. JS:js代码。可以是内嵌的,也可以是单独的文件。
    c. CSS样式表:css样式资源。可以是内嵌的,也可以是单独的文件。
    d. 图片。
    e. 字体文件。
    f. 等...

    资源加载器:分类以及大概作用。
    1) 针对每种资源类型的特定加载器。其特点是仅加载某一种资源。ImageLoader类仅加载图片资源。
    2)资源缓存机制的资源加载器。特点是所有加载器都需要通过它来检查和插入缓存资源。
    3)通用资源加载器--ResourceLoader类。当webkit需要从网络或者文件系统获取资源时负责获取资源的数据。
    webkit以这样的方式设计加载器,目的是想将加载的复杂机制逐渐简化为若干简单步骤。

    这些资源在webkit中都有不同的类表示,这些类的公共基类是CachedResource。比如:CachedScript存储js资源,CachedCssStyleSheet类存储样式表,等。
    那为什么基类是'Cached'字样呢?这里是因为资源加载的效率问题引入了缓存机制,它对资源的请求都会先获取缓存中的信息,以决定是否向服务器请求资源。

    那什么是资源的缓存呢?

    2)资源缓存
    这里主要说的缓存位置:内存缓存(memory cache),磁盘缓存(disk cache);当然还有service worker 缓存,以及http2协议涉及的Push Cache。
    a. 内存资源的缓存机制的基本思路:
    建立一个资源的缓存池,当webkit请求资源时,先从资源池中查看有没有响应资源。有则取出使用。如果没有,webkit会先创建一个新的CachedResource子类的对象,等待webkit收到资源后,以url为资源的唯一标识符,将其资源设置到新的CachedResource子类的对象中,待下次使用。
    内存缓存固然快,不过内存存在时间较短,当相应进程释放,内存缓存也会被清除。

     b. 磁盘本地缓存   
         为了进一步提升资源的加载效率(当进程释放后,再进入时,资源其实没有改变的),提出磁盘缓存机制。把资源存储在本地磁盘中。   
    
         1)磁盘存储的结构   
             数据以表的形式存储在磁盘上。分索引文件,和数据文件(块文件)。
             1.1) 索引文件: 
                 用来方便快速检索存放在数据文件中的索引项。包括索引头,和索引地址。索引文件直接将文件映射到内存地址(个人感觉应该是逻辑地址),方便快速找到表项的索引地址。  
                 头部包括:索引版本号,索引项数目,文件大小等。  
                 索引地址:保存各个表项对应的索引地址。  
                 
             1.2)数据文件(块文件)  
                 由很多特定大小的块组成,数据块的内容是表项。表项结构可以大致分为两部分:  
                 a. 用于数据标记的内容。包括元数据信息,自身内容,通常比较少变动。   
                 b. 包括回收算法服务所需信息,具体存储数据信息(也很可能是指向具体信息存放的地址)。更多详情,可参照如下: 
    

    Disk Cache

    Disk Cache:www.chromium.org/developers/…

         想比于内存缓存,磁盘能存的多,时间也可以更长,但磁盘的读取效率低。  
         
    

    不过chrome中,具体怎么权衡数据什么时候该缓存在内存中,什么时候该缓存在磁盘中,或者说什么资源应该缓存在内存或磁盘中?
    笔者比较认可的说法是:(根据内存和磁盘功能来分)
    1. 大文件,通常优先存磁盘。 2. 进程未释放,内存空间足够,该进程加载的资源需要缓存的话,优先内存缓存。 3. 内存紧张时,优先磁盘缓存。进程要释放时,内存中的资源可以有选择的存入磁盘。

    当代浏览器一般的查找缓存顺序:(查询还要带上浏览器缓存策略的条件)

    1. service worker 缓存
    2. memory cache
    3. dist cache
    4. push cache (http2) 都没命中,再会请求网络。(比如协商缓存)
  2. 资源加载的过程。
    当在浏览器输入一个url时:当前tab页的Render进程会将该资源获取的任务通过进程间通信,交给Browser进程来实现,这里是为了安全性和效率。Browser进程有权限从网络或者本地获取资源。
    1)判断是否有缓存:
    以此查看service worker缓存,内存缓存,磁盘缓存,(如果时http2,再查看push cache)是否有命中。如果完全命中,从缓存中获取信息。

    一个资源该不该缓存,该怎么缓存?这个主要由服务器返回信息决定,浏览器根据服务端响 应的请求信息判断是否缓存和怎么缓存。

    那缓存涉及的http信息字段有哪些呢?http缓存过程有哪些呢?
    分强缓存和协商缓存。 这里还有注意一个前提:http协议中有规范规定,浏览器可以发送消息确认是否需要更新请求信息。 1. 强缓存: a. 在http/1.0 中,根据响应头的Expires来判断,它表明的是资源失效时间点。
    b. 在http/1.1 中引入 Cache-Control,它可以在请求头或者响应头中设置,并且可以组合使用多种指令。
    响应通常设置max-age=来设置缓存存储的最大周期,单位是秒。
    2. 协商缓存: 若强缓存信息判断失效,则进行协商缓存
    a. ETag 标识符:请求(资源)响应的响应头特定版本的标识符; 每次资源更改(毫秒级),会生成新的ETag值。对比max-age,更准确和灵活。 客户端请求时,可以配合If-Match头来检查是否为最新版,或者配合If-None-Match检查 客户端显示已过期且不可用的资源 在服务端是否更改。
    b. 服务端-Last-Modified
    客户端 If-Modified-Since 或者If-None-Match 配合使用;

    2)如果没有命中任何缓存(协商缓存没有协商成功,还需要从服务端获取)。则
    2.1)DNS域名解析:
    在chromium 中,使用HostResolverImpl类的g"etaddrinfo()"方法来解析域名,这是一个阻塞式的函数,所以chromium会使用单独的线程来做域名解析。
    chromium对于域名解析的优化还有预解析方式。大概思想是:利用现有的DNS机制,提前解析网页中可能的网络连接。这里要注意可能,也就是说是一种猜测。比如用户在输入url的过程中,不断的根据已输入的字符,去猜测可能的url,提前建立连接。或者通过大数据来预测行为,推测可能性,提前建立连接。
    或者在解析HTML文档时,设置的dns-prefetch标示,当解析被js的同步请求阻碍时,可以单独建立线程,扫描文档中是否有有类似标识,记录标示信息,提前做DNS解析和下载资源。

     2.2)建立网络连接  
         应用层  http协议    
         会话层  SPDY协议  
         表示层  SSL协议 
         传输层  TCP/UDP协议 
         网络层  ip
    
         从上往下,先说http协议,http/1.0时,每一次http请求,需要等待TCP连接。   
         到http/1.1,通过管道(管线化)技术,只有在GET和HED等请求可行。优化http协议,通过服务端配合,开启永久连接,可以使用多个请求共用一次TCP连接(也就是把多个http数据填充在一个TCP数据包中),减少网络负载。但是请求还是有明显的先后顺序,(没有识别每个数据包的情况)所以需要按顺序返回。  
    
         为了解决上述问题,在会话层SPDY协议,真正意义上实现了多路复用。也是http/2.0的基础。
         SPDY协议的核心,多路复用-也就是一个TCP连接,实现多个资源传输。  
         此外,也有二进制传输,头部压缩,服务端主动推送,区分资源请求的优先级等。   
         
    

---待续,未完---