【传智播客郑州校区】HTTP/2技术整理

1. HTTP协议发展 1.1. HTTP的历史

HTTP于1989年正式发布,也就是HTTP/1啦,在经历10年后于1999年更新出了HTTP/1.1,也是我们现在普遍使用的版本。
在2015年初HTTP/2标准正式发表,取代HTTP1.1成为HTTP的实现标准。也就是说,到现在HTTP/2才出现不到3年。
(具体的发展可参考维基百科:
https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE)

1.2. 了解HTTP 1.2.1. 讨论环境:

相对于我们后台开发来说,对前端这块的概念相对比较薄弱,比如我们开发了一个BOS物流项目,已经放在tomcat上面,现在我们可以通过http://localhost:8080/bos/index.html来进行访问。那么客户端浏览器是怎么拿到首页的资源的?浏览器和服务器直接究竟是如何通信的呢?

1.2.2. HTTP通信过程

在这里主要有3个过程:
  • 建立TCP连接:也就是浏览器与服务器的3次握手.
  • 客户端请求: 建立TCP连接后,客户端就会向服务器发送一个HTTP请求信息(比如请求HTML资源,我们暂且就把这个称为“HTML请求”)
  • 服务器响应: 服务器接收到请求后进行处理将HTML响应回去。
  
当然,接下来还有浏览器解析渲染的过程~等等~我们才能最终看到页面~~
接下来我们着重看下HTTP/1.0和HTTP/1.1在这三个过程中的不同之处:

1.3. HTTP/1.0的通信

在HTTP/1.0下,每完成一次请求和响应,TCP连接就会断开。但我们知道,客户端发送一个请求只能请求一个资源,而我们的首页index.html不可能只有单单一个HTML文件吧?至少还要有CSS吧?还要有图片吧?于是又要一次TCP连接,然后请求和响应。
下图展示了HTTP/1.0请求一个HTML和一个CSS需要经历的两次TCP连接:

1.4. HTTP/1.1的通信

要知道,TCP连接有RTT(RoundTripTime,即往返时延)的,每请求一个资源就要有一次RTT,用户可是等不得这种慢节奏的响应的。于是到了HTTP/1.1,TCP可以持久连接了,也就是说,一次TCP连接要等到同域名下的所有资源请求/响应完毕了连接才会断开。恩!听起来情况好像好了很多,请求同域名下的n个资源,可以节约(n-1)*RTT的时间。
下图展示了HTTP/1.1时请求一个HTML和一个CSS只需要经历一次TCP连接:

1.5. HTTP优化

但前面提到了,客户端发送一个请求只能请求一个资源,那么我们会产生如下疑问:

1.5.1. 为什么不一次发送多个请求?

事实上,HTTP/1.x多次请求必须严格满足先进先出(FIFO)的队列顺序:发送请求,等待响应完成,再发送客户端队伍中的下一个请求。也就是说,每个TCP连接上只能同时有一个请求/响应。这样一来,服务器在完成请求开始回传到收到下一个请求之间的时间段处于空闲状态。

1.5.2. 有什么办法去改变吗?

“HTTP管道”技术实现了客户端向服务器并行发送多个请求。而服务器也是可以并行处理多个请求的。这么一来,不就可以多路复用了吗?但是,HTTP/1.x有严格的串行返回响应机制,通过TCP连接返回响应时,就是必须一对一,前一个响应没有完成,下一个响应就不能返回。所以使用“HTTP管道”技术时,万一第一个响应时间很长,那么后面的响应处理完了也无法发送,只能被缓存起来,占用服务器内存,这就是传说中的“队首阻塞”。

1.5.3. 既然一个TCP连接解决不了问题,那么可以开多个吗?

既然一条通道(TCP连接)通信效率低,那么就开多条通道呗!的确,HTTP/1.1下,浏览器是支持同时打开多个TCP会话的(一般为6个)。一个TCP只能响应一个请求,那么六个TCP岂不就能达到六倍速?想想还有点儿小激动!但事情往往不是这么简单。开启多个TCP会话,无疑会给客户端和服务器都带来负担,比如缓存、CPU时钟周期等,而且并行的TCP也会竞争带宽,并行能力也是受限制的,往往无法达到理想状态下的六倍速。
从下面这张谷歌浏览器的网络监控可以看出每次请求6个:

 
2. HTTP的使命

可见,我们采取了许多方法,希望可以并行处理请求/响应,但都不能从根本上解决问题。况且,很多方法与HTTP/1.x的设计理念是背道而驰的,在HTTP/1.x下,却没有正确利用好HTTP/1.x的特性。
于是,HTTP/2带着提高性能的使命,应运而生。
那么HTTP/2做了什么改变?
先对HTTP/2产生的影响有一个直观的认识:
这里有个Akamai公司(全球最大的CDN服务商)的一个官方演示,他是分别使用HTTP/1.1和HTTP/2请求379张小图片,最终拼成一副大图片,然后对比消耗时间。
链接:https://http2.akamai.com/demo
大家可以点开观察一下效果。这里我截取一下我电脑的结果:
家可以自己试试,我这个结果有点逗逼,测试了几次都是HTTP/1.1在20s以上,HTTP/2在2s以内。和网上别人的HTTP/1.1在7s以内差距有点大。但更可以明显看出,HTTP/2下加载时间和HTTP/1.1都不在一个数量级,那么HTTP/2到底为什么这么快?我们还是从它的新特性来进行全面的了解。
以下着重介绍五个特性:二进制分帧层、多向请求与响应、优先级和依赖性、首部压缩、服务器推送。

3. HTTP/2五大特性 3.1. 二进制分帧层

二进制分帧层(BinaryFramingLayer)指的是位于套接字接口与应用可见的高层HTTP API之间的一个新机制:HTTP的语义,包括各种动词、方法、首部,都不受影响,不同的是传输期间对它们的编码方式变了。
在新引进的二进制分帧层上,HTTP/2将所有传输的信息分割为更小的消息和帧,且都采用二进制格式的编码。
说了那么多都什么gui,也没听懂,还是看图吧:
从图上可以看到:在上面HTTP API和下面TCP连接中引入了一个二进制分帧层;在二进制分帧层上,它将我们以前普通的HTTP请求的请求头和请求正文分割成了两个部分:HEADERS帧和DATA帧。请求头起始行、首部被分割到HEADERS帧,实体正文被分割到DATA帧。
接下来,我们再深入地了解下这些被分割后的二进制帧是怎么工作的:
HTTP/2同域名的所有通信都是在一个TCP连接上完成,这个连接可以承载任意数量的双向数据流。而每个数据流都是以消息的形式发送的,消息由一个帧或多个帧组成。
u 流:已建立的连接上的双向字节流
u 消息:与逻辑消息对应的完整的一系列数据帧
u 帧:HTTP/2通信的最小单位,每个帧包含帧首部
好像很复杂的样子,咱们来捋一捋:
TCP连接在客户端和服务器间建立了一条运输的通道,可以双向通行,当一端要向另一端发送消息时,会先把这个消息拆分成几部分(帧),然后通过发起一个流对这些帧进行发送,最后在另一端将同一个流的帧重新组合。
这个过程就好像我们在搬家的时候,会把一个桌子先拆散成零部件,然后通过几次的搬运,到了新家后,再把桌子重新拼装起来。
下图展示了流、消息与帧的关系(注意到没,HEADERS帧总是在最前面的):
HTTP/2规范一共规定了10种不同的帧,其中最基础的两种分别对应于HTTP/1.1的DATA帧和HEADERS帧。

3.2. 多向请求与响应(多路复用)

多路复用允许同时通过单一的TCP连接发起多重的请求/响应消息,客户端和服务器可以把HTTP消息分解为互不依赖的帧,然后乱序发送,最后再在另一端根据 StreamID 把它们重新组合起来。
前面提到的一端发送消息会先对消息进行拆分,与此同时,也会给同一个消息拆分出来的帧带上一个编号(StreamID),这样在另一端接收这些帧后就可以根据编号对它们进行组合。
也正是有了这种编号的方式,当某一端发送消息时,可以发送多个消息拆分出来的多个帧(发起多个流),且这些帧可以乱序发送,因为这些帧都有自己的编号,它们之间互不影响。
下图展示了单一的TCP连接上有多个请求/响应并行交换:
从图上可以看出,服务器向客户端发送stream1的多个DATA帧(说明HEADERS帧已发送完毕)与stream3的HEADERS帧和DATA帧,客户端正在向服务器发送stream5的DATA帧,可见,帧的发送是乱序的,且请求/响应是并行的。
细心的你会发现,stream1中有多个DATA帧,这是为什么呢?因为有DATA帧有长度的控制(2的14次方-1字节,约16383个字节),应用数据过大时,会被拆分成多个DATA帧(还记得讲二进制分帧层展示的HTTP/1.1的请求被分割成更小的帧吗?DATA帧就是用来携带应用数据的)。

3.3. 优先级和依赖性

新建流的终端可以在报头帧中包含优先级信息来对流标记优先级。
优先级的目的是允许终端表达它如何让对等端管理并发流时分配资源。更重要的是,在发送容量有限时优先级能用来选择流来传输帧。
HTTP/2中,流可以有一个优先级属性(即“权重”):
可以在HEADERS帧中包含优先级priority属性;
可以单独通过PRIORITY帧专门设置流的优先级属性。
流的优先级用于发起流的终端(客户端/服务器)向对端(接收的一方)表达需要多大比重的资源支持,但这只是一个建议,不能强制要求对端一定会遵守。
借助于 PRIORITY帧,客户端同样可以告知服务器当前的流依赖于其他哪个流。该功能让客户端能建立一个优先级“树”,所有“子流”会依赖于“父流”的传输完成情况。
不依赖任何流的流的流依赖为 0x0。换句话说,不存在的流标识0组成了树的根。
我们通过以下几个例子来理解下优先级“树”:
第一种情况:流A和流B不依赖流,即为0x0;流A的权重为12,流B的权重为4;则流A分配到的资源占比为12/(12+4)=12/16,流B分配到的资源占比为4/(12+4)=4/16。
第二种情况:流D为0x0,流C依赖于流D;流D能被分配到全额资源,等到流D关闭后,依赖于流D的流C也会被分配到全额资源(它是唯一依赖于流D的流,它的权重的大小此时并不重要,因为没有竞争的流)。
第三种情况:流D为0x0,流C依赖于流D,流A和流B依赖于流C;流D能被分配到全额资源,等到流D关闭后,依赖于流D的流C也会被分配到全额资源;等到流C关闭后,依赖于流C的流A和流B根据权重分配资源(3:1)。
第四种情况:流D为0x0,流C和流E依赖于流D,流A和流B依赖于流C;流D能被分配到全额资源,等到流D关闭后,依赖于流D的流C的流E和流B根据权重分配资源(1:1);等到流C关闭后,依赖于流C的流A和流B根据权重分配资源(3:1)。
前面说到,“可以单独通过PRIORITY帧专门设置流的优先级属性”,也就是说可以对原本没有优先级属性(包括依赖关系)的流进行设置,也可以对原本已有优先级属性的流进行修改。因此,优先级可以在传输过程中被动态的改变。

3.4. 首部压缩

HPACK是专门为HTTP/2量身定制的为有效地表示HTTP首部字段的压缩技术。
在服务器和客户端各维护一个“首部表”,表中用索引代表首部名,或者首部键-值对,上一次发送两端都会记住已发送过哪些首部,下一次发送只需要传输差异的数据,相同的数据直接用索引表示即可。
具体实现如下图所示:
这个过程比较容易理解:通过索引表的对应关系,来标记首部表中的不同信息。
同一个域名下的请求/响应的首部往往有很多重复的信息,当客户端要向服务器发送某个请求时,通过查找索引表,发现该信息的首部已经发送过,此时服务器端的索引表也应该有对应的信息,则不需要再次发送;若查找发现部分首部信息不在索引表中,则发送该部分信首部息即可。
如在上图的示例中,第二个请求只需要发送变化了的路径首部(:path),其他首部没有变化,就不用再发送了。
比如我第一次请求index.html,那么我携带所有的信息过去,第二次如果我请求该服务器下面的login.html,那么只需要携带这个路径:path/login.html就行了,别的信息不用携带,减少数据发送量。

3.5. 服务器推送

服务器推送(ServerPush),服务器可以对一个客户端请求发送多个响应。也就是说,除了对最初请求的响应外,服务器还可以额外向客户端推送资源。
在了解“二进制分帧层”的时候我们提到,“HTTP/2规范规定了10种不同的帧”,其中有一种名为“PUSH_PROMISE”,就是在服务器推送的时候发送的。当客户端解析帧时,发现它是一个PUSH_PROMISE类型,便会准备接收服务端要推送的流。
从上图可以看出,当服务器响应了HTML请求后,可以知道客户端接下来要发送JS请求、CSS请求,于是服务器通过推送的方式(主动发起新流,而不是等客户端请求然后再响应),向客户端发出要约(PUSH_PROMISE)。当然,客户端可以选择缓存这个资源,也可以拒绝这个资源。
这个过程有点类似于我们常用的资源内嵌的手段:将一个图片资源转为base64编码嵌入CSS文件中,当客户端发起CSS请求时,也会请求该图片。因此在响应CSS请求后,服务器会强制(客户端是无法拒绝的)向客户端发送图片响应。但内嵌资源是无法被单独缓存的,而服务器推送的资源是可以被缓存的。
需要注意,服务器必须遵循请求-响应的循环,只能借着请求的响应来推送资源,也就是说,如果客户端没有发送请求,服务器是没法先手推送的。而且,如上图中stream4,PUSH_PROMISE帧必须在返回响应(DATA帧)之前发送,因为万一客户端请求的恰好是服务器打算推送的资源,那传输过程就会混乱了。
注:由客户端发起的流StreamID为奇数,由服务器发起的流StreamID为偶数,回顾上面的图就能发现啦!

4. 小结

u HTTP/2通过二进制分帧与多路复用机制,有效解决了HTTP/1.x下请求/响应延迟的问题。
u 新的首部压缩技术使HTTP/1.x首部信息臃肿的问题得到解决。
u 优先级和依赖性与服务器推送使得我们可以更有效地利用好这个单一的TCP连接。
可见,HTTP/2在HTTP/1.1的基础上有了一个较大的性能提升。这时候你会发现,我们针对HTTP/1.x的一些优化手段(如上文提到的资源内嵌)似乎有点不适用了。

【郑州校区】Python3 创建虚拟环境
【郑州校区】python基础之基本指令汇总
【郑州校区】Ubuntu 如何创建快捷方式
【郑州校区】python基础班之Linux 终端命令格式
【郑州校区】python基础班笔记之python操作系统
【郑州校区】subline 取消自动更新提示
【郑州校区】常用 Linux 命令的基本使用
【郑州校区】MyBatis 入门开发 —-基础入门
【郑州校区】文件和目录常用命令
【郑州校区】MyBatis 入门开发 ----Mapper接口开发
【郑州校区】系统信息相关命令
【郑州校区】用户权限相关命令
【郑州校区】远程管理常用命令
【郑州校区】文件和目录(理解)
【郑州校区】python学习之其他命令
【郑州校区】MyBatis 入门开发 ----配置文件
【郑州校区】MyBatis 入门开发 ----输入,输出参数
【郑州校区】三大框架之Struts2-day01
【郑州校区】三大框架之Struts2-day02
【郑州校区】将第3方的jar 添加到本地Maven库
【郑州校区】Hibernate入门------HelloWord
【郑州校区】三大框架笔记之Struts2-day03
传智播客·黑马程序员郑州校区地址
河南省郑州市 高新区长椿路11号大学科技园(西区)东门8号楼三层
联系电话 0371-56061160/61/62
来校路线  地铁一号线梧桐街站A口出

【传智播客郑州校区分享】HTTP/2技术整理相关推荐

  1. 【传智播客郑州校区分享】MyBatis的架构设计分析

    [传智播客郑州校区分享]MyBatis的架构设计分析 MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简单.优雅.本文主要讲述MyBatis的架构设计思路,并且讨论MyBat ...

  2. 【传智播客郑州校区分享】在传智播客郑州校区学习倍感幸运

    小李是传智播客郑州校区的一名学员,他大学学的是土木工程,当初报这个专业是觉得毕业后比较好找工作,然而经历四年的学习后才发现很多事情并不是自己想象的那样,但是刚毕业的小李还是不得不去从事与专业相关的工作 ...

  3. 【传智播客郑州校区分享】AndroidAnnotations框架详解

    AndroidAnnotations框架详解 文/传智播客郑州中心就业服务部 简介 在之前的开发中,你肯定用到了xUtils及ButterKnife等依赖注入框架,你可以使用这些框架来简化你的代码,因 ...

  4. 【传智播客郑州校区分享】JQ插件机制

    JQ插件概述 所谓的jquery插件,其实就是利用jquery语法,完成的一些工具或者模块.jquery插件是jQuery功能的扩展.可以让开发人员能更好更快速的完成某些特定的需求,只需要用很少的代码 ...

  5. 【传智播客郑州校区分享】数据库MYSQL笔记详解

    第1章 数据库 1.1 数据库概述 l 什么是数据库 数据库就是存储数据的仓库,其本质是一个文件系统,数据按照特定的格式将数据存储起来,用户可以对数据库中的数据进行增加,修改,删除及查询操作. l 什 ...

  6. 【传智播客郑州校区分享】阿里大鱼短信介入demo

    下面是关于大鱼短信平台对接的例子,发短信的话,可以用这个,很好用 /** * 通过阿里短信接口发送短信验证码 * * * */ public class SendSmsUtil { private s ...

  7. 【传智播客郑州校区】辞掉7年工作转行程序员,为了理想在传智播客前行

    小何,一位已经工作了7年的白领,生活和工作衣食无忧,是什么原因让她辞掉干了七年的工作,选择到传智播客郑州校区参加培训,成为程序员的呢? 其实做程序员,是小何心里早就存在的想法.大学毕业的时候她很想做程 ...

  8. 【传智播客郑州】90后大学生到传智播客郑州校区追梦未来

    小张来自河南南阳,是一名90后大学生.临近毕业时,小张对自己的前途很迷茫,不知道自己该干什么,能干什么.就在小张茫然无措的时候,传智播客的老师去小张所在的大学宣讲课程.听完传智播客老师讲的三天课程,小 ...

  9. 深圳php就业,传智播客深圳校区PHP04期毕业15个工作日就业率67.74%

    近年来,随着PHP的应用更加广泛.全球有70%的站点在使用PHP开发,它与Linux/Mysql/Apache/Nginx等组成黄金搭档,无论是从成本投入还是从开发周期考虑,PHP都是一个不错的选择. ...

最新文章

  1. 1063 Set Similarity
  2. 如何禁用请求库中的日志消息?
  3. JAVA运算符\操作符
  4. Linux Shell脚本专栏_监控100台服务器磁盘利用率脚本_07
  5. 计算机网络之应用层:2、DNS域名解析系统
  6. [Cacti] cacti监控mongodb性能实战
  7. C语言--学生管理系统--(完整代码)
  8. 4个关于中台最常见的误区,用最通俗的话给你一次解释清楚
  9. go中的make和new的区别
  10. 复杂度及圈复杂介绍介绍
  11. ASCII码字符对照表
  12. 接口测试面试题及参考答案,轻松拿捏面试官
  13. 顶级赛事 | 2019 CCF BDCI,最值得参与的国际大数据及AI赛事之一
  14. python刷b站视频浏览量_python刷B站播放量
  15. MySQL的数据类型(三)
  16. 参赛必备 | “互联网+”大赛参赛攻略
  17. mysql写出总分最高的学生姓名_编写SQL语句查询出每个各科班分数最高的同学的名字,班级名称,课程名称,分数...
  18. 羊le个羊 小游戏 简单源码开源 思路分析
  19. 打开计算机没有显示出硬盘,电脑启动时显示找不到硬盘如何解决_都叫兽软件...
  20. OpenCV实战5 车牌号识别

热门文章

  1. 机器学习实现简单人脸识别
  2. android 实现按住说话功能
  3. 计算机基础知识整理 手抄报,第三课时《电脑小报的组成及设计》
  4. 43、基于51单片机数码管温控温度控制风扇系统设计
  5. 解决:Java source1.5不支持diamond运算符
  6. php 通过curl上传图片
  7. 付利赚浅谈副业已成刚需
  8. iPhone键盘自定义及通知
  9. 使用Betaflight Configurator飞控刷写固件时各步骤的含义
  10. ocr文字识别如何识别文字?