目录
http协议1.1和2的区别
介绍
1.x的设计基于早期的互联网
- 早期互联网带宽有限+信道质量差(需要让tcp协议保证数据传输的正确性)
- 而http/2 -- 管道化,它的优化目标就是让每一个tcp连接可以被多个http会话复用 -- 减少服务端需要轮询的句柄数量,减轻服务端压力
数据传输格式
HTTP/1.1
使用纯文本格式传输数据
头部信息以文本形式表示,数据部分可以是文本/二进制
HTTP/2:
采用二进制分帧层
- 将所有传输的信息分割为更小的帧,并以二进制格式编码
- 这些帧按序列号发送和接收,并在接收端重新组装成完整的消息
- 这种方式提高了数据传输的灵活性和效率
文本协议和二进制协议的区别
二者间区别在于,协议是以文本字符串为导向,还是以数据结构为导向
文本协议请求
- 内容由文字构成
二进制请求
- 排列会比文本协议更加紧凑
- 不需要添加特殊字符来辅助解析一段文字
- 数字占用空间更小:比如http中状态码200,在文本协议中, "2" "0" "0"这三个数字需要分别用编码表示 ; 但是在二进制协议中,直接用二进制11001000表示数字
二进制分帧层
引入
- http/2中的二进制分帧层给一个tcp链接同时发送多个请求提供了可能
- 二进制分帧层可以解决 -- 当一次发送多个请求/响应时,如何找到对应的请求和响应? 的问题
- 为什么要解决这个问题?
- 因为请求和响应本身拥有的字段无法一一对应
那在HTTP/1中,请求和响应为什么可以对应起来?
- 因为在HTTP/1中,一个tcp请求只能同时完成一个请求响应过程
- 所以请求以及响应天生一一对应,不存在响应和请求无法匹配的问题
介绍
首部信息被封装成Headers帧,主体部分被封装成Data帧
- 可以对头部分帧进行二进制压缩,HPACK 头部压缩的优势更加明显(后面会介绍)
- 如果还是之前的文本格式,则无法进行头部压缩
这样,应用层消息的最小单位不再是消息文本了,而是应用层帧
- 即在应用层就切好数据(变成多个帧),发送给tcp
那么,如何处理帧之间的对应关系呢?
在HTTP/2中,每个请求和响应都分配一个独特的STREAM ID(唯一标识符)
- 这种机制允许HTTP/2协议通过二进制分帧格式,将请求和响应拆分成独立的帧,每个帧都包含一个STREAM ID,用于在传输过程中识别和关联相应的请求和响应
- 通过使用STREAM ID,可以让不同请求和响应在同一TCP连接中并行处理,而不必按顺序接收或发送(解除了不同请求响应之间相对顺序的限制),提高了数据传输的效率和灵活性
所以,在发送前:
- 会标记好这些帧是属于哪一个http请求的第几节的流,然后再给tcp送出去
- 这样即使多个http请求并发发送,应用层自己也可以基于这些标记来把应用层的帧数据拼接成原始消息
并发机制
HTTP/1.1:
每个请求需要单独的 TCP 连接,或者通过流水线化技术在同一连接上发送多个请求
- 但可能导致队头阻塞问题,所以并未启用
HTTP/2:
引入“流”的概念,实现了多路复用技术
多路复用技术
必要性
简单来说,假设你访问某个网站需要请求 10 个资源
如果使用 HTTP1.1 协议:
- 只能串行地发请求,资源 1 请求成功之后才能发送资源 2 的请求,以此类推 -- 这个过程是非常耗时的
- 如果想 10 个请求并发,不需要串行等待的话,在 HTTP1.1 中,应用就需要为一个域名同时建立 10 个 TCP 连接才行(一般浏览器不允许建立这么多)-- 这无疑是对资源的极大的浪费
HTTP/2 的多路复用解决了这一问题,能使多条请求并发
作用
http2的本质是 -- 可以乱序响应
- 不再是谁先发请求就先响应,这样就避免了无效等待
在单个 TCP 连接上并行发送多个数据流时
- 每个数据流中包含多个消息包,每个消息被切分为多个数据帧(也就是前面介绍的,将请求/响应切分为帧)
- 这些数据帧可以在传输期间交错发送,然后在接收端重新组装
- 这样避免了HTTP/1.1 应用层的队头阻塞问题 (不会出现 某个慢请求阻塞其他请求 的情况),从而提高传输效率
流的优先级的问题
流的优先级表示了这个请求被处理的优先级
- 比如客户端请求的关键的 CSS 和 JS 资源是必须高优先级返回的,图片视频等资源可以晚一点响应
优先级设置是一个难以平衡或者难以做到公平合理的事情
- 如果设置稍微不恰当,就会导致有些请求很慢 (在用户看来就是用了 HTTP/2 后,有的请求却变慢了)
丢包问题
所有的流都在一条 TCP 连接上,增大了丢包概率
- 在多条流并发的时候,某条流的某个包丢了,序号在该包后面的其他流的数据都不能被应用程序读取
如果换做 HTTP1.1
- 由于 HTTP1.1 是多条连接,某个连接上的请求丢包了,并不影响其他连接
所以在丢包比较严重的情况下,HTTP/2 整体效果大概率不如 HTTP1.1
和IO模型中多路复用的关系
虽然 HTTP/2 的多路复用和
epoll
属于不同层次的技术,但在实际应用中,epoll
等 I/O 多路复用机制为 HTTP/2 的高效运行提供了底层支持
- 这个支持主要是针对 高并发场景 下的 服务器端多个 TCP 连接管理,而不是 HTTP/2 连接内部的流复用
- HTTP/2 只是在一个 TCP 连接内部实现了多路复用,但服务器仍然要管理 多个这样的 TCP 连接,这就需要 epoll
头部压缩
HTTP/1.1:
头部信息以纯文本形式传输,未进行压缩,可能导致冗余数据增加网络开销
- 相同的头部字段(如
User-Agent
、Cookie
)会在每次请求中重复发送,造成带宽浪费和网络开销增加
HTTP/2:
采用 HPACK 算法对头部字段进行压缩,从而减小了每个请求和响应的头部大小
通过将头部字段编码为二进制格式,并利用静态表和动态表来减少重复传输,从而提高传输效率
使用较小的索引号表示重复的字符串,并使用霍夫曼编码压缩数据,减少了传输的数据量,提高了传输效率
HPACK 算法
这种算法通过服务端和客户端各自维护索引表来实现
- 索引表又分为静态表和动态表
静态表
静态表中定义了61个Index和Header字段(字段名+值),存储常见的 HTTP 头部字段
- 可以通过传输Index进而获取Header的字段与值,极大减少了报文大小
- 静态表中的字段和值固定,而且是只读的
- 总之,静态表中是使用索引号来代替完整字段的,而不用使用霍夫曼编码
静态表部分值
动态表
存储本次连接中频繁出现的字段,减少重复传输
- 为什么还需要一个动态表?
- 并不是所有头部字段都能预定义在静态表中,动态表可以用于处理自定义字段
动态表接在静态表之后,结构与静态表相同,可随时更新
- 查询方式也相同 -- 客户端通过传输索引号,服务端根据索引号在动态表中获取Header的key与value
下图中索引号62、63即为动态表字段:
- 这些需要完整传输的自定义头部字段的值,就可以使用霍夫曼编码减少数据量
- 而头部字段名本身可以使用索引名代替,就不需要压缩
霍夫曼编码
霍夫曼编码用来压缩 HTTP 头部字段的值,进一步减少传输数据量
服务器推送
HTTP/1.1:
服务器仅在客户端请求时才发送资源
HTTP/2:
支持服务器推送功能
- 允许服务器在客户端请求之前主动向客户端推送资源
- 例如在请求 HTML 页面时,服务器可以主动推送相关的 CSS、JavaScript 文件,减少延迟
安全性
HTTP/1.1:
可以在不使用加密的情况下传输数据,但这可能导致安全风险
HTTP/2:
虽然协议本身并未强制要求使用加密,但主流浏览器和服务器实现通常要求使用 TLS(传输层安全协议)加密,以确保数据传输的安全性
性能
HTTP/1.1:
由于存在队头阻塞等问题,性能可能受到限制
HTTP/2:
通过多路复用、头部压缩和服务器推送等特性,显著提高了传输性能,降低了延迟和带宽占用
有参考 -- HTTP/2协议之头部压缩【原理笔记】-CSDN博客