1、HTTP

点击阅读更多查看文章内容

状态码

 五大类 HTTP 状态码
  • 200 OK:成功

  • 301 Moved Permanently:永久重定向,请求的资源已永久移动到新的 URL。浏览器会缓存重定向结果,后续请求直接访问新地址,不再请求旧地址。

  • 302 Found:临时重定向,请求的资源临时移动到新的 URL。浏览器不会缓存重定向结果,后续请求仍会访问旧地址。(301和302都会在响应头里使用字段Location,指明后续要跳转的URL,浏览器会自动重定向新的URL)

  • 304 Not Modified:主要用于 缓存优化,表示客户端缓存的资源仍然有效(资源未修改),可以直接使用,而无需服务器重新传输资源内容。

  • 400 Bad Request:客户端发送的请求有语法错误,服务器无法理解。(请求参数、请求头或 URL 格式错误)

  • 403 Forbidden:服务器理解请求,但拒绝执行。(权限不足、IP 黑名单、文件权限设置不正确)

  • 404 Not Found:服务器未找到请求的资源。(URL 错误、资源被删除或移动、路由配置错误)

  • 500 Internal Server Error:服务器内部错误(代码 bug、配置错误、数据库连接失败)

  • 501 Not Implemented:未实现的功能(已经定义暂未实现)

  • 502 Bad Gateway:服务器作为网关或代理时,从上游服务器接收到无效响应(上游:如果服务A需要服务B的响应才能完成自身逻辑,则服务B是服务A的上游。)。

  • 503 Service Unavailable:服务器暂时无法处理请求,通常是由于过载或维护。


常见字段

Host:指定服务器的域名 Host: www.A.com

Content-Length:表明响应的数据长度 Content-Length: 1000

Content-Encoding:说明服务器返回的数据的压缩方法 Content-Encoding: gzip,客户端请求时使用Accept-Encoding: gzip, deflate 说明自己可以接收哪些压缩方法


GET与POST

  1. 用途
    • GET主要用于请求资源或获取数据。它用于从服务器获取数据,并且应当是“安全”的操作,不会对服务器上的资源进行修改。
    • POST:主要用于提交数据给服务器对指定的资源做出处理,例如表单提交或上传文件。它用于创建或更新服务器上的资源,通常会更改服务器的状态。
  2. 数据传输方式
    • GET:数据通过 URL 传递。参数附加在 URL 后面,格式为 ?key1=value1&key2=value2
    • POST:数据通过请求体(body)传递,POST 请求的数据没有大小限制(不过有一些实际的服务器和客户端可能会有限制)

缓存

针对重复性的HTTP请求,可以把这对 请求-响应 的数据都缓存在本地,通过缓存之前请求的资源,使后续请求可以直接使用缓存数据,而无需重新向服务器请求相同的内容。

HTTP的缓存有两种实现方式:强制缓存和协商缓存

强制缓存

原理:在缓存有效期内,客户端直接使用本地缓存数据,而不与服务器通信。

实现:使用HTTP响应头的 Cache-ControlExpires 头部字段实现,Cache-Control的优先级高于Expires。

Cache-Control

  • 作用:HTTP/1.1 引入的字段,提供更灵活的缓存控制机制,优先级高于 Expires
  • 常用指令:
    • **max-age=秒**:资源缓存的有效时间(从请求开始计算)。例如 max-age=3600 表示缓存1小时。
    • public:允许任何中间代理(如CDN)或浏览器缓存资源。
    • **private**:仅允许用户浏览器缓存,禁止代理服务器缓存(适用于用户私有数据)。
    • **no-cache**:缓存前必须向服务器验证资源是否过期(即使未过期)。
    • **no-store**:禁止缓存任何资源(适用于敏感数据)。
    • **must-revalidate**:缓存过期后必须重新验证,不能直接使用旧缓存。
  • 示例:
    • Cache-Control: public, max-age=604800

Expires

  • 作用:HTTP/1.0 的字段,指定一个具体的过期时间点(绝对时间)。优先级低于 Cache-Control

  • 示例

    • Expires: Wed, 21 Oct 2023 07:28:00 GMT

协商缓存:

原理:客户端请求资源时,服务器会检查缓存是否仍然有效,决定是否返回 304(Not Modified)响应,让客户端使用本地缓存(服务器无需返回所有数据)。

实现1:请求头的 If-None-Match 字段与响应头的 ETag 字段(推荐)

  • ETag(实体标签)是服务器生成的一个资源唯一标识符,类似文件的哈希值。

  • 客户端第一次请求资源时,服务器返回 ETag 头部。

  • 客户端后续请求时,在请求头中带上 If-None-Match 头部(携带上次 ETag 值)。

    If-None-Match: “737060cd8c284d8af7ad3082f209582d”

  • 服务器比较 ETag

    • 相同:返回 304 Not Modified,客户端继续使用缓存。
    • 不同:返回新资源,并生成新的 ETag

实现2:使用请求头的If-Modified-Since字段与响应头的Last-Modified字段

  • Last-Modified 记录资源的最后修改时间

  • 客户端第一次请求资源时,服务器返回 Last-Modified 头部。

  • 客户端后续请求时,带上 If-Modified-Since 头部(上次的 Last-Modified 时间)。

    If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT

  • 服务器比较资源的修改时间

    • 未修改:返回 304 Not Modified
    • 已修改:返回新资源,并更新 Last-Modified

缓存策略选择

  • 强制缓存 优先,适用于静态资源(如 JS、CSS、图片)。
  • 协商缓存 适用于可能变化的资源,减少带宽消耗。

缓存位置

  1. 浏览器缓存:存储在客户端本地,提高页面加载速度。
  2. CDN 缓存:由内容分发网络(CDN)存储资源,减少服务器压力。
  3. 代理服务器缓存:如 Nginx 反向代理,可缓存服务器响应。
  4. 服务器缓存:如 Redis/Memcached,在后端减少数据库查询。

HTTP 1.1

相比 HTTP/1.0,HTTP/1.1 进行了多个优化,包括持久连接(Keep-Alive)、管道化请求(Pipelining)、缓存控制、分块传输编码(Chunked Encoding)等,使得性能和灵活性大大提高

持久连接(Keep-Alive,默认开启)

  • HTTP/1.0 问题:每次请求都会建立一个新的 TCP 连接,消耗大量资源。

  • HTTP/1.1 解决方案

    • 默认使用 持久连接,即多个 HTTP 请求可以复用同一个 TCP 连接,避免频繁建立和关闭连接。

    • 服务器会在响应头中加入 Connection: keep-alive

    短连接与长连接

管道化请求(Pipelining,已被 HTTP/2 替代)

  • 问题:HTTP/1.0 需要等待前一个请求的响应才能发送下一个请求(队头阻塞)。

  • HTTP/1.1 解决方案:

    • 支持 Pipelining,即客户端可以同时发送多个请求,服务器按顺序处理并返回响应。

    • 但由于 服务器仍需按顺序返回响应,且存在队头阻塞问题(如果处理前面的请求耗时较长,那么后续的请求处理都会被阻塞),Pipelining 实际上很少被使用,HTTP/2 引入了多路复用彻底解决了这个问题。

      管道网络传输

无状态

  • 服务器在处理每个 HTTP 请求时,不会自动保留之前的请求信息,每次请求都必须携带所需的全部信息。
  • 好处:不需要管理每个客户端的状态,减少资源占用。
  • 坏处:需要额外的机制来维护用户状态 Cookie + Session
    • 服务器发送 Set-Cookie,客户端在每次请求时携带 Cookie 维持会话。服务器通过 Session ID 识别用户。

HTTPS

HTTP中信息都是明文传输的缺乏安全性

HTTPS在TCP和HTTP之间加入了SSL/TLS协议,使得报文能够加密传输。

  • 加密:防止数据被窃听(中间人攻击)。通过加密算法实现。
  • 数据完整性:防止数据在传输过程中被篡改(MITM 攻击)。通过摘要算法实现。
  • 身份认证:防止伪造网站(钓鱼攻击),确保访问的是正确的服务器。通过数字证书实现

混合加密:

  • 通过非对称加密交换会话密钥,后续就不再使用非对称加密
  • 基于会话密钥采用对称加密的方式加密明文数据

摘要+数字签名

发送方对内容计算出摘要同内容一起传输给对方,对方收到内容后也计算一个摘要并与发送方的摘要进行比较,如果相同则没有被篡改。

通过哈希算法可以确保内容不会被篡改,但是并不能保证「内容 + 哈希值」不会被中间人替换,因为这里缺少对客户端收到的消息是否来源于服务端的证明。

数字签名:服务端使用自己的私钥加密摘要,客户端根据服务端的公钥解密进行认证,如果可以解密,则说明该消息是服务端发送的

数字证书

证明公钥确实属于对应的服务器,而不是其他人伪造的密钥对

通过权威机构CA将服务器公钥存放在数字证书中,只要证书是可信的,公钥就是可信的

数子证书工作流程

HTTPS连接建立

SSL/TLS 协议基本流程:

  • 客户端向服务器索要并验证服务器的公钥。
  • 双方协商生产「会话秘钥」。
  • 双方采用「会话秘钥」进行加密通信。

前两步也就是 SSL/TLS 的建立过程,也就是 TLS 握手阶段。 TLS 的「握手阶段」涉及四次通信,使用不同的密钥交换算法,TLS 握手流程也会不一样的,现在常用的密钥交换算法有两种:RSA 算法 (opens new window)和 ECDHE 算法 (opens new window)。

HTTPS 连接建立过程
  1. 客户端(浏览器)发起 HTTPS 请求
    • 浏览器请求 https://example.com
    • 服务器响应,开始 TLS 握手
  2. 服务器返回 SSL 证书
    • 证书由 CA(证书颁发机构)签发,包含公钥、域名、有效期等信息。
    • 浏览器验证证书的合法性(检查是否被篡改、是否被信任)。
  3. 客户端和服务器协商加密方式
    • 浏览器和服务器协商支持的 TLS 版本、加密算法
  4. 客户端生成对称密钥
    • 浏览器使用服务器的公钥加密对称密钥,然后发送给服务器。
    • 服务器用私钥解密得到对称密钥
  5. 使用对称加密通信
    • 之后的 HTTP 请求和响应都使用这个对称密钥加密,保证数据安全。

HTTP/1.1、HTTP/2、HTTP/3

HTTP/1.1

HTTP/1.1 相比 HTTP/1.0 性能上的改进:

  • 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
  • 支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。

但 HTTP/1.1 还是有性能瓶颈:

  • 请求 / 响应头部(Header)未经压缩就发送,首部信息越多延迟越大。只能压缩 Body 的部分;
  • 发送冗长的首部。每次互相发送相同的首部造成的浪费较多;
  • 服务器是按请求的顺序响应的,如果服务器响应慢,会招致客户端一直请求不到数据,也就是队头阻塞
  • 没有请求优先级控制;
  • 请求只能从客户端开始,服务器只能被动响应

HTTP/2

HTTP/2 协议是基于 HTTPS 的,保证了安全性

那 HTTP/2 相比 HTTP/1.1 性能上的改进:

  • 头部压缩:HTTP/1.1 的 Header 信息较大,HTTP/2 采用 HPACK 进行压缩,减少带宽占用。在客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。
  • 二进制格式:HTTP/1.1 是文本协议,HTTP/2 使用二进制帧格式,解析更快,传输更高效。
  • 并发传输:单个 TCP 连接可同时传输多个 HTTP 请求和响应,不受顺序影响,彻底解决队头阻塞(HTTP/1.1 的主要问题)。
    • 将请求和响应分解为小的二进制帧(Frame),每个帧属于一个特定的流(Stream)。多个流的帧可以交错传输,接收端根据帧头中的流 ID 重新组装。流是 HTTP/2 中的一个逻辑概念,表示一个独立的请求-响应交互。
  • 服务器主动推送资源:服务器可以主动推送资源到客户端缓存,减少后续请求等待时间。比如客户端在访问HTML时,服务器可以直接主动推送CSS文件,减少消息传递的次数。(虽然能主动推送资源(如 CSS/JS),但仅限于与客户端请求相关的预加载,且不能推送任意动态数据,与websocket的全双工通信不同)

缺点:

  • 仍然基于 TCP,存在队头阻塞(HoL Blocking):虽然 HTTP/2 解决了 HTTP 层的队头阻塞,但 TCP 层仍然有丢包重传的阻塞问题,影响整体吞吐量。TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区里的数据返回给 HTTP 应用,那么当「前 1 个字节数据」没有到达时,后收到的字节数据只能存放在内核缓冲区里,只有等到这 1 个字节数据到达时,HTTP/2 应用层才能从内核中拿到数据,这就是 HTTP/2 队头阻塞问题。

    img

HTTP/3

HTTP/3 由 Google QUIC 协议演变而来,彻底抛弃 TCP,使用 UDP + QUIC 传输,提高连接速度稳定性

UDP 发送是不管顺序,也不管丢包的,所以不会出现像 HTTP/2 队头阻塞的问题。但 UDP 是不可靠传输的,不过基于 UDP 的 QUIC 协议 可以实现类似 TCP 的可靠性传输。

QUIC(Quick UDP Internet Connections)是一种基于 UDP 的传输层协议,最初由 Google 在 2012 年提出,并在 HTTP/3 中作为默认传输协议。它的目标是提高网络传输效率,减少延迟,并优化 HTTP/2 在 TCP 上的缺陷(如队头阻塞问题)

主要特点:

  • 基于 UDP,减少连接建立时间

    • QUIC 不使用 TCP,而是基于 UDP 传输,并在应用层实现可靠性保证。

    • 0-RTT 连接建立(如果之前通信过):

      • 传统的 TCP + TLS 需要 2~3 次 RTT来建立安全连接,而 QUIC 只需要 1 个 RTT,甚至可以做到 0-RTT(复用之前的加密信息)。
    • 1-RTT 连接建立(首次连接):

      • 结合 QUIC + TLS 1.3,QUIC 在1 次 RTT 内完成握手,相比 TCP 快了一倍。
  • 解决 TCP 的队头阻塞

    • HTTP/2 的多路复用仍受 TCP 队头阻塞影响:
      • 如果一个数据包丢失,整个 TCP 连接都会等待重传。
    • QUIC 在 UDP 之上实现了自己的流控制:
      • 每个 HTTP 请求/响应被视为独立的流,丢失的数据包只影响单个流,而不会影响整个连接。
  • 连接迁移

    • TCP 连接依赖 IP + 端口,如果切换网络(如 Wi-Fi → 4G),连接会断开。
    • QUIC 连接基于 “Connection ID”,与 IP/端口无关。当用户切换网络时,QUIC 连接可以自动恢复,而不需要重新握手。
  • 内置 TLS 加密

    • 传统的 HTTPS 需要 TCP + TLS,而 QUIC 直接在协议层集成了 TLS 1.3,所有 QUIC 连接默认是加密的。
    • 这不仅提高了安全性,还减少了握手延迟(因为 QUIC 和 TLS 1.3 可以同时握手)。
  • 更好的流控 & 拥塞控制

    • TCP 的丢包检测依赖超时(timeout),可能会导致不必要的延迟。
    • QUIC 基于 ACK 反馈机制,可以更快地调整数据传输速率,提高传输效率。

如何优化HTTP/1.1

使用缓存

避免发送HTTP请求,对于一些有重复性的HTTP请求,可以把这对 [请求-响应] 缓存在本地,那么下次发送请求时可以直接读取本地缓存的响应而无需发送网络请求,缓存的实现可以参考前文。

减少HTTP请求次数

  • 减少重定向请求次数:将重定向的工作在代理服务器上执行而无需客户端重新发送请求

    imgimg

    更进一步如果代理服务器知晓了重定向规则后,可以进一步减少消息传递的次数,直接请求新地址

    img
  • 合并请求:把多个访问小文件的请求合并成一个大的请求,虽然传输的总资源还是一样,但是减少请求,也就意味着减少了重复发送的 HTTP 头部。

    • 使用webpack等打包工具将多个 CSS 或 JavaScript 文件合并为一个文件,减少请求数。
    • 使用 CSS Sprites:将多个小图标合并为一张大图,无需分别请求每个小图标。
  • 延迟发送请求:一般 HTML 里会含有很多 HTTP 的 URL,当前不需要的资源,我们没必要也获取过来,于是可以通过「按需获取」的方式,来减少第一时间的 HTTP 请求次数。 请求网页的时候,没必要把全部资源都获取到,而是只获取当前用户所看到的页面资源,当用户向下滑动页面的时候,再向服务器获取接下来的资源,这样就达到了延迟发送请求的效果

减少HTTP响应的数据大小

  • 无损压缩:对原始资源建立统计模型,利用这个统计模型,将常出现的数据用较短的二进制比特序列表示,将不常出现的数据用较长的二进制比特序列表示,生成二进制比特序列一般是「霍夫曼编码」算法。
    • gzip是常见的无损压缩算法,在http请求头中添加 Accept-Encoding: gzip, deflate, br
    • 服务器收到后会选择一个服务器支持的压缩算法对响应资源进行压缩,通过响应头中的 Content-Encoding: gzip 字段告诉客户端
  • 有损压缩:将次要的数据舍弃,牺牲一些质量来减少数据量、提高压缩比,常用于多媒体文件
    • 关于图片压缩,目前压缩比最高的是WebP格式

既然有HTTP协议,为什么还要有RPC

  1. 性能需求
    • RPC 使用二进制协议,传输效率更高,适合对性能要求高的场景。
  2. 开发效率
    • RPC 提供类似本地方法调用的体验,减少开发工作量。
  3. 服务治理
    • RPC 框架通常内置服务发现、负载均衡、熔断等功能,适合微服务架构。
  4. 内部通信
    • 在内部服务之间,RPC 比 HTTP 更轻量、更高效

上面比较的 HTTP,其实特指的是现在主流使用的 HTTP/1.1,HTTP/2 在前者的基础上做了很多改进,所以性能可能比很多 RPC 协议还要好,甚至连 gRPC 底层都直接用的 HTTP/2。 那么问题又来了,为什么既然有了 HTTP/2,还要有 RPC 协议? 这个是由于 HTTP/2 是 2015 年出来的。那时候很多公司内部的 RPC 协议都已经跑了好些年了,基于历史原因,一般也没必要去换了。


既然有HTTP协议,为什么还要有WebSocket

怎么样才能在用户不做任何操作的情况下,网页能收到消息并发生变更

最常见的解决方案是,网页的前端代码里不断定时发 HTTP 请求到服务器,服务器收到请求后给客户端响应消息。 这其实时一种「伪」服务器推的形式。 它其实并不是服务器主动发消息到客户端,而是客户端自己不断偷偷请求服务器,只是用户无感知而已。 用这种方式的场景也有很多,最常见的就是扫码登录。

比如,某信公众号平台,登录页面二维码出现之后,前端网页根本不知道用户扫没扫,于是不断去向后端服务器询问,看有没有人扫过这个码。而且是以大概 1 到 2 秒的间隔去不断发出请求,这样可以保证用户在扫码后能在 1 到 2 秒内得到及时的反馈,不至于等太久。

使用HTTP定时轮询会有两个比较明显的问题:

  • 当你打开 F12 页面时,你会发现满屏的 HTTP 请求。虽然很小,但这其实也消耗带宽,同时也会增加下游服务器的负担。
  • 最坏情况下,用户在扫码后,需要等个 12 秒,正好才触发下一次 HTTP 请求,然后才跳转页面,用户会感到明显的卡顿。 使用起来的体验就是,二维码出现后,手机扫一扫,然后在手机上点个确认,这时候卡顿等个 12 秒,页面才跳转

解决方案:长轮询

我们知道,HTTP 请求发出后,一般会给服务器留一定的时间做响应,比如 3 秒,规定时间内没返回,就认为是超时。 如果我们的 HTTP 请求将超时设置的很大,比如 30 秒,在这 30 秒内只要服务器收到了扫码请求,就立马返回给客户端网页。如果超时,那就立马发起下一次请求。 这样就减少了 HTTP 请求的个数,并且由于大部分情况下,用户都会在某个 30 秒的区间内做扫码操作,所以响应也是及时的。

图片

像这种发起一个请求,在较长时间内等待服务器响应的机制,就是所谓的长轮询机制。我们常用的消息队列 RocketMQ 中,消费者去取数据时,也用到了这种方式。

图片

不管是不断轮询还是长轮询,本质上还是客户端主动取数据,对于扫码这种场景还能使用,但是在网页游戏中,游戏一般会有大量的数据需要从服务器主动推送到客户端,这里就需要websocket。

websocket

我们知道 TCP 可以进行全双工通信。 而现在使用最广泛的HTTP/1.1,也是基于TCP协议的,却只支持半双工。 也就是说,好好的全双工 TCP,被 HTTP/1.1 用成了半双工。 为什么? 这是由于 HTTP 协议设计之初,考虑的是看看网页文本的场景,能做到客户端发起请求再由服务器响应,就够了,根本就没考虑网页游戏这种,客户端和服务器之间都要互相主动发大量数据的场景。

所以,为了更好的支持这样的场景,我们需要另外一个基于TCP的新协议。 于是新的应用层协议WebSocket就被设计出来了。

建立websocket连接:使用http进行一次通信,带上特殊的header头 Upgrade: WebSocket,就会升级成websocket协议,websocket升级后与http就没有关系了,它是基于tcp的协议

使用场景:完全继承了TCP协议的全双工能力,适用于服务器与客户端频繁交互的大部分场景,比如网页/小程序游戏,网页聊天室等

作者

ShiHaonan

发布于

2025-03-03

更新于

2025-08-21

许可协议

评论