初识 HTTP 2.0 (一)

2023年 9月 25日 75.2k 0

大家好,在我们的软件开发过程中,相信大家对于 http 协议是再熟悉不过了。比如:当你在阅读这篇文章时,其实就是通过 http 协议获取的文章内容。但是,你有没有意识到,类似稀土掘金等常用网站,其实都已经使用了 http 协议 的 2.0 版本来提高访问速度。来,打开你的浏览器,按下 F12,查看一下协议版本。

Untitled Diagram.drawio.png

相较于 1.1 版本, 2.0 版本,做了哪些改变呢?今天我们就来一探究竟。老规矩,先看理论,再来实践。

理论

我们要知道,http 的 2.0 版本,首次发布是在 2015 年的 5 月份,距离其 1.1 版本(1997 年 7 月)过了 18 年之久。那官方是如何定义 http 2.0 的呢?打开 RFC Editor,在第二章 Http/2 Protocol Overview 中,对重要特性作了总结性的说明。

Untitled Diagram.drawio.png

从内容中,我们可以得知,2.0 版本的主要特性包括:

  • 传输的最小单元从 http 1.1 版本的文本格式,变成了帧,不同的帧,完成不同的事情
  • 多路复用,通过流式传输,减少多个 http 请求之间的阻塞
  • 流量控制和优先级控制
  • 数据压缩
  • 服务端推送

那这些新特性具体长的什么样呢?接下来,我们就通过 Chrome 浏览器的开发者工具和 wireshark 抓包工具,一起来揭开它神秘的面纱。

实践

首先,我们准备一个支持 http 2.0 协议的服务,因为 http 2.0 是建立在 TLS 协议基础之上的,所以,我们还要准备一个证书,这里项目的版本及证书如下:

  • jdk: 17
  • spring boot: 3.1.0
  • tls: 通过 keytool 生成的自签名证书
  • 对于 spring boot 项目来说,启用对 http 2.0 协议的支持还是很简单的,只需要打开配置即可:
    image.png

  • 接下来,我们准备一个简单的 html 页面,里面包含几个 css 文件,js 文件和图片。当我们使用浏览器访问页面的时候,浏览器首先会向服务端请求 index.html 页面,然后解析到页面依赖于其它文件,就会再次向服务器发起请求,下载 css 文件,js 文件,图片等信息。
    image.png

  • 项目准备好以后,我们将打好的 jar 包拷贝到虚拟机上,然后启动服务,从日志中我们可以看到,服务已经可以支持 http 2.0 版本了
    Untitled Diagram.drawio.png

  • 我们修改 yml 文件,将端口号改为 9999,并关掉对 http2 的支持,重新打包,并启动。这样我们就可以方便的去对比,浏览器和服务端面对不同版本的协议时,都是如何处理的。
    image.png
    Untitled Diagram.drawio.png

  • 执行 tcpdump 命令,分别监听 8888 端口和 9999 端口,并将监听到的内容写入文件。
    Untitled Diagram.drawio.png

  • 打开 Chrome 浏览器,打开开发者工具的 Network 面板,分别访问 8888 端口和 9999 端口的 index.html 页面。
    8888.png
    9999.png

  • 从执行结果里,我们可以观察到:

  • 在协议一栏,已经可以明显看出当前浏览器和服务器通讯时,采用的 http 协议版本。
  • 对于 8888 端口的请求,浏览器和服务器只在第一次请求 html 页面时,建立了一次连接。而当浏览器访问 9999 端口时,除了访问 html 页面建立了一次连接以外,还在请求下载 9113.png 图片时,又和服务器建立了一次连接。
    Untitled Diagram.drawio.png
    这里其实是 Chrome 浏览器的一个优化机制:当浏览器和一个域名建立连接时,会自动和服务器建立多个连接,这样,每个连接执行多个 http 请求,从而达到并发的能力,以此来优化页面的打开速度。而浏览器之所以要做这样的事情,就是因为 http 协议在 1.1 版本中,存在一个队头阻塞问题(Head-of-line (HoL) blocking)。
    就是说,当第一个 http 请求发送后,后续其它的 http 请求都要等前一个请求结束,才能发送下一个请求。我们假设一个页面包含100张图片,如果第一个下载图片的请求阻塞住了,那后续99个下载图片的请求都要排队,可想而知,我要多久以后才能看到这个页面?因此,浏览器会自动建立多个连接,同时下载多个图片。但是建立过多的连接对服务端也会造成压力,所以一般来说,浏览器会和同一个域名建立至多6个连接,以达到保证速度和节省资源的平衡。
  • 接下来,我们再来看看 wireshark 的抓包结果。首先是对 9999 端口抓包的结果,过滤 http 协议:

    Untitled Diagram.drawio.png
    Untitled Diagram.drawio.png
    Untitled Diagram2.drawio.png

    查看 37 号数据包和 128 号数据包,可以看到两次 http 请求分别在不同的 tcp 连接上发送的,这也和我们在浏览器的开发者工具看到的结果是一致的,接着我们以 2476 端口为例,我们可以看到,后一个请求都是要等到前一个请求结束以后,才会发送出去。点开任一请求或者响应记录,可以看到传输的格式为文本格式。

    Untitled Diagram.drawio.png

    Untitled Diagram.drawio.png

    查看 8888 端口的抓包结果,同样,先过滤 http2 协议:

    Untitled Diagram.drawio.png

  • 查看每一个数据包后,可以看到,在浏览器基于 http 2.0 协议进行通讯时,只和服务端建立了一次连接

  • 浏览器向服务端请求第一个 html 页面时,发送了一个带有 HEADERS【1】标记的数据包,而服务端在向浏览器返回数据时,发送了两个带有 DATA【1】标记的数据包,从而完成了一次 http 请求。这个,就是 http 2.0 的最小传输单元帧,关于帧的具体内容,在下篇文章中,我们可以具体分析,这里先有一个印象。
    Untitled Diagram.drawio.png

  • 当请求页面信息结束以后,我们可以看到第 38, 39, 41 号数据包,分别去请求了 css 样式文件和图片,但是他们并没有等待其它请求结束再发送请求,而是并发的将请求发送到服务端。而后面服务端在返回数据时,也没有按照顺序一次性的将所有数据返回,而是分成了不同的帧,乱序的返回。这就是上面说的多路复用的特性。

  • 我们可以看到,同样是请求 index.html 页面,http 2.0 版本的 header 长度只有 564,而在 http 1.1 版本,长度达到了 813,这也就是数据压缩的特性

  • 总结

    经过上面的操作,我们对 http 2.0 协议有了一个初步认识,在后续的文章中,我们会对其重要特性,再做更多的学习和介绍。

    (感谢阅读,如有不正确之处,欢迎指教)

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论