Skip to content

HTTP 的一些知识点梳理

网络有大把 http2、http3 的各种文章和性能测试,这里就不再重复了,这里主要收集和梳理了下实践中比较容易混淆,不容易理解的一些问题

HTTP 和 TCP 的关系

HTTP 是应用层协议,TCP 是传输层协议,HTTP 依赖于 TCP 来传输数据,浏览器因此有不一样的表现

  • HTTP/1.0 使用短连接,HTTP 请求和 TCP 一一对应,即一个 TCP 连接只能传输一个 HTTP 请求和响应,传输完毕后就关闭连接,下次请求需要重新建立连接
  • HTTP/1.1 默认使用长连接,一个 TCP 连接可以传输多个 HTTP 请求和响应。但是 TCP 连接的复用是串行的,一个请求的响应没有返回时,后续请求只能等待。这样就导致了队头阻塞问题,一个请求的响应时间过长,会影响后续请求的响应时间。为了同时发送多个请求,浏览器会为每个域名创建多个 TCP 连接(一般为 6 个),这就是 HTTP 有并发限制的原因。
  • HTTP/2 进一步优化,支持在单个 TCP 连接上进行多路复用,进行 HTTP 并发请求,浏览器只需要为每个域名创建一个 TCP 连接。这大大减少了需要创建的 TCP 连接数量,从而减少了 TCP 连接的建立和关闭的开销。虽然 HTTP/2 协议本身没有并发请求的限制,但是浏览器和服务器可能会设置自己的限制,实际上,浏览器和服务器可能会设置自己的并发流的限制,以防止资源过度使用。例如,Chrome 浏览器默认限制每个 TCP 连接上的并发流数量为 100。这个限制可以通过浏览器的设置进行调整。
  • HTTP/3 使用了 QUIC 协议,基于 UDP,减少握手时间,解决 TCP 队头阻塞问题,如果使用 0-RTT ,将进一步提高了传输效率

队头阻塞

队头阻塞是指一个请求的响应时间过长,会影响后续请求的响应时间。

  • HTTP/1.1 默认使用长连接,一个 TCP 连接可以传输多个 HTTP 请求和响应。但请求是串行的,一个请求的响应没有返回时,后续请求只能等待。这样就导致了队头阻塞问题
  • HTTP/2 通过 Stream 设计,多个 Stream 复用一条 TCP 连接,达到并发的效果,解决了 HTTP/1.1 队头阻塞的问题,提高了 HTTP 传输的吞吐量。
  • HTTP/3 使用了 QUIC 协议,基于 UDP,减少握手时间,解决 TCP 队头阻塞问题,如果使用 0-RTT ,将进一步提高了传输效率

HTTP 和 RPC

  • 纯裸 TCP 是能收发数据,但它是个无边界的数据流,上层需要定义消息格式用于定义消息边界。于是就有了各种协议,HTTP 和各类 RPC 协议就是在 TCP 之上定义的应用层协议。
  • RPC 本质上不算是协议,而是一种调用方式,而像 gRPC 和 Thrift 这样的具体实现,才是协议,它们是实现了 RPC 调用的协议
  • RPC 比 HTTP 出现的要早,且比目前主流的 HTTP/1.1 性能要更好,所以大部分公司内部都还在使用 RPC。现在 HTTP/2.0 在 HTTP/1.1 的基础上做了优化,性能可能比很多 RPC 协议都要好
  • 主流的 HTTP/1.1 协议,其默认在建立底层 TCP 连接之后会一直保持这个连接(Keep Alive),之后的请求和响应都会复用这条连接。而 RPC 协议,也是通过建立 TCP 长链接进行数据交互,但不同的地方在于,RPC 协议一般还会再建个连接池,在请求量大的时候,建立多条连接放在池内,要发数据的时候就从池里取一条连接出来,用完放回去,下次再复用,

HTTP 和 WebSocket

  • TCP 协议本身是全双工的,但我们最常用的 HTTP/1.1,虽然是基于 TCP 的协议,但它是半双工的,对于大部分需要服务器主动推送数据到客户端的场景,都不太友好,因此我们需要使用支持全双工的 WebSocket 协议。
  • 在 HTTP/1.1 里,只要客户端不问,服务端就不答。基于这样的特点,对于登录页面这样的简单场景,可以使用定时轮询或者长轮询的方式实现服务器推送(comet)的效果。
  • 对于客户端和服务端之间需要频繁交互的复杂场景,比如网页游戏,都可以考虑使用 WebSocket 协议。
  • WebSocket 和 socket 几乎没有任何关系,只是叫法相似。
  • 正因为各个浏览器都支持 HTTP 协 议,所以 WebSocket 会先利用 HTTP 协议加上一些特殊的 header 头进行握手升级操作,升级成功后就跟 HTTP 没有任何关系了,之后就用 WebSocket 的数据格式进行收发数据。

HTTPS 各个版本握手的区别

  • HTTP 标准的 tcp 连接,三次握手,四次挥手,然后发送明文数据
    • 三次握手的主要原因是为了防止旧的重复连接初始化造成混乱
  • TLS1.2 RSA
    • 有 4 次握手,2 个 RTT
    • 第二次握手是,服务端返回证书公钥,客户端使用服务端的证书公钥加密 pre-master,服务端使用自己的私钥解密得到 pre-master,然后双方使用 Client Random、Server Random、pre-master 生成 Master Secret(会话密钥),用于对后续的 HTTP 请求/响应的数据加解密(对称加密)
    • RSA 密钥协商算法的最大问题是不支持前向保密,如果服务端的私钥泄漏,那么之前的所有通信都会被破解
  • TLS1.2 ECDHE
    • 可以只有 3 次握手,客户端可以不用等服务端的最后一次 TLS 握手,就可以提前发出加密的 HTTP 数据,TLS 握手的消息往返由 2 RTT 减少到 1 RTT
    • 第二次握手时,服务端返回的证书只做验证作用,不会用于加密
    • 第 2、3 次握手,客户端和服务端交换 ECDHE 算法公钥(椭圆曲线公钥),然后用 Client Random、Server Random、ECDHE 算法算出的共享密钥生成会话密钥
  • TLS1.3
    • TLS 1.3 把 Hello 和公钥交换这两个消息合并成了一个消息,这样就减少到只需 1 RTT 就能完成 TLS 握手
  • QUIC
    • TTP/1 和 HTTP/2 协议,TCP 和 TLS 是分层的,先 TCP 握手,再 TLS 握手。QUIC 内部包含了 TLS,它在自己的帧会携带 TLS 里的“记录”,再加上 QUIC 使用的是 TLS 1.3,因此仅需 1 个 RTT 就可以「同时」完成建立连接与密钥协商,甚至在第二次连接的时候,应用数据包可以和 QUIC 握手信息(连接信息 + TLS 信息)一起发送,达到 0-RTT 的效果。

时间对比:

  • HTTP TCP 握手 = 1 RTT
  • TLS1.2 RSA TCP + TLS 4 次握手 = 3 RTT
  • TLS1.2 ECDHE TCP + TLS 3 次握手 = 2 RTT
  • TLS1.3 TCP + TLS 2 次握手 = 2 RTT
  • QUIC UDP + TLS = 1 RTT,最优 0-RTT

Released under the MIT License.