演变
HTTP 2
由来
这些年来,<b>网页变得越来越复杂,更多的数据通过 HTTP 请求被传输</b>。<br>HTTP/1.1链接需要请求以正确的顺序发送,理论上可以用一些并行的链接(一般是5到8个),导致成本和复杂性显著加大。<br>比如,HTTP流水线就成为了Web开发的负担。<br><br>在2010年到2015年,谷歌通过实现一个实验性的SPDY协议,展示了另一种在客户端和服务器端交换数据的方式。<br>其收集了浏览器和服务器端的开发者的焦点问题。明确了 增加响应数量和解决传输数据复用 的目标,SPDY成为了HTTP/2协议的基础。<br>
与 HTTP/1.1 的不同
1. HTTP/2是<b><font color="#0076b3">二进制协议</font></b>而<b>不是文本协议</b>。<font color="#924517"><b>不再可读</b>,<b>也不可无障碍的手动创建</b>。</font><br>尽管有此障碍,改善的优化技术现在可被实施。<br>2. 这是一个复用协议。<b><font color="#0076b3">并行的请求能在同一个链接中处理</font></b>,移除了HTTP/1.x中顺序和阻塞的约束。<br>3.<font color="#0076b3"> <b>压缩 headers。因为 headers 在一系列请求中常常是相似的</b></font>,这移除了传输数据的重复性,降低了传输数据的成本。<br>4. <b>允许服务器 通过 <font color="#0076b3">server push</font> 的机制 在被请求前 <font color="#0076b3">向客户端缓存传输数据</font></b>。<br>
高流量的站点最迅速地接受了 HTTP2,在数据传输上节省了可观的成本和支出。<br><br>这种迅速的普及率很可能是因为 HTTP2 不需要站点和应用做出改变:使用HTTP/1.1和HTTP/2对他们来说是透明的。<br>拥有一个最新的服务器和新点的浏览器进行交互就足够了。<br>只有一小部分群体需要做出改变,而且随着陈旧的浏览器和服务器的更新,不需Web开发者做什么,用的人自然就增加了。<br>
继续优化
建立在 TCP(传输控制协议/Transmission Control Protocol 是一种面向连接的、可靠的、基于字节流的<b>传输层</b>通信协议)<br> 之上的 HTTP2.0 仍然受到 TCP 一些特性的制约。<br>谷歌开发的 QUIC <b>基于 UDP 协议</b>,2018年10月被确立为 <b>HTTP3.0 </b>的基础
HTTP 消息
HTTP/1.x 报文<br>请求和响应结构<br>
<ol><li>一行起始行用于描述要执行的请求,或者是对应的状态,成功或失败。这个起始行总是单行的。<br></li><li>一个可选的HTTP头集合指明请求或描述消息正文。<br></li><li>一个空行指示所有关于请求的元数据已经发送完毕。<br></li><li>一个可选的包含请求相关数据的正文 (比如HTML表单内容), 或者响应相关的文档。 正文的大小由HTTP头来指定。<br><br></li></ol>起始行和 HTTP 消息中的HTTP 头统称为请求头,而其有效负载被称为消息正文。<br>
<br>
HTTP/1.x 报文<br>在性能上的缺点
<ol><li>Header 不像 body,它不会被压缩。<br></li><li>两个报文之间的 header 通常非常相似,但它们仍然在连接中重复传输。<br></li><li>无法复用。当在同一个服务器打开几个连接时TCP 热连接比冷连接更加有效。<br></li></ol>
HTTP/2 引入了一个<b>额外的步骤</b>:它将 HTTP/1.x 消息分成帧并嵌入到流 (stream) 中。<br><b>数据帧和报头帧分离</b>,这允许<b>报头压缩</b>。<br><b>多个流可以组合</b>,这是一个被称为<b>多路复用</b> (multiplexing) 的过程,它允许更有效的底层 TCP 连接。<br>
HTTP 缓存
缓存是一种<b>保存 资源副本</b> 并在下次请求时直接 <b>使用该副本</b> 的技术。
缓存的类型
(私有)浏览器缓存
私有缓存只能用于单独用户。<br><br>你可能已经见过浏览器设置中的“缓存”选项。浏览器缓存拥有<b>用户通过 HTTP 下载的所有文档</b>。<br>这些缓存为浏览过的文档提供 <b>向后/向前导航</b>,<b>保存网页</b>,<b>查看源码</b>等功能,可以避免再次向服务器发起多余的请求。它同样可以提供 <b>缓存内容的离线浏览</b>。<br>
(共享)代理缓存
共享缓存可以被多个用户使用。<br><br>例如,ISP 或你所在的公司可能会架设一个 web 代理来作为本地网络基础的一部分提供给用户。<br>这样 <b>热门的资源就会被重复使用</b>,减少网络拥堵与延迟。<br>
缓存操作的目标
常见的 HTTP 缓存只能<b>存储 GET 响应</b>。<br>primary cache key 包括 request method 和目标URI(oftentimes only the URI is used as only GET requests are caching targets)。<br> <br>常见的缓存案例:<br><ul><li>一个检索请求的成功响应: 一个包含例如HTML文档,图片,或者文件的 200(OK) 响应。<br></li><li>永久重定向: 一个 301 响应。<br></li><li>错误响应: 一个 404 结果页面。<br></li><li>不完全的响应: 一个 206(Partial Content) 响应。<br></li><li>Responses other than GET if something suitable for use as a cache key is defined.<br></li></ul>
新鲜度<br>(强缓存)
理论上来讲,当一个资源被缓存存储后,该资源应该可以被永久存储在缓存中。<br><br>由于缓存只有有限的空间用于存储资源副本,所以缓存会定期地将一些副本删除,这个过程叫做 <b><font color="#0076b3">缓存驱逐</font></b>。<br><br>另一方面,当服务器上面的资源进行了更新,那么缓存中的对应资源也应该被更新,<br>由于HTTP是C/S模式的协议,服务器更新一个资源时,不可能直接通知客户端更新缓存,<br>所以双方必须为该资源约定一个过期时间,在该 <b><font color="#0076b3">过期时间之前</font></b>,该资源(缓存副本)就是 <b>新鲜的</b>,<br>当 <font color="#0076b3" style="font-weight: bold;">过了过期时间后</font><b>,</b>该资源(缓存副本)则变为 <b>陈旧的。</b>驱逐算法中新鲜的资源被优先保留。<br>
freshness lifetime 的计算<br>是基于几个 headers 的<br>
Cache-control: max-age=N
If a "Cache-control: max-age=N" header is specified, <br>then <b>the freshness lifetime is equal to N</b>.<br>
如果没有 Cache-control:max-age=N header,<br>检查 <b>Expires header </b>是否存在
If an Expires header exists, <br>then <b><font color="#0076b3">its value minus the value of the Date header</font></b> <br>determines the freshness lifetime. <br>
以上两个 headers 都没有,<br>检查 <b>Last-Modified</b> header
If this header is present, <br>then the cache's freshness lifetime is equal to <br><b>the value of the <font color="#0076b3">Date header minus </font><font color="#5c5c5c">the value of the</font><font color="#0076b3"> Last-modified header</font> divided by 10</b>.<br>
<font color="#0076b3">expiration time</font> 的计算
expirationTime = responseTime + freshnessLifetime - currentAge
缓存对于<br>对应新鲜与不新鲜资源<br>请求的处理<br>
当缓存接收到一个<b>对应新鲜资源的请求,</b>缓存代理不会向服务器发送请求,会直接从缓存中读取资源。<br>在 chrome 控制台的 Network选项中可以看到该请求返回 Status Code: 200 (from disk cache)。<br>
当缓存接收到一个<b>对应陈旧资源的请求</b>,缓存会 <b>先将此请求<font color="#0076b3">附加一个 If-None-Match</font> 头</b>,<b>然后发给目标服务器</b>,<br>以此来检查该资源副本是否是依然还是算新鲜的。这里即走了下面 缓存验证 的流程。<br>若服务器返回了 304 (Not Modified)(该响应不会有带有实体信息),表示此资源副本是新鲜的,这样一来,可以节省一些带宽。<br>若服务器通过 If-None-Match 或 If-Modified-Since判断后发现已过期,那么会带有该资源的实体内容返回。<br>
缓存验证<br>(协商缓存)
当缓存的文档过期后,需要进行缓存验证或者重新获取资源。<br>只有在服务器返回 强校验器 或者 弱校验器 时才会进行验证。<br>
什么时候触发验证
<ol><li>用户点击<b><font color="#0076b3"> F5</font></b> 或者 <b><font color="#0076b3">刷新按钮</font></b> 时会开始缓存验证。<br></li><li>如果缓存的响应里含有"Cache-control: must-revalidate” header,在浏览的过程中也会触发缓存验证。<br></li><li>在浏览器偏好设置里设置Advanced->Cache为强制验证缓存也能达到相同的效果。<br></li></ol>
强弱校验器
强校验器
ETag
ETag <b>响应头 </b>是一个对用户代理(User Agent, 下面简称UA)不透明的值。对于像浏览器这样的HTTP UA,不知道ETag代表什么,不能预测它的值是多少。如果资源请求的响应头里含有 ETag, <b>客户端</b>可以在后续的请求的头中<b><font color="#0076b3">带上 If-None-Match</font></b> 头来验证缓存。
弱校验器
Last-Modified
Last-Modified <b>响应头 </b>可以作为一种弱校验器。<br>说它<b>弱</b>是因为它<b><font color="#0076b3">只能精确到一秒</font></b>。<br>如果响应头里含有这个信息,<b>客户端</b>可以在后续的请求中<b><font color="#0076b3">带上 If-Modified-Since</font></b> 来验证缓存。<br>
When a validation request is made, the server can<b> either ignore</b> the validation request and<b> response with a normal 200 OK</b>, <br>or it can<b> return 304 Not Modified </b>(with an empty body) to<b> instruct the browser to use its cached copy</b>. <br>The latter response can also include headers that update the expiration time of the cached document.<br>
带 Vary 头的响应
Vary HTTP 响应头决定了对于后续的请求头,如何判断是请求一个新的资源还是使用缓存的文件。<br><br>当缓存服务器收到一个请求,其对应了一个有 vary header 的缓存响应时,<br>只有当 <b>Vary</b> header 中提到的<b>所有 header 字段</b>在<b>新请求与缓存请求中都匹配</b>时才能使用缓存的响应。<br>
使用vary头有利于内容服务的动态多样性。<br><br>例如,使用<b><font color="#0076b3"> Vary: User-Agent</font></b> 头,缓存服务器需要通过UA判断是否使用缓存的页面。<br><b><font color="#0076b3">如果需要区分移动端和桌面端的展示内容,利用这种方式就能避免在不同的终端展示错误的布局。<br></font></b>另外,它可以帮助 Google 或者其他搜索引擎更好地发现页面的移动版本,并且告诉搜索引擎没有引入Cloaking(Cloaking is a search engine optimization (SEO) technique in which the content presented to the search engine spider is different from that presented to the user's browser. )。<br>
浏览器缓存刷新
1. <b><font color="#16884a">在地址栏中输入网址后按回车或点击转到按钮</font></b><br><br>浏览器<b>以最少的请求来获取网页的数据</b>,浏览器会<b>对所有没有过期的内容 直接使用本地缓存</b>,从而减少对浏览器的请求。<br><b>Cache-control:max-age=N header,Expires header <font color="#924517">强缓存依然有效</font></b>。<br>
2. <font color="#0076b3"> </font><b><font color="#16884a">按F5或浏览器刷新按钮</font></b><br><br>浏览器会在请求中<b><font color="#924517">附加必要的缓存协商</font></b>,但<b><font color="#924517">不允许浏览器直接使用本地缓存</font><font color="#0076b3">。</font></b><br>ETag、Last-Modified 依然有效,但是 Cache-control:max-age=N ,Expires 失效。<br>
3. <b><font color="#0076b3"> </font><font color="#16884a">按 Ctrl+F5 或按Ctrl并点击刷新按钮</font><br></b><br>这种方式就是<b><font color="#924517">强制刷新</font></b>,总会发起一个全新的请求,<b><font color="#924517">不使用任何缓存</font></b>。<br>