3
头图

文章基调

  • 不是科普类文章,不是科普 http2 功能的文章
  • 记录 http2 中难以理解的点,系作者在学习 http2 时的困惑,已经最终的理解
  • 是个人的理解,可能有不严谨的地方,欢迎讨论

    如何理解 TCP 分帧 与 http2 分帧 的区别

  • 假设「传输完整的数据」是「运输一个订单货物」,每「订单中的一个货物」占满「一个货车厢」
  • TCP 位于传输层,可以理解为运输的货车

    • TCP 中的每一帧都是有序的,按发车时间标记趟次,第一趟次,第二趟次,第三趟次,第 N 趟次
    • TCP 可以同时发若干辆车,假设 4 辆车,则一次发车就有,第一趟次,第二趟次,第三趟次,第四趟次
    • 每辆车有些提前到达,有些很慢才到,不一定按照发车的顺序到达目的地
    • 当其中一个趟次的车回来了,才发下一趟车次。
    • 趟次的作用,是为了货品送到目的地的时候可以重新按顺序排列,可以将每趟次的货理解为高达模型的零件(一车只运送一个零件),有顺序,才能识别重新拼装起来
    • 将一个订单中的货,分别通过不同的趟次运输,就是「TCP 二进制分帧」,将订单的货(完整的数据)分成每一车(帧)进行运输
  • HTTP 处于应用层,一个 http 请求及响应,可以理解为下订单(请求)购买一批货(响应)的过程,(注意是一批,有若干个货物)而这批货并没有货品名称(无法对应这批货对应的是哪个订单,无法将响应与请求关联起来

    • 这时 http 还没有分帧化,粒度是以订单为单位,一个订单就是一个 http
  • 将一个 TCP 链接理解为一个运输合同

  • http0.9,1.0
    【问题】每一次订单都签一次运输合同,很麻烦

    • 签一次运输合同(三次握手)
    • 下订单(请求)
    • 生产货物(服务器计算结果)
    • 发起运输(TCP 传输内容)
    • 收货物(响应)
    • 结一次运输合同的账(四次挥手)
    • 签一次运输合同(三次握手)
    • 下订单(请求)
    • 生产货物(服务器计算结果)
    • 发起运输(TCP 传输内容)
    • 收货物(响应)
    • 结一次运输合同的账(四次挥手)
  • http1.1 keep-alive
    【改进】运输合同改成月结,复用 TCP 链接
    【问题】工厂无法同时生产多个订单的货物,需要上一个订单收货

    • 签一次运输合同(三次握手)
    • 下订单(请求)
    • 生产货物(服务器计算结果)
    • 发起运输(传输内容)
    • 收货物(响应)
    • 下订单(请求)
    • 生产货物(服务器计算结果)
    • 发起运输(传输内容)
    • 收货物(响应)
    • 结一次运输合同的账(四次挥手)
  • http1.1 pipeline
    【改进】可以同时发多个订单了,通过收货顺序,来识别货物对应的是那一次订单的内容
    【改进】工厂可以同时生产多个订单的货
    【问题】订单存在依赖关系,即使第二次订单的货物生产好了,也得等第一次订单的货物生产好并全部传输完,才能发货。否则会被当做第一个订单的货

    • 签一次运输合同(三次握手)
    • 下订单(第一个请求)
    • 下订单(第二个请求)
    • 同时生产第一个批和第二批货物(服务器计算结果)
    • 按顺序发第一个订单的运输(传输内容)
    • 按顺序收货物(响应,对应第一个响应)
    • 按顺序发第二个订单的运输(传输内容)
    • 收货物(响应,对应第二个响应)
    • 结一次运输合同的账(四次挥手)
  • http2
    【改进】将一个订单的货拆分成多个批次,为每个批次标识上是哪个订单的货
    【改进】由于能识别一批次货品所属订单,最小粒度从一个订单的货,改为一批次的。原本按订单运输,现在改为按批次运输,这个最小颗粒度的变化就是 http2 分帧:将一个响应或请求拆分成多个分帧片段
    【改进】最小粒度变成了批次,生产完一批次的货品,就可以马上传输,不需要等整个订单的货全部生产完才传输

    • 签一次运输合同(三次握手)
    • 下订单(第一个请求)
    • 下订单(第二个请求)
    • 同时生产第一个订单和第二订单的货物(服务器计算)
    • 每生产批次货物,就给这批次的货打上标签,标识是哪个订单的货(分帧)
    • 准备好了一批次货物,就发运输,不需要管是哪个订单的(传输)
    • 收货,重新分拣是哪个订单的货(根据分帧标识对应是那一次请求的响应)
    • 每生产批次货物,就给这批次的货打上标签,标识是哪个订单的货(分帧)
    • 准备好了一批次货物,就发运输,不需要管是哪个订单的(传输)
    • 收货,重新分拣是哪个订单的货(根据分帧标识对应是那一次请求的响应)
    • 直到所有货物都传输完成
    • 结一次运输合同的账(四次挥手)
  • 总结

    • 可以看出,tcp 的分帧与 http2 的分帧是不同维度的区分
    • tcp 的分帧维度是一个趟次运输(一个趟次运输一个货物),http2 的分帧维度是 一批货物(可能是一个货物,也可能是若干个货物)
    • http2 分帧的本质是将原本一个订单(一个请求或响应),拆分成多个批次(多个帧),缩小数据颗粒度,增加灵活性
  • 参考资料

为什么要分帧

  • 本质上,只要给一个订单的货(响应)打上订单(请求)标识,就可以标识是哪个订单的,就可以解决先订单依赖的问题(后一个订单不需要等前一个订单传输完),为什么需要将订单拆散为批次呢?

    • 车的数量(TCP 通道宽度,流量宽度)是有限的,拆散了并不是加快运输过程
  • 拆成批次(分帧)是为了从根源支持数据的分配传输,如果一个订单的量很大,按订单发货,就得等整个订单的货生产完成才能运输。而拆成批次(分帧)就可以将粒度减低,生产一个批次就运输一个批次,不需要等整个订单的货生产完成。

如何理解 http1.1 是文本协议,http2 是二进制协议

  • stackoverflow 中对应的讨论对应国内论坛
  • 文本协议,信息传输过程经历以下步骤

    • 编写文本,由于规则比较松散,可能存在多余的前后空格,多余的换行等情况
    • 传输文本,传输的是 ASCII,即文字对应的编码
    • 读取文本,理解文本,识别 ASCII 码组合起来的单词,再通过字符串匹配的方式匹配意义
  • 二进制协议

    • 这里的二进制,主要体现在两个方面「二进制帧封装」及「头部压缩」
    • 「二进制帧封装」即将数据打散,外包一层二进制帧数据(用于标识当前帧的特性)

      • 所以是这一分帧层进行了二进制封装,而不是 http 的内容二进制,所以更合适的称呼应该是「增加了二进制分帧层」
      • 这里的二进制帧数据,是用二进制为颗粒度代表数据的含义,每个 0 和 1 代表特殊的含义
      • 而文本协议,是通过 ASCII 对应的单词来表达含义,
      • 即代表数据的颗粒度不一样了,原本是有若干个字母,现在是由若干个比特
    • 「头部压缩」

      • 通过静态表和动态表的索引值来代表含义
      • 相当于双方建立的临时暗号

http2 头部压缩,如何做到浏览器动态表与服务器动态表的同步


momo707577045
2.4k 声望609 粉丝

[链接]