透视 HTTP 协议
罗剑锋(Chrono)
前奇虎 360 技术专家,Nginx/OpenResty 开源项目贡献者
63943 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 48 讲
开篇词 (1讲)
透视 HTTP 协议
15
15
1.0x
00:00/00:00
登录|注册

31 | 时代之风(下):HTTP/2内核剖析

HTTP/2解决队头阻塞问题
帧大小的选择
HTTP/2的无状态性
流与多路复用
二进制帧
头部压缩
连接前言
HTTP/2的内容
流状态转换图
流ID的生命周期
关闭状态
半关闭状态
打开状态
空闲状态
多路复用
流状态转换
双向传输
并发多请求
流的特点
流ID
流的概念
帧实例分析
流标识符
帧标志信息
帧类型
帧结构
报文拆分成二进制帧
压缩效果比gzip更好
静态表和动态表
伪头字段
使用HPACK算法压缩头部数据
服务器确认建立HTTP/2连接
连接前言字符串
TCP握手和TLS握手
HTTP/2基于TLS
课下作业
小结
流状态转换
流与多路复用
二进制帧
头部压缩
连接前言
HTTP/2内核剖析

该思维导图由 AI 生成,仅供参考

今天我们继续上一讲的话题,深入 HTTP/2 协议的内部,看看它的实现细节。
这次实验环境的 URI 是“/31-1”,我用 Wireshark 把请求响应的过程抓包存了下来,文件放在 GitHub 的“wireshark”目录。今天我们就对照着抓包来实地讲解 HTTP/2 的头部压缩、二进制帧等特性。

连接前言

由于 HTTP/2“事实上”是基于 TLS,所以在正式收发数据之前,会有 TCP 握手和 TLS 握手,这两个步骤相信你一定已经很熟悉了,所以这里就略过去不再细说。
TLS 握手成功之后,客户端必须要发送一个“连接前言”(connection preface),用来确认建立 HTTP/2 连接。
这个“连接前言”是标准的 HTTP/1 请求报文,使用纯文本的 ASCII 码格式,请求方法是特别注册的一个关键字“PRI”,全文只有 24 个字节:
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
在 Wireshark 里,HTTP/2 的“连接前言”被称为“Magic”,意思就是“不可知的魔法”。
所以,就不要问“为什么会是这样”了,只要服务器收到这个“有魔力的字符串”,就知道客户端在 TLS 上想要的是 HTTP/2 协议,而不是其他别的协议,后面就会都使用 HTTP/2 的数据格式。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

HTTP/2协议的内部实现细节是本文的重点。文章首先介绍了HTTP/2连接的前言,即“连接前言”(connection preface),以及其在Wireshark中的展示。接着,文章详细解释了HTTP/2中的头部压缩机制,介绍了HPACK算法的特点和优势,以及静态表和动态表的运作原理。另外,文章还介绍了HTTP/2中的二进制帧结构,包括帧的长度、类型、标志位和流标识符等重要信息。通过对Wireshark抓包的帧实例进行解析,读者可以更直观地了解HTTP/2中的帧结构。整体来看,本文通过深入解析HTTP/2协议的内部实现细节,为读者呈现了一个清晰的技术画面,使读者能够快速了解HTTP/2协议的特点和优势。 HTTP/2协议的核心部分是流与多路复用。在HTTP/2连接上,流是二进制帧的双向传输序列,并且可以并发多个流传输数据,实现了“多路复用”。流具有诸多特点,包括可并发、双向、独立、可设置优先级等。文章还介绍了流的状态转换,以及HTTP/2的流生命周期和流ID的使用规则。通过对流的特性和状态转换的解释,读者可以更好地理解HTTP/2协议中流的运行机制。 总的来说,本文深入解析了HTTP/2协议的内部实现细节,重点介绍了连接前言、头部压缩机制和二进制帧结构,以及流与多路复用的核心部分。通过本文的阅读,读者可以快速了解HTTP/2协议的特点和优势,以及流的运行机制和状态转换。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《透视 HTTP 协议》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(54)

  • 最新
  • 精选
  • -W.LI-
    1.还是无状态,流状态只是表示流是否建立,单次请求响应的状态。并非会话级的状态保持 2.小帧好,少量多次,万一拥堵重复的的少。假设大帧好,只要分流不用分帧了。 3.每一个请求响应都是一个流,流和流之间可以并行,流内的帧还是有序串行。

    作者回复: good。

    2019-08-07
    2
    45
  • djfhchdh
    3、首先要明确造成“队头阻塞”的原因,因为http1里的请求和应答是没有序号标识的,导致了无法将乱序的请求和应答关联起来,也就是必须等待起始请求的应答先返回,则后续请求的应答都会延迟,这就是“队头阻塞”,而http2采用了虚拟的“流”,每次的请求应答都会分配同一个流id,而同一个流id里的帧又都是有序的,这样根据流id就可以标识出同一次的请求应答,不用再等待起始请求的应答先返回了,解决了“队头阻塞”

    作者回复: http/1里的请求都是排队处理的,所以有队头阻塞。 http/2的请求是乱序的,彼此不依赖,所以没有队头阻塞。

    2019-08-07
    19
  • 好好好
    老师,我还是无法理解HTTP1.X无法实现多路复用的具体原因。如果我在HTTP1.X版本的一个TCP连接下同时发送多个请求,会发生什么情况呢?造成这个情况的具体原因又是什么呢?

    作者回复: 1.http协议要求请求-响应必须一来一回,上一个请求没有处理完,下一个请求是不能发出去的。一个tcp连接上的http请求必然是串行。 2.管道模式可以顺序发出多个请求,但响应也必须顺序响应。这些都是http/1.1里规定的。 3.再对比http/2,一个tcp连接里有多个流,每个流就是一个请求,所以多个请求可以并发,“复用”在了一个连接里。

    2020-06-30
    4
    13
  • 何用
    服务端是不是要为每一个客户端都单独维护一份索引表?连接的客户端多了的话内存不就OOM了嘛

    作者回复: 是的,不过动态表也有淘汰机制,服务器可以自己定制策略,不会过度占用内存。

    2019-08-07
    13
  • 何用
    HTTP/2 底层还是依赖 TCP 传输,没有解决队头阻塞的问题啊,这就是为何 HTTP/3 要基于 UDP 来传输

    作者回复: 对,虽然是部分解决,但对于http/1来说已经是一个很大的进步了。

    2019-08-07
    3
    12
  • 想个昵称好难
    还有一个问题想请教下老师,您之前在《HTTP的前世今生》上有一段回复是说,只要是HTTP/1.1,就都是文本格式,虽然里面的数据可能是二进制,但分隔符还是文本,这些都会 在“进阶篇”里讲, 不过我看到现在还是有点迷惑,所二进制协议和文本协议的区别是什么呢?可以按照stackoverflow中https://stackoverflow.com/questions/2645009/binary-protocols-v-text-protocols 的回答来理解吗?

    作者回复: 指的是协议本身的数据格式,而不是负载(payload)的格式。 你看http/1,请求行、头、body里的分隔符,都是ASCII码。 而http/2,是二进制帧,用字节、位来表示信息,没有ASCII码。 你可以把自己想象成协议的解析器,你看到的协议头是什么格式,文本还是二进制。

    2019-08-07
    9
  • 想个昵称好难
    老师您好,打扰您实在是抱歉,想请教您一个问题,您在文中说HTTP/2会在两端维护“Key-Value”的索引表,静态表应该是一摸一样的,那动态表俩边一样吗?如果一样的话,同步是比较难做的事情吧,我看RFC文档中是这么写的,”When used for bidirectional communication, such as in HTTP, the encoding and decoding dynamic tables maintained by an endpoint are completely independent, i.e., the request and response dynamic tables are separate.“, 所以我的理解是,动态表在客户端和服务器各自都有俩个表,一个是用来保存客户端发送的message的header,另外一个是保存服务器发送的header, 我看stackoverflow中也是这么写的,https://stackoverflow.com/questions/53003333/how-does-headers-keep-sync-in-both-client-and-server-side-in-http-2, 如果我有哪个地方理解错了,麻烦下老师指点一下

    作者回复: 是的,客户端和服务器维护各自的动态表,收发各一张表,但字典里的内容必须是一致的,否则索引号就对不上了。

    2019-08-07
    2
    9
  • Luke
    1、一个流中的多个帧是有序的,但是在二进制帧协议中,并没有看到这个序号,请问下老师,这个序号是在哪里?或者一个流中的多个帧是如何保证有序的? 2、如果出现丢帧的情况是如何重传的?

    作者回复: 1.由tcp保证帧是顺序到达的,因为tcp是有序的字节流。 2.同样是由tcp实现,具体细节就太底层了,有兴趣可以研究tcp协议。

    2019-09-02
    2
    7
  • djfhchdh
    1、http的“无状态”是指对事务处理没有记忆,每个请求之间都是独立的,这与HPACK算法里的动态表、流状态转换是两回事。HPACK算法里维护动态表是用于头部压缩,而流状态转换只是表示一次请求应答里流的状态,都不会记录之前事务的信息

    作者回复: 说的很好,也可以这么表述“语法上有状态,语义上无状态”。

    2019-08-07
    7
  • 许童童
    HTTP/2 的动态表维护、流状态转换很复杂,你认为 HTTP/2 还是“无状态”的吗? 还是无状态的,对上层应用来说,动态表维护、流状态转换这些操作对它不可见。 HTTP/2 的帧最大可以达到 16M,你觉得大帧好还是小帧好? 大帧好,应该小帧需要很多额外的头信息,有数据冗余。小帧可以当出差错时,只转输出错的帧,细粒度控制。 结合这两讲,谈谈 HTTP/2 是如何解决“队头阻塞”问题的。 因为流可以并发,一个流被阻塞了,并不影响其它的流。

    作者回复: 我个人认为小帧比较好,当然如果在某些特定场景里,比如下载大文件,可以适当加大。

    2019-08-07
    4
    6
收起评论
显示
设置
留言
54
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部