HTTP 的前世今生

HTTP 协议 是浏览器和服务器之间的 通信语言
HTTP1 协议 HTTP/0.9: 图灵完备,但功能单一
  • 需求:主要用于学术交流,用来在网络之间 传递 HTML 超文本 的内容
  • 特点:没有 HTTP 请求头和请求体,服务器也没有返回头信息
HTTP/1.0:面向大众,解决刚需
  • 需求:支持多种类型的文件下载
    • 通过请求头和响应头来进行协商文件的类型、压缩方式、编码等
  • 特点:
    • 新增 状态码:过响应行的方式来通知浏览器的
    • 新增 Cache:缓存已经下载过的数据
    • 新增 用户代理:收集客户端基础信息
  • 缺点:
    • 每一次 HTTP 通信都要经历:TCP连接 → 传输 HTTP 数据 → 断开 TCP
    • 相当于每次和别人说一句话都要 say hello 一次,非常的搞笑
HTTP/1.1: 缝缝补补
  • 核心优化项:
    • 持久连接
      • 只要服务器或浏览器没有明确断开连接,TCP 连接会一直保持
      • 默认开启,同一个域名最多同时建立 6 个 TCP 连接
    • 使用 CDN 实现 域名分片 机制
      • 将一个页面的资源利用多个域名下载,提高 TCP 并发数量
    • 不成熟的 HTTP 管线化
      • 持久连接虽能减少 TCP 重连次数,但 TCP 的连接还是串行的,如果有请求没有及时返回就会阻塞后面的请求,著名的 队头阻塞 问题
      • 管线化是要解决队头阻塞的问题,但该方案由于种种原因被放弃
    • 提供 虚拟机 的支持
      • HTTP/1.0 中,每个域名绑定唯一 IP 地址,一个 IP 地址只能对应一个物理机,但随着虚拟机技术的发展,一个物理机可以有多个虚拟机,每个虚拟机都有自己的单独域名,但域名的 IP 是一样的
      • 因此,新增了 Host 字段,来表示当前域名,服务器就可以根据 Host 做不同的处理
    • 支持 动态生成内容
      • HTTP/1.0 中,用 Content-Length 来确定数据的大小,但是随着服务端的发展,很多页面内容都是 动态生成 的,所以在传输之前不知道最终的数据大小,导致浏览器 无法确定 数据是否已全部接收
      • Chunk transfer 机制:服务器会将数据分割成若干 任意大小 数据块且附加其长度,最后使用 零长度 的数据块作为发送完成标志
    • 客户端 Cookie
      • 解决 HTTP 无状态的限制,用于 标识 用户身份和信息
  • 依然存在的问题:
    • 带宽的利用率却并不理想
      • 带宽:每秒最大能发送或接收的字节数
        • 上行带宽:每秒能 发送 的最大字节数
        • 下行带宽:每秒能 接收 的最大字节数
      • 原因:
        1. TCP 的 慢启动:
          • TCP 建立连接之后就进入发送数据状态,发送数据的速度 由慢到快,慢启动是 TCP 为了减少 网络拥塞 的一种缓冲策略
          • 页面中常用的一些关键资源文件本来就不大,如 HTML/CSS/JS 文件,通常这些文件在 TCP 建立连接后就要发起请求的,但由于是慢启动,耗时比正常时间要多很多,延迟了首次渲染页面的时长
        2. 资源竞争:
          • 多条 TCP 连接会竞争固定带宽时无法区分重要资源的 优先级
          • 当页面中下载的资源较多且带宽不够时,TCP 连接就会动态减慢接收数据的速度,而这个减慢没有优先级,也就会造成视频和图片这种较大的资源会和 HTML /CSS 这种渲染初始页面的 资源竞争 而造成白屏
        3. 队头堵塞:
          • 每个 TCP 管道中同一时刻只能处理一个请求,请求未结束时,其他请求只能等待,类似于在同一个窗口排队办业务
HTTP2 协议
解决 HTTP1.1 中 TCP 的慢启动、带宽资源竞争、队头拥塞问题,虽然 TCP 是造成这些问题的源头,但是目前依然无法脱离 TCP 协议,只能想办法规避上述问题。
HTTP2 核心特性
  • 如何解决队头堵塞?
    • 多路复用机制
      • 浏览器每个发起的请求都会携带一个 **ID**,当服务器返回数据时也会携带对应的 ID,浏览器会将返回的 ID 的内容拼接为完整的 HTTP 响应数据
        • 就好比去一个可以自助点餐的饭店,点完之后就等着上菜,解决了排队点菜造成的对头堵塞的问题
      • 可将请求分成一帧一帧的数据进行传输,当收到优先级高的请求时,服务器可以暂停之前的请求来 处理优先级较高 的资源请求
      • 实现机制:
        HTTP2 新增了 二进制分帧层
        1. 浏览器准备好 请求数据
        2. 二进制分帧层将请求数据 转换 为一个个带有请求 ID 编号的帧,通过协议栈将其发送给服务器
        3. 服务器接收所有帧,会将所有 相同 ID 的帧 合并为一条完整的请求信息
        4. 服务器处理该请求,并将处理的响应行、响应头、响应体 分别发送 至二进制分帧层
        5. 二进制分帧层会将响应数据转换为一个个带有请求 ID 编号的帧,经过协议栈发送给浏览器
        6. 浏览器接收到响应帧后,会根据 ID 编号将帧的数据提交给对应的请求
        flowchart BT subgraph HTTP2 协议栈 subgraph 请求列表 ... end subgraph 二进制分帧层 subgraph 完整请求 请求头+ID 响应头+ID 响应体+ID end end 请求列表 <-->二进制分帧层 <--> TLS[TLS 可选] <-->TCP/IP end

  • 如何解决 TCP 慢启动次数和 TCP 之间宽带资源竞争?
    • 一个域名只使用一个 TCP 长连接 来传输数据,减少每次 TCP 连接造成的 副作用
HTTP2 其他特性
  1. 可设置请求的优先级:优先加载重要资源
  2. 服务器推送
    1. 当刚请求 HTML 后,直接将 HTML 所需的 CSS/JS 也一块发送给浏览器,将于拿来这对首次打开页面的速度起来了 至关重要 的作用。
  3. 头部压缩
依然存在的优化
  • TCP 传输层 的队头阻塞 :
    • 虽然 HTTP2 解决了 应用层面到传输层 的队头阻塞问题,但是 传输层到网络层 的丢包重传机制还是可以堵塞 TCP 管道,而 TCP 最初就是为了单连接而设计的
    • 在丢包严重的情况下 HTTP2 比 HTTP1.1 表现还差,因为后者有 6 个 TCP 管道
  • TCP 建立连接的延时:
    • 在传输数据之前,需要花费 TCP + TLS 握手所消耗的 3~4 RTT
  • TCP 协议僵化:
    • 因 TCP 发展到现在的历史包袱已经太重,而又属于计算机相对的底层和核心的位置,全设备同步是不可能的
HTTP3
甩掉 TCP、TLS 的包袱,构建高效网络
QUIC 协议
在考虑兼容中间设备的僵化,重新构建一个新的 传输层协议 并被很好兼容,已无可能,只能在现有基础上做拓展和优化。
【HTTP 的前世今生】因为 HTTP3 选择折中的方案 — UDP 协议,基于 UDP 实现了类似于 TCP 的功能,这套协议称作为 QUIC 协议。
HTTP 的前世今生
文章图片

HTTP/2 和 HTTP/3 协议栈
HTTP3 的挑战
  1. 兼容性:服务器和浏览器都没有对 HTTP3 提供比较完整的支持
  2. UDP 的市场成熟度:系统内核对 UDP 的优化远远没有达到 TCP 的优化程度
  3. 中间设备僵化:其对 UDP 的优化程度低于 TCP,使用 QUIC协议时, 3%~7% 的丢包率
总结:
核心诉求 优点 不足
HTTP 0.9 能传输 HTML 文件,用于学术交流 简单、图灵完备 功能单一
HTTP 1.0 能下载多种文件类型 完成时代迫切下载需求 TCP 重复握手,效率低下
HTTP 1.1 TCP 持久话复用 优化 1.0 各种需求 管道阻塞
HTTP 2.0 实现管道内多路复用 大幅提升 1.1 的传输效率 TCP 协议导致的各种问题
HTTP 3.0-未来 解决 TCP 的短板 更快速 兼容性差
从 HTTP 0.9 到未来的 HTTP3.0,协议的变化也是随着时代和市场的需求而变化,每个大版本的变化都是为了解决一个核心问题,而每一次的更改也都需要慎重,当不可逆的行为发生时,会形成沉重的历史包袱,甚至会延迟社会的进步,直到被忍无可忍时将其推翻,到时为此买单的可能将是整个世界。

    推荐阅读