-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
lijingze
committed
Feb 3, 2024
1 parent
4a044be
commit 64f4186
Showing
5 changed files
with
195 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
### HTTP 缓存有哪些实现方式? | ||
|
||
对于一些具有重复性的 HTTP 请求,比如每次请求得到的数据都一样的,我们可以把这对「请求-响应」的数据都**缓存在本地**,那么下次就直接读取本地的数据,不必在通过网络获取服务器的响应了,这样的话 HTTP/1.1 的性能肯定肉眼可见的提升。 | ||
|
||
所以,避免发送 HTTP 请求的方法就是通过**缓存技术**,HTTP 设计者早在之前就考虑到了这点,因此 HTTP 协议的**头部**有不少是针对缓存的字段。 | ||
|
||
HTTP 缓存有两种实现方式,分别是**强制缓存和协商缓存**。 | ||
|
||
### 什么是强制缓存? | ||
|
||
强缓存指的是只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在于浏览器这边。 | ||
|
||
如下图中,返回的是 200 状态码,但在 size 项中标识的是 from disk cache,就是使用了强制缓存。 | ||
|
||
![img](https://cdn.xiaolincoding.com//mysql/other/1cb6bc37597e4af8adfef412bfc57a42.png) | ||
|
||
强缓存是利用下面这两个 HTTP 响应头部(Response Header)字段实现的,它们都用来表示资源在客户端缓存的有效期: | ||
|
||
- `Cache-Control`, 是一个相对时间; | ||
- `Expires`,是一个绝对时间; | ||
|
||
如果 HTTP 响应头部同时有 Cache-Control 和 Expires 字段的话,**Cache-Control 的优先级高于 Expires** 。 | ||
|
||
### 什么是协商缓存? | ||
|
||
当我们在浏览器使用开发者工具的时候,你可能会看到过某些请求的响应码是 `304`,这个是告诉浏览器可以使用本地缓存的资源,通常这种通过服务端告知客户端是否可以使用缓存的方式被称为协商缓存。 | ||
|
||
协商缓存可以基于两种头部来实现。 | ||
|
||
第一种:请求头部中的 `If-Modified-Since` 字段与响应头部中的 `Last-Modified` 字段实现,这两个字段的意思是: | ||
|
||
- 响应头部中的 `Last-Modified`:标示这个响应资源的最后修改时间; | ||
- 请求头部中的 `If-Modified-Since`:当资源过期了,发现响应头中具有 Last-Modified 声明,则再次发起请求的时候带上 Last-Modified 的时间,服务器收到请求后发现有 If-Modified-Since 则与被请求资源的最后修改时间进行对比(Last-Modified),如果最后修改时间较新(大),说明资源又被改过,则返回最新资源,HTTP 200 OK;如果最后修改时间较旧(小),说明资源无新修改,响应 HTTP 304 走缓存。 | ||
|
||
第二种:请求头部中的 `If-None-Match` 字段与响应头部中的 `ETag` 字段,这两个字段的意思是: | ||
|
||
- 响应头部中 `Etag`:唯一标识响应资源; | ||
- 请求头部中的 `If-None-Match`:当资源过期时,浏览器发现响应头里有 Etag,则再次向服务器发起请求时,会将请求头 If-None-Match 值设置为 Etag 的值。服务器收到请求后进行比对,如果资源没有变化返回 304,如果资源变化了返回 200。 | ||
|
||
第一种实现方式是基于时间实现的,第二种实现方式是基于一个唯一标识实现的,相对来说后者可以更加准确地判断文件内容是否被修改,避免由于时间篡改导致的不可靠问题。 | ||
|
||
如果在第一次请求资源的时候,服务端返回的 HTTP 响应头部同时有 Etag 和 Last-Modified 字段,那么客户端再下一次请求的时候,如果带上了 ETag 和 Last-Modified 字段信息给服务端,**这时 Etag 的优先级更高**,也就是服务端先会判断 Etag 是否变化了,如果 Etag 有变化就不用在判断 Last-Modified 了,如果 Etag 没有变化,然后再看 Last-Modified。 | ||
|
||
**为什么 ETag 的优先级更高?**这是因为 ETag 主要能解决 Last-Modified 几个比较难以解决的问题: | ||
|
||
1. 在没有修改文件内容情况下文件的最后修改时间可能也会改变,这会导致客户端认为这文件被改动了,从而重新请求; | ||
2. 可能有些文件是在秒级以内修改的,`If-Modified-Since` 能检查到的粒度是秒级的,使用 Etag就能够保证这种需求下客户端在 1 秒内能刷新多次; | ||
3. 有些服务器不能精确获取文件的最后修改时间。 | ||
|
||
注意,**协商缓存这两个字段都需要配合强制缓存中 Cache-Control 字段来使用,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求**。 | ||
|
||
下图是强制缓存和协商缓存的工作流程: | ||
|
||
![img](https://cdn.xiaolincoding.com/gh/xiaolincoder/network/http/http%E7%BC%93%E5%AD%98.png) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# http1 | ||
|
||
- 短连接,每发送一次请求就要从新建立一次tcp连接 | ||
- 不支持管道网络传输,请求发出后,需要等待响应,才能再次发送 | ||
|
||
|
||
|
||
# http1.1 | ||
|
||
### 优点 | ||
|
||
- 长连接,多次请求可以复用一个tcp长连接(由响应头里面的Connection字段控制,默认值为keep-alive,默认开启,关闭需要设置该值为close) | ||
- 支持管道(pipeline)网络传输,第一个请求发出后,不要等待响应,就可以继续发送请求 | ||
|
||
### 缺点 | ||
|
||
- 虽然没有请求端的队头阻塞,但是有响应端的,响应必须按顺序返回,如果前面的请求服务器响应慢,依旧会阻塞后面的请求。 | ||
- 冗余的头部信息,头部有很多重复的信息,且无法压缩(虽然body部分内容可以使用gzip等算法压缩) | ||
- 没有请求优先级控制 | ||
- 请求只能由客户端发起 | ||
|
||
|
||
|
||
## http2 | ||
|
||
### 优点: | ||
|
||
- 头部压缩:**HPACK** 算法,使用静态字典维护高频使用的常用的头部信息(例如响应状态码),使用动态字典动态的添加其他用户相关的信息(例如user-agent),最后使用Huffman编码压缩算法,进一步压缩头部信息。HPACK 算法主要包含三个组成部分: | ||
- 静态字典; | ||
- 动态字典; | ||
- Huffman 编码(压缩算法); | ||
- 使用二进制,增加了数据传输效率(比如状态码 200 ,在 HTTP/1.1 是用 '2''0''0' 三个字符来表示(二进制:00110010 00110000 00110000),共用了 3 个字节,在 HTTP/2 对于状态码 200 的二进制编码是 10001000,只用了 1 字节就能表示,相比于 HTTP/1.1 节省了 2 个字节) | ||
- *并发传输*:增加流标识符,相比于http1.1建立多个tcp连接实现并发请求,http2在一条tcp连接上就可以实现并发请求,并且流标识符还可以标记优先级,让客户端优先处理某些响应。 | ||
- 服务器主动推送资源:服务端不再是被动地响应,可以**主动**向客户端发送消息。客户端和服务器**双方都可以建立 Stream**, Stream ID 也是有区别的,客户端建立的 Stream 必须是奇数号,而服务器建立的 Stream 必须是偶数号 | ||
|
||
### 缺点 | ||
|
||
虽然在http层全面解决了利用流的并发,解决了http1.1响应队头阻塞的问题,但是在tcp层,由于tcp是面向字节流的协议,一个消息可能会被拆分成多个报文,当某一个字节的数据发生丢失时,由于传输层无法将消息组合完整,会把数据暂存在缓冲区,等待丢失的数据接收到之后才会组装成完整的消息,交给应用层,也就是http。**一旦发生丢包,就会阻塞住所有的 HTTP 请求**,这属于 TCP 层队头阻塞 | ||
|
||
所以,http2仍然存在队头阻塞问题,不过问题出在它依赖的传输层协议tcp | ||
|
||
|
||
|
||
# http3 | ||
|
||
### **QUIC 协议** | ||
|
||
为了解决http2中tcp层存在的队头阻塞问题,http3使用了基于udp的QUIC协议,QUIC主要有以下三个特点 | ||
|
||
- 无队头阻塞:QUIC 协议也有类似 HTTP/2 Stream 与多路复用的概念,也是可以在同一条连接上并发传输多个 Stream,Stream 可以认为就是一条 HTTP 请求。 | ||
|
||
QUIC 有自己的一套机制可以保证传输的可靠性的。**当某个流发生丢包时,只会阻塞这个流,其他流不会受到影响,因此不存在队头阻塞问题**。这与 HTTP/2 不同,HTTP/2 只要某个流中的数据包丢失了,其他流也会因此受影响。 | ||
|
||
所以,QUIC 连接上的多个 Stream 之间并没有依赖,都是独立的,某个流发生丢包了,只会影响该流,其他流不受影响。 | ||
|
||
- *更快的连接建立*:之前由于tcp和tls是分层的,分别属于内核实现的传输层、openssl 库实现的表示层,因此它们难以合并在一起,需要分批次来握手,先 TCP 握手,再 TLS 握手。HTTP/3 在传输数据前虽然需要 QUIC 协议握手,但是这个握手过程只需要 1 RTT,握手的目的是为确认双方的「连接 ID」,连接迁移就是基于连接 ID 实现的。 | ||
|
||
但是 HTTP/3 的 QUIC 协议并不是与 TLS 分层,而是 QUIC 内部包含了 TLS,它在自己的帧会携带 TLS 里的“记录”,再加上 QUIC 使用的是 TLS/1.3,因此仅需 1 个 RTT 就可以「同时」完成建立连接与密钥协商,如下图: | ||
|
||
![TCP HTTPS(TLS/1.3) 和 QUIC HTTPS ](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/HTTP/28-HTTP3%E4%BA%A4%E4%BA%92%E6%AC%A1%E6%95%B0.png) | ||
|
||
- *连接迁移*:基于 TCP 传输协议的 HTTP 协议,由于是通过四元组(源 IP、源端口、目的 IP、目的端口)确定一条 TCP 连接。![TCP 四元组](https://cdn.xiaolincoding.com//mysql/other/format,png-20230309231026577.png) | ||
|
||
那么**当移动设备的网络从 4G 切换到 WIFI 时,意味着 IP 地址变化了,那么就必须要断开连接,然后重新建立连接**。而建立连接的过程包含 TCP 三次握手和 TLS 四次握手的时延,以及 TCP 慢启动的减速过程,给用户的感觉就是网络突然卡顿了一下,因此连接的迁移成本是很高的。 | ||
|
||
而 QUIC 协议没有用四元组的方式来“绑定”连接,而是通过**连接 ID** 来标记通信的两个端点,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化后,导致 IP 地址变化了,只要仍保有上下文信息(比如连接 ID、TLS 密钥等),就可以“无缝”地复用原连接,消除重连的成本,没有丝毫卡顿感,达到了**连接迁移**的功能。 | ||
|
||
### 结论 | ||
|
||
所以, QUIC 是一个在 UDP 之上的**伪** TCP + TLS + HTTP/2 的多路复用的协议。 | ||
|
||
QUIC 是新协议,存在很多兼容性的问题,对于很多网络设备,根本不知道什么是 QUIC,只会当做 UDP,这样会出现新的问题,因为有的网络设备是会丢掉 UDP 包的,而 QUIC 是基于 UDP 实现的,那么如果网络设备无法识别这个是 QUIC 包,那么就会当作 UDP包,然后被丢弃。 | ||
|
||
HTTP/3 现在普及的进度非常的缓慢,不知道未来 UDP 是否能够逆袭 TCP。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# [浏览器输入url会发生什么 - 巨详细完整版](https://juejin.cn/post/7279093851000242234?searchId=20240125160411B5BF1DCDDFB0C09372EA#heading-22) | ||
|
||
## 大致的执行顺序 | ||
|
||
- URL解析 | ||
- DNS 解析:缓存判断 + 查询IP地址 | ||
- TCP 连接:TCP 三次握手 | ||
- SSL/TLS四次握手(只有https才有这一步) | ||
- 浏览器发送请求 | ||
- 服务器响应请求并返回数据 | ||
- 浏览器解析渲染页面 | ||
- 断开连接:TCP 四次挥手 | ||
|
||
## 总结 | ||
|
||
- 浏览器先判断是否为合法的url格式,不合法则在搜索引擎中搜索 | ||
- 合法后,DNS解析会先判断缓存中是否有url的ip地址。 | ||
- 缓存的查询顺序是:浏览器缓存 -> 操作系统缓存(本地的hosts文件) -> 路由器缓存 -> 本地的DNS服务器缓存 | ||
- 在缓存中没有的情况,则向服务器发起请求查询ip地址。 | ||
- 查询IP地址的顺序是:根域名服务器 -> 顶级域名服务器 -> 权威域名服务器。直到查找到返回,并将其存储在缓存中下次使用 | ||
- TSP建立连接,也就是三次握手 | ||
- 第一次握手,携带建立连接请求SYN=1和随机序列seq=x | ||
- 第二次握手,携带确定字段ACK=1、连接请求SYN=1、随机序列seq=y和ack为上一次握手的seq+1,就是x+1 | ||
- 第三次握手,携带确定字段ACK=1、ack=y+1、seq=x+1 | ||
- 如果是https,还有一个TLS四次握手 | ||
- 第一次握手,客户端向服务端发送 支持的协议版本 + 支持的加密方法 + 生成的随机数 | ||
- 第二次握手,服务端向客户端发送 证书 + 公钥 + 随机数 | ||
- 第三次握手前,客户端会先验证证书有没有过期、域名对不对、是否可信机构颁发的。 | ||
- 没有问题或者用户接受不受信的证书,浏览器会生成一个新的随机数 | ||
- 第三次握手,将之前的三个随机数通过一定的算法生成会话秘钥,之后的加密解密都是用这个秘钥 | ||
- 第四次握手,服务端收到回复,是用确定的加密方法进行解密,得到第三个随机数,使用同样的算法计算出会话秘钥 | ||
- 建立连接之后,浏览器发送http请求 | ||
- 请求报文由请求行、请求头、空行和请求体组成 | ||
- 服务器解析请求报文,返回响应报文 | ||
- 响应报文由响应行、响应头、空行和响应体组成,我们需要的html文件就在响应体中 | ||
- 浏览器拿到html文件并开始解析,构建dom tree。遇到css文件,下载并构建CSSOM tree。等到两者都构建完成之后,一起构建Render tree。然后进行布局和绘制 | ||
- 其中遇到了script标签,则停止构建dom tree,等下载完成之后才会继续构建dom tree | ||
- 当资源传输完毕之后,TSP关闭连接,进行四次挥手的操作,其中四次挥手的操作客户端和服务器都可以发起 | ||
- 第一次挥手,携带断开连接的FIN=1、确定字段ACK=1、随机序列seq=u,ack=v | ||
- 第二次挥手,携带确定字段ACK=1、随机序列seq=v,ack=u+1 | ||
- 第三次挥手,携带确定字段ACK=1、断开连接FIN=1、随机序列seq=w、ack=u+1 | ||
- 第四次挥手,携带确定字段ACK=1,随机序列seq=u+1,ack=w+1 | ||
- 等待2MSL后进入关闭状态 | ||
- 断开连接,结束通讯 |