什么是 HTTP 协议
HTTP(超文本传输协议),是一种实现网络通信的规范。它定义了客户端和服务器之间交换报文的格式和方式,默认使用的是 80 端口,其底层使用 TCP 作为传输层协议,保证了数据传输的可靠性。
特点:
-
无状态:协议对客户端没有状态存储,对事物处理没有“记忆”能力,比如访问一个网站需要反复进行登录操作
-
基于请求和响应:基本的特性,由客户端发起请求,服务端响应
-
使用明文通信
-
无连接:很多资料都将“无连接”作为 HTTP 的一个特点,但是并不准确。
-
在 HTTP/1.0 中每次连接要经历 TCP 的 3 次握手和 4 次挥手,这可以看作是短连接,尽管可以通过
Keep-Alive来避免,但并不是所有的浏览器都兼容,因为在 HTTP/1.0 中Keep-Alive并没有得到官方的广泛支持; -
在 HTTP/1.1
Keep-Alive被正式纳入规范,并且默认情况下是开启的,也就是说默认是长连接,除非在请求或响应的 HTTP 头部中包含Connection: close。这样,在请求/响应完成后,TCP 连接就会被关闭。。 -
在 HTTP/2 中引入了多路复用和服务器推送等技术,单个 TCP 连接被用于所有 HTTP 消息的交换
-
HTTP/3 用 QUIC 作为传输层协议,不是基于 TCP 了
-
HTTP 各版本区别
HTTP/1.0
在 HTTP/1.0 中,每个 TCP 连接通常只能处理一个 HTTP 请求和一个响应,即非持久连接(也称为短连接)。这意味着对于每个 HTTP 请求,都需要进行 TCP 的三次握手来建立连接,并在请求和响应完成后进行 TCP 的四次挥手来关闭连接,极大的增加了通信开销。。
HTTP/1.1
-
HTTP/1.1 引入了更多的缓存控制策略,如 Cache-Control、ETag 等
-
HTTP/1.1 可以在请求头中加入
Range头部,表示允许范围请求,用于指定希望接收的资源范围 -
HTTP/1.1 的请求消息和响应消息都必须包含
Host头部,用于指定请求的目标主机名和端口号(如果端口号不是80) -
管线化,客户端可以同时发出多个HTTP请求,而不用一个个等待响应
-
HTTP/1.1 默认开启持久连接(长连接),一定时间内,同一域名下的 HTTP 请求,只要两端都没有提出断开连接,则持久保持 TCP 连接状态,其他请求可以复用这个连接通道。但是持久连接采用阻塞模式,下次请求必须等到上次响应返回后才能发起,如果上次的请求还没返回响应内容,下次请求就只能等着(就是常说的线头阻塞)。
HTTP/2.0
在 HTTP/2 中,有两个非常重要的概念,分别是帧(frame)和流(stream),理解这两个概念是理解下面多路复用的前提。
帧代表数据传输的最小的单位,每个帧都有序列标识表明该帧属于哪个流。
流也就是多个帧组成的数据流,每个流表示一个请求。
-
二进制分帧: HTTP/2.0 通过采用二进制格式进行协议解析,克服了HTTP/1.x 基于文本解析的缺陷(例如空格、换行符、引号等字符在文本中的使用可能导致解析上的歧义或错误),提高了传输效率,增强了功能支持,并提升了协议的稳定性和可靠性。
-
多路复用: HTTP/2.0 支持多路复用,这是 HTTP/1.1 持久连接的升级版。
-
多路复用,就是在一个 TCP 连接中可以存在多条流,也就是可以发送多个请求,服务端则可以通过帧中的标识知道该帧属于哪个流(即请求),通过重新排序还原请求。
-
多路复用允许并发的发起多个请求,每个请求及该请求的响应不需要等待其他的请求或响应,避免了线头阻塞问题。这样某个请求任务耗时严重,不会影响到其它连接的正常执行,极大的提高传输性能。
-
简单来说就是 HTTP/1.1 中多个请求通常是串行的,而 HTTP/2.0 则支持多个请求并行处理
-
-
头部压缩: HTTP/1.x 的请求和响应头部带有大量信息,而且每次请求都要重复发送。HTTP/2.0 引入的头部压缩机制,通过 HPACK 算法和静态/动态字典,高效压缩 HTTP 头部信息,显著减少传输数据量,加快页面加载速度,优化缓存效率。
-
服务端推送: HTTP/2.0 的服务端推送功能允许服务器在响应客户端的初始请求时,预测并主动推送客户端可能需要的额外资源。这些资源可以是 CSS 样式表、JavaScript 脚本、图片等。通过服务端推送,客户端可以在一次 HTTP 连接中同时获取到多个资源,减少了后续的请求次数和延迟。
| 特性 | HTTP/1.1 长连接 | HTTP/2.0 多路复用 |
|---|---|---|
| 连接类型 | 持久连接,减少建立/关闭开销 | 持久连接,支持并行处理 |
| 请求处理 | 顺序处理 | 并行处理,解决队头阻塞 |
| 数据传输 | 文本协议,可能存在解析复杂性 | 二进制分帧,提高传输效率 |
| 头部信息 | 每次请求/响应需携带完整头部,存在冗余 | 引入头部压缩,减少传输量 |
| 额外功能 | 无服务器推送 | 支持服务器推送,减少请求延迟 |
HTTP/3.0
HTTP/2.0 使用了多路复用,一般来说同一域名下只需要使用一个 TCP 连接。但当这个连接中出现了丢包的情况,那就会导致整个 TCP 都要开始等待重传,也就导致了后面的所有数据都被阻塞了。反而对于 HTTP/1.0 来说,可以开启多个 TCP 连接,出现丢包反倒只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。 出现包阻塞的原因是因为底层 TCP 协议导致的问题,但是修改 TCP 协议是不现实的问题。既然不能修改你,那就另起一个协议取代你。Google 基于 UDP 协议推出了一个的 QUIC 协议,并且使用在了 HTTP/3 上。
QUIC 基于 UDP,但是 UDP 本身存在不稳定性等诸多问题,所以 QUIC 在 UDP 的基础上新增了很多功能,比如多路复用、0-RTT、使用 TLS1.3 加密、流量控制、有序交付、重传等等功能。
HTTP/3.0 的几个主要功能点:
-
避免包阻塞: 多个流的数据包在 TCP 连接上传输时,若一个流中的数据包传输出现问题,TCP 需要等待该包重传后,才能继续传输其它流的数据包。但在基于 UDP 的 QUIC 协议中,不同的流之间的数据传输真正实现了相互独立互不干扰,某个流的数据包在出问题需要重传时,并不会对其他流的数据包传输产生影响。
-
快速重启会话: 普通基于 TCP 的连接,是基于两端的 I、端口、协议来建立的。在网络切换场景,例如手机端切换了无线网,使用 4G 网络,会改变本身的 IP,这就导致 TCP 连接必须重新创建。而 QUIC 协议使用特有的 UUID 来标记每一次连接,在网络环境发生变化的时候,只要 UUID 不变,就能不需要握手,继续传输数据。
-
0-RTT: 是一种在协议握手过程中减少延迟的机制,它允许客户端在建立连接的同时发送加密的应用数据,而无需等待服务器完成完整的握手过程。也就是说客户端可以在发送 Client Hello 消息时,立即附带加密的应用数据。
| 优化方面 | HTTP/2.0 | HTTP/3.0 |
|---|---|---|
| 传输层协议 | TCP | QUIC(基于UDP) |
| 连接建立和握手时延 | TCP三次握手 + TLS四次握手,时延较长 | QUIC同时处理连接和TLS握手,支持0-RTT,时延显著降低 |
| 性能提升 | 多路复用,但存在TCP队头阻塞问题 | QUIC原生支持多路复用,无队头阻塞,提高资源利用效率 |
| 连接迁移和持久性 | 网络变化时需重新建立TCP连接和TLS握手 | 支持连接迁移,网络变化时无缝复用原连接,减少重连成本 |
| 安全性 | 支持TLS加密,但握手过程与TCP连接分离 | TLS加密集成到QUIC协议栈中,更高效的安全机制 |
| 头部压缩 | 使用HPACK算法进行头部压缩 | 继承并可能进一步优化HTTP/2.0的头部压缩功能(具体取决于实现) |
| 流量控制和拥塞控制 | 依赖于TCP的流量控制和拥塞控制算法 | QUIC实现自己的流量控制和拥塞控制算法,可能更适应Web流量特性 |
HTTP 请求为什么要分 header 和 body
-
结构清晰: HTTP 头部包含了关于请求的元数据(metadata),比如请求的方法(GET, POST, PUT 等)、请求的 URL、请求的协议版本、请求和响应的缓存信息、内容类型等。 而 HTTP 主体则包含了实际要发送或接收的数据。将这两部分分开,有助于服务器和客户端更好地理解请求的内容和目的。
-
灵活性: 由于 HTTP 头部和主体是分离的,因此它们可以独立地进行扩展。例如,可以添加新的头部字段来支持新的功能或协议,而不需要改变主体的格式。 同样,主体部分也可以根据需要采用不同的格式(如 JSON、XML、文本、二进制等),从而支持不同的数据传输需求。
-
安全性:通过 HTTP 头部中的某些字段(如 Authorization、Cookie 等),可以实现身份验证和授权等安全功能。 将这些安全相关的信息与实际的请求数据分开,有助于更好地保护用户的隐私和安全。
HTTP 报文组成
请求报文
-
请求行:包括请求方法、URL、协议/版本
-
请求头(Request Header):一系列的键值对
-
空行:用于区分请求头部和请求正文
-
请求正文(Request Body):不是所有的请求报文都包含请求正文,如 GET 请求通常不包含请求正文。
响应报文
-
状态行:包含 HTTP 协议版本、状态码和状态消息,如 HTTP/1.1 200 OK
-
响应头:一系列的键值对
-
空行:用于区分响应头部和响应正文
-
响应正文(Response Body)
HTTP 缓存
是在 HTTP 协议中定义的一种数据缓存机制,通过在客户端(如浏览器)或代理服务器(如 nginx)中存储响应数据,以便在后续请求中复用这些数据
HTTP 缓存主要解决哪些问题?
-
减少不必要的网络传输
-
减低延迟、提高响应速度
-
减少服务器负载
-
可以离线预览
缺点就是会占用内存。
HTTP 缓存又分为两种两种缓存,强制缓存和协商缓存
强制缓存
如果浏览器判断请求的目标资源有效命中强缓存,则可以直接从内存中读取目标资源,无需与服务器做任何通讯。
Expires
在以前,我们通常会使用响应头的 Expires 字段去实现强缓存:
public class CacheControlServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置内容类型
response.setContentType("text/html;charset=UTF-8");
// 创建一个表示资源过期时间的Date对象
// 例如,设置资源在1小时后过期
Date expires = new Date(System.currentTimeMillis() + 1000 * 60 * 60); // 1小时后的时间
// 设置Expires头部
response.setDateHeader("Expires", expires.getTime());
// 你可以在这里添加更多的逻辑来生成响应内容
// 响应完成
}
// 注意:doPost等其他方法也可以根据需要被重写
}
Expires 判断强缓存是否过期的机制是:获取本地时间戳,与资源文件中的 Expires 字段的时间做比较,在时间范围内,则从内存(或磁盘)中读取缓存返回。
这里有一个巨大的漏洞:如果我本地时间不准咋办?
所以,Expires 字段几乎不被使用了。现在的项目中,我们使用 Cache-control 字段来代替 Expires 字段的强缓存功能。
Cache-control
Cache-control 是在资源的响应头上设置缓存时间,单位是秒。
response.setHeader("Cache-Control", "max-age=3600"); // 设置资源在1小时内有效
从第一次请求资源的时候开始,往后 N 秒内,资源若再次请求,则直接从内存(或磁盘)中读取,不与服务器做任何交互。
Cache-Control 有 6 个属性:
-
max-age:决定客户端资源被缓存多久。
-
s-maxag:决定代理服务器(如 nginx)缓存的时长。
-
no-cache:表示强制进行协商缓存,即跳过强缓存校验,直接去服务器进行协商缓存。
-
no-store:是表示禁止任何缓存策略。
-
public:表示资源即可以被浏览器缓存也可以被代理服务器缓存。
-
private:表示资源只能被浏览器缓存。
no-cache 和 no-store 互斥(即不能同时存在),public 和 private 互斥
Cache-Control 设置多个属性:
response.setHeader("Cache-Control", "max-age=10000,s-maxage=200000,public");
协商缓存
协商缓存主要有四个头字段,它们两两组合配合使用,Last-Modified 和 If-Modified-Since 一组,Etag 和 If-None-Match 一组,当同时存在的时候会以Etag 和 If-None-Match 为主。
当命中协商缓存的时候,服务器 HTTP 状态码会返回 304,让客户端直接从本地缓存里面读取文件。
Last-Modified 和 If-Modified-Since
流程:
1、首次请求资源
1.1、浏览器发送请求:
浏览器首次向服务器请求某个资源(如HTML、CSS、JS文件等)
1.2、服务器响应请求:
-
服务器在返回资源的同时,在 HTTP 响应头中添加 Last-Modified 字段,该字段的值表示资源在服务器上的最后修改时间
-
浏览器接收资源并缓存起来,同时缓存响应头中的 Last-Modified 值
2、再次请求资源
2.1、浏览器检查缓存:
当浏览器再次请求相同的资源时,它会先检查本地缓存中是否存在该资源
2.2、构造请求头:
如果缓存资源存在且未过期且协商缓存被启用,浏览器会在 HTTP 请求头中添加 If-Modified-Since 字段,其值为上一次请求时服务器返回的 Last-Modified 值
2.3、发送请求:
浏览器将包含 If-Modified-Since 请求头的请求发送给服务器
3、服务器处理请求
3.1、检查资源修改时间:
-
服务器收到请求后,会检查请求头中的 If-Modified-Since 字段。
-
然后,服务器会将该字段值与资源在服务器上的当前最后修改时间做比较。
3.2、返回响应:
-
如果资源未修改(即服务器的最后修改时间未超过 If-Modified-Since 指定的时间):
-
服务器将返回 HTTP 状态码 304(Not Modified),并且不会返回资源内容。
-
浏览器接收到 304 状态码后,会从本地缓存中加载资源。
-
-
如果资源已修改:
-
服务器将返回 HTTP 状态码 200(OK),并发送资源的新内容。
-
同时,服务器还会在响应头中更新 Last-Modified 值,以便下次请求时使用。
-
4、浏览器更新缓存
-
如果服务器返回了 304 状态码,浏览器将保持本地缓存不变,并继续从缓存中加载资源。
-
如果服务器返回了 200 状态码和新的资源内容,浏览器将更新本地缓存中的资源文件和 Last-Modified 值。
这种方式的重点是判断资源文件的修改时间,以此来判断资源文件有没有被修改,而不是判断时间有效期。
这种方式的缺点是:
-
只要编辑了,不管内容是否真的有改变,都会更新最后修改时间。
-
Last-Modified 过期时间只能精确到秒。如果在同一秒既修改了文件又获取文件,客户端是获取不到最新文件的。
为了解决上述问题,从 HTTP.1 开始新增了一个头信息,ETag。
ETag 和 If-None-Match
ETag 和 If-None-Match 是 HTTP/1.1 引入,If-Modified-Since 和 Last-Modified HTTP/1.0 中就已经存在。
ETag 就是将 Last-Modified 那套比较时间戳的形式修改成了比较文件指纹。
文件指纹就是根据文件内容计算出的唯一哈希值。文件内容一旦改变则指纹改变。
ETag 和 If-None-Match 流程与 Last-Modified 和 If-Modified-Since 流程几乎一样,只是将 Last-Modified 替换成 ETag,If-Modified-Since 替换成 If-None-Match,比较文件修改时间替换成比较文件指纹
ETag 有强验证和弱验证:
-
强验证:哈希码深入到每个字节,哪怕文件中只有一个字节改变了,也会生成不同的哈希值,它可以保证文件内容绝对的不变。
-
弱验证:提取文件的部分属性来生成哈希值,整体速度会比强验证快,但是准确率不高
ETag 缺点:
- 计算文件指纹意味着服务端需要更多的计算开销,文件尺寸大、数量多会影响服务器的性能,尤其是强验证会非常消耗计算量
很多网站在获取静态资源时会同时使用 Last-Modified 和 ETag,可能是考虑浏览器的兼容性问题吧。
强制缓存和协商缓存区别
| 强制缓存 | 协商缓存 | |
|---|---|---|
| 工作原理 | 浏览器直接从本地缓存中读取资源,不向服务器发送请求 | 浏览器向服务器发送请求,询问资源是否更新,根据服务器响应决定是否使用缓存 |
| HTTP头字段 | 主要依赖 Cache-Control 或 Expires | 主要依赖 ETag 或 Last-Modified |
| 响应状态码 | 缓存命中时,无请求发出,因此无状态码 | 缓存命中时返回 304 Not Modified |
| 适用场景 | 适用于不经常变动的静态资源,如图片、CSS、JavaScript文件等 | 适用于可能被频繁更新的资源,如动态数据等 |
| 性能影响 | 减少网络请求,提高页面加载速度,降低服务器压力 | 仍然需要网络请求,但可以减少数据传输量,对于频繁更新的资源能确保用户获取最新内容 |
如果同时设置了强制缓存和协商缓存,浏览器会先判断强制缓存是否命中,如果强制缓存未命中,则再判断协商缓存是否命中。
使用 nginx 配置缓存
server {
listen 80;
server_name your-domain.com; # 修改为你的域名
location /vue-app/ {
alias /path/to/your/dist/; # 修改为你的Vue项目dist目录的实际路径
try_files $uri $uri/ /vue-app/index.html; # 对于单页面应用,确保所有路由都返回index.html
# 开启强制缓存
add_header Cache-Control "public, max-age=3000";
# 跳过强制缓存,强制进行协商缓存
# add_header Cache-Control "no-cache";
# 禁用缓存
# add_header Cache-Control "no-store";
# nginx 会自动给静态文件添加 Last-Modified 和 ETag 头部,无需额外配置
......
}
# 其他location块或server配置...
}
浏览器的缓存策略
在浏览器地址栏中输入然后通过回车/书签访问:这种情况下,默认使用浏览器的缓存机制,如果缓存有对应的资源,则直接从缓存中读取,否则会向服务器请求资源。
F5/点击工具栏中的刷新按钮/右键菜单重新加载:这种情况下,浏览器发送的请求头中加入了 Cache-Control: no-cache,这表示客户端不想使用缓存数据。服务器收到请求后,会忽略客户端的缓存,重新生成响应返回给客户端。
Ctl+F5:这种情况下,浏览器发送的请求头中加入了 Cache-Control: no-store,这表示客户端不希望在任何情况下使用缓存,所有数据都必须从服务器获取。服务器收到请求后,不仅忽略客户端的缓存,而且还不会在服务器端缓存响应数据。
HTTP 状态码
HTTP状态码表示客户端HTTP请求的返回结果、标识服务器处理是否正常、表明请求出现的错误等。
| 2XX | 成功(这系列表明请求被正常处理了) |
|---|---|
| 200 | OK,表示从客户端发来的请求在服务器端被正确处理 |
| 204 | No content,表示请求成功,但响应报文不含实体的主体部分 |
| 206 | Partial Content,进行范围请求成功 |
| 3XX | 重定向(表明浏览器要执行特殊处理) |
|---|---|
| 301 | moved permanently,永久性重定向,表示资源已被分配了新的 URL |
| 302 | found,临时性重定向,表示资源临时被分配了新的 URL |
| 303 | see other,表示资源存在着另一个 URL,应使用 GET 方法获取资源(对于301/302/303响应,几乎所有浏览器都会删除报文主体并自动用GET重新请求) |
| 304 | not modified,协商缓存,与重定向无关 |
| 307 | temporary redirect,临时重定向,和302含义类似,但是期望客户端保持请求方法不变向新的地址发出请求 |
| 4XX | 客户端错误 |
|---|---|
| 400 | bad request,请求报文存在语法错误 |
| 401 | unauthorized,表示发送的请求需要有通过 HTTP 认证的认证信息 |
| 403 | forbidden,表示对请求资源的访问被服务器拒绝,可在实体主体部分返回原因描述 |
| 404 | not found,表示在服务器上没有找到请求的资源 |
| 5XX | 服务器错误 |
|---|---|
| 500 | internal sever error,表示服务器端在执行请求时发生了错误 |
| 501 | Not Implemented,表示服务器不支持当前请求所需要的某个功能 |
| 503 | service unavailable,表明服务器暂时处于超负载或正在停机维护,无法处理请求 |
HTTP 头部字段
| 通用首部 | 作用(请求报文和响应报文都可能使用) |
|---|---|
| Cache-Control | 控制缓存的行为:no-cache(强制向服务器再次验证)、no-store(不做任何缓存)、max-age=111111(资源可缓存最大时间 秒)、public(客户端、代理服务器都可利用缓存)、private(代理服务器不可用缓存) |
| Connection | 浏览器想要优先使用的连接类型: keep-alive close(开启和关闭持久连接) |
| Date | 创建报文时间 |
| Pragma | 只用于请求报文,客户端要求中间服务器不返回缓存的资源 |
| Via | 代理服务器相关信息,每经过一个代理服务器就会添加相关信息,用逗号分割 |
| Transfer-Encoding | 传输编码方式:chunked分块传输 |
| Upgrade | 要求客户端使用的升级协议,需配合Connection: Upgrade一起使用:websocket |
| Warning | 缓存相关问题的警告 |
| 请求首部 | 作用(请求报文专用) |
|---|---|
| Accept | 能正确接收的媒体类型:application/json text/plain |
| Accept-Charset | 能正确接收的字符集: unicode-1-1 |
| Accept-Encoding | 能正确接收的编码格式列表:gzip deflate |
| Accept-Language | 能正确接收的语言列表:zh-cn,zh;1=0.9,en,1=0.8 |
| Authorization | 客户端认证信息:Bearer dSdSdFFlsfdjasd123,一般存token用 |
| Cookie | 发送给服务器的Cookie信息 |
| Expect | 期待服务端的指定行为 |
| From | 请求方邮箱地址 |
| Host | 服务器的域名,用于区分单台服务器多个域名的虚拟主机,是HTTP/1.1唯一必须包含的字段。 |
| If-Match | 两端资源标记比较,只有判断条件为真服务端才会接受请求:If-Mach: "123456,和服务端文件标记比较 |
| If-Modified-Since | 本地资源未修改返回 304(比较时间) |
| If-None-Match | 本地资源未修改返回 304(比较标记) |
| User-Agent | 客户端信息 |
| Max-Forwards | 限制可被代理及网关转发的次数 |
| Proxy-Authorization | 向代理服务器发送验证信息 |
| Range | 请求某个内容的一部分,配合If-Range使用 |
| Referer | 请求发起页面的原始URI |
| TE | 传输编码方式 |
| 响应首部 | 作用(响应报文专用) |
|---|---|
| Accept-Ranges | 告知客户端服务器是否可接受范围请求,是bytes,否none |
| Age | 资源在代理缓存中存在的时间 |
| ETag | 资源标识,资源发生变化时标识也会发生改变 |
| Location | 客户端重定向到某个 URL |
| Proxy-Authenticate | 向代理服务器发送验证信息 |
| Server | 服务器名字:Apache Nginx |
| WWW-Authenticate | 获取资源需要的认证方案 |
| Set-Cookie | 需要存在客户端的信息,一般用于识别用户身份 |
| 实体首部 | 作用(补充请求报文或响应报文相关信息) |
|---|---|
| Allow | 资源的正确请求方式:GET HEAD POST |
| Content-Encoding | 内容的编码格式:gzip deflate |
| Content-Language | 内容使用的语言:zh-CN |
| Content-Length | request body 长度(即实体主体的大小) |
| Content-Location | 返回数据的备用地址 |
| Content-MD5 | Base64加密格式的内容 MD5检验值 |
| Content-Range | 响应主体的内容范围 |
| Content-Type | 内容的媒体类型(如'application/json;charset=UTF-8'则会发送预检请求) |
| Expires | 内容的过期时间 |
| Last_modified | 内容的最后修改时间 |
在浏览器访问一个 URL 会发生什么?
-
URL 解析:首先会判断输入的是一个合法 url 还是关键词,并根据输入的内容进行相应的操作。
-
查找缓存:浏览器会判断所请求的资源是否在浏览器缓存中,以及是否失效。如果没有失效就直接使用;如果没有缓存或失效了,就继续下一步。
-
DNS 解析:此时需要获取 URL 中域名对应的 IP 地址。
-
浏览器会依次查看浏览器缓存、操作系统缓存中是否有 IP 地址
-
如果缓存中没有就会向本地域名服务器发起请求,获取 IP 地址。本地域名服务器也会先检查缓存,有则直接返回;
-
如果也没有,则采用迭代查询方式,向上级域名服务器查询。
-
先向根域名服务器发起请求,获取顶级域名服务器的地址;
-
再向顶级域名服务器发起请求以获取权限域名服务器地址;
-
然后向权限域名服务器发起请求并得到 URL 中域名对应的 IP 地址。
-
-
本地域名服务器、操作系统、浏览器依次将 IP 缓存起来
-
-
建立 TCP 连接:根据ip地址,
三次握手与服务器建立TCP连接。 -
发起请求:浏览器向服务器发起HTTP请求。
-
响应请求:服务器响应HTTP请求,将相应的HTML文件返回给浏览器。
-
关闭 TCP 连接:
四次挥手关闭TCP连接。 -
渲染页面:浏览器解析HTML内容,并开始渲染:
-
构建 DOM 树
-
构建 CSSOM 树
-
生成渲染树(Render Tree)
-
布局(Layout)
-
绘制(Paint)
-
重排和重绘(Reflow & Repaint)
详细过程请参阅:前端(五):浏览器渲染流程
-
GET 和 POST 请求的本质区别是什么
标准答案:
-
GET 在浏览器回退时是无害的,而 POST 会再次提交请求。
-
GET 产生的 URL 地址可以被保存为书签,而 POST 不可以。
-
GET 请求会被浏览器主动 cache,而 POST 不会,除非手动设置。
-
GET 请求只能进行 url 编码,而 POST 支持多种编码方式。
-
GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留。
-
GET 请求在 URL 中传送的参数是有长度限制的,而 POST 没有。
-
对参数的数据类型,GET 只接受 ASCII 字符,而 POST 没有限制。
-
GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息。
-
GET 参数通过 URL 传递,POST 放在 Request body 中。
(本标准答案参考自w3schools)
“很遗憾,这不是我们要的回答!”
请告诉我真相。。。
如果我告诉你 GET 和 POST 本质上没有区别你信吗?
让我们扒下 GET 和 POST 的外衣,坦诚相见吧!
GET 和 POST 是什么?HTTP 协议中的两种发送请求的方法。
HTTP 是什么?HTTP 是基于 TCP/IP 的关于数据如何在万维网中如何通信的协议。
HTTP 的底层是 TCP/IP。所以 GET 和 POST 的底层也是 TCP/IP,也就是说,GET/POST 都是 TCP 链接。GET 和 POST 能做的事情是一样一样的。你要给 GET 加上 request body,给 POST 带上 url 参数,技术上是完全行的通的。
在我大万维网世界中,TCP 就像汽车,我们用 TCP 来运输数据,它很可靠,从来不会发生丢件少件的现象。但是如果路上跑的全是看起来一模一样的汽车,那这个世界看起来是一团混乱,送急件的汽车可能被前面满载货物的汽车拦堵在路上,整个交通系统一定会瘫痪。
为了避免这种情况发生,交通规则 HTTP 诞生了。HTTP 给汽车运输设定了好几个服务类别,有 GET、POST、PUT、DELETE 等等,HTTP 规定,当执行 GET 请求的时候,要给汽车贴上 GET 的标签(设置 method 为 GET),而且要求把传送的数据放在车顶上(url 中)以方便记录。如果是 POST 请求,就要在车上贴上 POST 的标签,并把货物放在车厢里。
当然,你也可以在 GET 的时候往车厢内偷偷藏点货物,但是这是很不光彩;也可以在 POST 的时候在车顶上也放一些数据,让人觉得傻乎乎的。HTTP 只是个行为准则,而 TCP 才是 GET 和 POST 怎么实现的基本。
但是,我们只看到 HTTP 对 GET 和 POST 参数的传送渠道(url 还是 requrest body)提出了要求。“标准答案”里关于参数大小的限制又是从哪来的呢?
在我大万维网世界中,还有另一个重要的角色:运输公司。不同的浏览器(发起 http 请求)和服务器(接受 http 请求)就是不同的运输公司。虽然理论上,你可以在车顶上无限的堆货物(url 中无限加参数)。
但是运输公司可不傻,装货和卸货也是有很大成本的,他们会限制单次运输量来控制风险,数据量太大对浏览器和服务器都是很大负担。业界不成文的规定是,(大多数)浏览器通常都会限制 url 长度在 2K 个字节,而(大多数)服务器最多处理 64K 大小的 url。
超过的部分,恕不处理。如果你用 GET 服务,在 request body 偷偷藏了数据,不同服务器的处理方式也是不同的,有些服务器会帮你卸货,读出数据,有些服务器直接忽略,所以,虽然 GET 可以带 request body,也不能保证一定能被接收到哦。
好了,现在你知道,GET 和 POST 本质上就是 TCP 链接,并无差别。但是由于 HTTP 的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。
我们的大BOSS还等着出场呢。。。
这位BOSS有多神秘?当你试图在网上找"GET 和 POST 的区别"的时候,那些你会看到的搜索结果里,从没有提到他。他究竟是什么呢。。。
GET 和 POST 还有一个重大区别
简单的说:
- GET 产生一个 TCP 数据包;POST 产生两个 TCP 数据包。
长的说:
-
对于 GET 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200(返回数据);
-
而对于 POST,浏览器先发送 header,服务器响应 100 continue,浏览器再发送 data,服务器响应 200 ok(返回数据)。
也就是说,GET 只需要汽车跑一趟就把货送到了。而 POST 得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。
因为 POST 需要两步,时间上消耗的要多一点,看起来 GET 比 POST 更有效。因此 Yahoo 团队有推荐用 GET 替换 POST 来优化网站性能。但这是一个坑!跳入需谨慎。为什么?
-
GET 与 POST 都有自己的语义,不能随便混用。
-
据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的 TCP 在验证数据包完整性上,有非常大的优点。
-
并不是所有浏览器都会在 POST 中发送两次包,Firefox 就只发送一次。
现在,当面试官再问你"GET 与 POST 的区别"的时候,你的内心是不是这样的?