欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

本文由mariolu发表于云+社区专栏

序言

目前HTTP/2.0(简称h2)已经在广泛使用(截止2018年8月根据Alexa流行度排名的头部1千万网站中,h2占比约29%,https://w3techs.com/technologies/details/ce-http2/all/all)。写此文章的目的是:h2作为较新的技术,并逐渐占有率广泛,虽然目前有更新的QUIC,但其实现思路类似于h2。颠覆以往的HTTP/1.x,H2的创造性的技术值得我们细细品味。此篇文章根据笔者在h2开发经验和思考,向你介绍全面的h2知识以及是非功过。本篇更注重于帮助读者理解h2的设计思路、亦可作为一篇RFC导读或者总结。

第一话、追踪溯源

图1、HTTP年鉴图

早在1991年,伴随WWW诞生之初,HTTP/0.9协议已经提出。HTTP0.9是简单且应用受限的协议。支持去网络主机获取对应路径的资源。但是没有扩展属性。其协议之简单甚至只用下面一个访问谷歌主机的例子概括了HTTP/0.9的全部。如下所示,协议只支持GET,没有http头;响应只能是超文本。

telnet google.com 80

Connected to x.x.x.x

GET /about

(Hyper text)

(Conection closed)

随着人们对富媒体信息的渴望以及浏览器的普及,HTTP/1.0在1996年被提出来。HTTP/1.0的很多特性目前还被广泛使用,但是仍然像HTTP/0.9一样一次请求需要创建一次的tcp连接。随即短短几年时间内,HTTP/1.1以RFC标准形式再次展现在人们眼前。此时的HTTP协议1.1版本已经重新设计了长连接、options请求方法、cache头、upgrade头、range头、transfer-encoding头, 以及pieline(in order)等概念。

而我们另一个所熟知的HTTPS的SSL/TLS技术各个版本差不多在后来的十年间逐渐被提出。出于安全考虑,互联网通信间的防火墙路由交换机等设备,这些设备一般仅会开发有限的端口(如80和443)。各种版本的通信协议只能复用这些端口。HTTP1.1的80端口设计了upgrade请求头升级到更高级的协议,而443端口为了避免多消耗个网络RTT,在tls握手过程中使用了NPN/ALPN技术直接在通信之前保持CS两端的协议一致。NPN/ALPN是是TLS协议扩展,其中NPN是Google为实现spdy提出的。由服务端提供可支持的协议,供客户端选择。ALPN则是更接近于HTTP交互的方式,由客户端先发出使用某种协议的请求,由服务端确认是否支持协议。ALPN为了HTTP2诞生做铺垫。

另外一个不得不提的是spdy协议。Spdy旨在解决HTTP1.1的线头阻塞问题(后面章节有详细讨论)于2009被google提出。同时分别于2012提出了spdy3.0实现了流控制,2013-2014期间提出了流优先级,server push等概念。Spdy的存在意义更像是http2.0的体验服。它为探索HTTP继续演进道路做了铺垫。

第二话、人机交互

汇编是效率最高的语言之一,但是又是最晦涩难懂的语言之一。而人脑易懂的编程语言往往牺牲性能作为折衷。简单说,HTTP/1.x协议就是为了人类语言习惯所设计的协议,但是转换成机器执行协议并不是高效的。让我们在回顾下计算机执行解析HTTP1.x的流程。

GET / HTTP/1.1<crlf>

Host: xxx.aa.com<crlf>

<crlf>

对应的解析伪代码是

loop

while(! CRLF)

read bytes

end while

if line 1:

parse line as Request-Line

else if empty line:

Break out and We have done

else If start with non-whitespace

parse header

else if space

continue with last heade

end if

end loop

在伪代码解析流程可以看到,肉眼看起来简洁的协议解析起来是这么的费劲。而且在HTTP服务器中还要考虑这种问题:字节行的长度是未知的,也不知道预先分配多大内存。

HTTP/2.0使用了计算机易懂的二进制编码信息,而且得向上兼容HTTP的涵义。具体我们来看下他是如何做到的。像大多数通信协议一样,桢是传输最小单位。桢分为数据帧和控制桢。数据帧作为数据的载体,控制桢控制信道的信令。h2桢的通用格式为首部9字节+额外的字符。正如你能想到的那样,桢的第一个部分是描述长度,第二个部分描述了桢的类型,第三个部分描述了标志Flag,第四个部分是唯一序列号。这是所有桢的通用头。通用头紧接的是桢的实体。图4展示了桢的结构。

图2、通用桢的格式

这样设计有什么好处呢。再来看一下桢的解析流程,你就会发现对计算机来说更简洁。

loop

read 9 byte

payload_Length=first 3 bytes

read payload

swith type:

Take action

end loop

HTTP2.0使用header桢表达HTTP header+request line,data桢表达Body。header桢和data桢使用相同的stream id组成一个完整的HTTP请求/响应包。这里的stream描述了一次请求和响应,相当于完成了一次HTTP/1.x的短连接请求和响应。

第三话、并行不悖

上节讲到我们用h2桢完整表达了HTTP/1.x。但是h2协议抱负远不止于此。它的真正目的是解决之前HTTP1.x的线头阻塞问题、改善网络延迟和页面加载时间。

我们知道一个完整的网页包含了主页请求和数次或数十次的子请求。HTTP/1.1已经可以并行发出所有请求.但是HTTP本身是无状态的协议,它依赖于时间的顺序来识别请求和响应直接的对应关系。先来的请求必须先给响应。那么如果后面的响应资源对浏览器构建DOM或者CSSOM更重要。那它必须阻塞等待前者完成。当然这也难不倒我们,我们可以多开几条tcp连接(浏览器规定一个origin(协议+host+port)最多6个)或者合并资源来减少不必要的阻塞。这是有代价的。首先tcp建连的开销,其实合并资源带来一小块子资源过期导致整个合并资源的缓存过期。对此,h2有一揽子的解决方案,接下来一一道来。

h2在一个tcp连接创建多个流。每个流可以有从属关系,比如说根据浏览器加载的优先级顺序(主请求>CSS>能改变DOM结构的JS文件>图片和字体资源文件)建立一条依赖关系链。处于同一等级的依赖关系中可以设置权重。权重用于分配传输信道资源多少。

图6例子说明了有一次主页请求index.html、一次main.css,一次jq.js以及一些image文件和字体文件qq.tff

图3、h2请求的依赖树

HTML的优先级最高,在HTML传输完成之前,其他文件不会被传输。HTML传输完成后,JS和CSS根据其分配的权重占比分配信息传输资源。如果CSS传输完成后,TFF和PNG如果是相同权重,那么他们将占有1/4的信道资源。

这里抛出3个问题和答案。

  • 如果CSS被阻塞了,那么 JS 得到本属于CSS的通信资源
  • 如果CSS传输完成但没有被移依赖树, TFF和PNG继承CSS的通信份额 (假设TFF和PNG权重一样,那么各分得1/4通信资源).
  • 如果CSS在依赖数被移除,JS, TFF, PNG平分通信资源(假设3个权重一样,那么三者各分得1/3通信资源)

第四话、众星捧月

HTTP2还设计了一系列方案来改善网络的性能、包括流量控制,HPack压缩,Server Push。

什么你说TCP已经有流量控制了,HTTP不是多此一举吗?没错,但是在单条TCP内部,各个流可是没有流量控制。流量控制使用了Update Frame不断告知发送方更新发送的窗口大小(上限)。流量控制一个现实用途是阻塞不重要的请求,以腾出更大的通信资源给重要的请求使用。流量控制是不可以被关闭的,流量大小可以设置2的31次方-1(2GB)。不同的中间网络设备有不一样的吞吐能力。流控的另一个用途在于同步所有的中间设备交换机最小的上限。流量窗口初始大小为65535(2的16次方-1)。

就像世界上的大多数财富聚集在少数人身上一样。在一份对48,452,989个请求的统计中,以下11个头占据了99%的数量,依名次递减分别是user-agent、accetp-encoding、accept-language、accept、referer、host、connection、cookie、origin、upgrade-inseure-request、content-type。http header的值也有很大相似性,比如说”/index.html“, “gzip, deflate”。Cookie也携带冗余的信息。

这些都组成了http header大量可以压缩的内容。

而在一份GET请求和一个304响应或者content-length很少的响应中,这些头占据了很大比例的通信资源。2016年发布的一份HTTP报告中,请求头大约在460bytes,对一个通常的网页,平均会有140个请求对象。这些头总共需要63KB。这些量很有可能会是首屏和页面加载时间优化的瓶颈。

可能你会说用gzip等压缩算法这些请求头,不就完了吗?的确spdy就这样干过,直到2013年BREACH攻击暴露了gzip压缩在https应用的安全性,这种攻击让攻击者很容易获得session cookie等数据。于是才有了HPACK。

HPACK简单来说就是索引表,包括静态表和动态表。静态表由RFC定义,从不改变,静态表预留了62个表项。每个连接的通信双方维护着动态表。

H2协议使用索引号代表http中的name、value或者name-value。假设被索引的是name,value没有索引,那么value还可以用霍夫曼编码压缩。

  • 在预定的头字段静态映射表 中已经有预定义的 Header Name 和 Header Value值,这时候的二进制数据格式如图4, 第一位固定为1, 后面7位为映射的索引值。图8的83就是这样的,83的二进制字节标示1000 0011,抹掉首位就是 3 , 对应的静态映射表中的method:POST。

图4、index索引name和value

图5、抓包示意

  • 预定的头字段静态映射表中有 name,需要设置新值。图6所示例子,一个指定 path的Header,首字符 为 44 ,对应的二进制位0100 0100。前两个字符为 01 ,Index 为 4 ,即对应静态映射表中的 path 头。第二个字符为 95对应的二进制位 1001 0101,排除首字符对应的 Value Length 为 十进制的21。即 算上 44,一共23个字符来记录这个信息。

图6、index索引name和自定义value

图7、抓包示意

  • 预定的头字段静态映射表中没有 name,需要设置新name和新值。40 的二进制是0100 0000,02 的二进制是0000 0010,后七位的十进制值是 2,86 的二进制是1000 0110,后7位的十进制值是6。

图8、index索引自定义name和自定义value

图9、抓包示意

  • 明确要求该请求头不做hpack的index

HTTP2.0还有个大杀器是Server Push,Server Push利用闲置的带宽资源可以向浏览器预推送页面展示的关键资源,Server push有效得降低了页面加载时间。具体详情参考笔者的另一篇文章https://cloud.tencent.com/developer/article/1159626。

第五话、庖丁解牛

HTTP2相比HTTP1更适合计算机执行。但是其二进制特性不易于人脑理解。这一话我们专门来讲讲关于http2的调试工具。

  • Chrome(qq浏览器)可以按住F12查看h2协议。图13所示为浏览器网络时序图,列出了具体协议名称。chrome还可以在地址栏敲入chrome://net-internals/#http2查看到h2协议细节,如图11所示。点击相应的host就可以看到h2协商过程,如图12所示。

图10、浏览器的网络时序图

chrome://net-internals/#http2

图11、chrome调试h2

图12、h2协商过程

  • wireshark(需和chrome或firefox搭配使用)。设置环境变量SSLKEYLOGFILE=c:\temp\sslkeylog.log。然后在Wireshark->Preferences->Protocols->SSL配置key所在路径。

图13、wireshark配置

图14、wireshark抓h2包

  • Nghttp2是一个完整的http2协议实现的组件。作者也参与过spdy实现。目前nghttp2库被很多知名软件作为h2协议实现库使用。另外nghttp2也自带了h2协议的分析工具。图18展示了在明文状态使用upgrade头升级到h2c。图19展示了在https基础上升级到h2。

图15、明文状态使用upgrade头升级到h2c

图16、展示了在https基础上升级到h2

Curl的—http2选项(需要和nghttp2一起编译)

图17、支持h2的curl客户端调试

  • Github还有些实用的http2工具组件,诸如chrome-http2-log-parser、http2-push-manifest等组件,笔者后续会专门开篇文章介绍这些工具。对于移动端的调试,ios可以用charles proxy做代理,android需要开发者模式使用移动端的chrome,笔者在移动端使用较少,这里就不做展开。

第六话、雕栏玉砌

H2怎么部署呢,目前主流服务端像nginx、apache都已经支持http2,主流的客户端curl和各种浏览器(包括移动端safari和chrome-android)基本也支持http2。代理服务器如ATS、Varnish,Akamai、腾讯云等CDN服务也支持http2。那么怎么把一套网站部署到h2。或者说部署h2网站和之前h1网站有什么不一样?

如果是自己的源站,那么请确保服务器支持TLS1.2已经RFC7540所要求的加密套件,h2需要保证支持alpn。你可以使用ssllabs等网站检查。对于h2服务器的要求是h2必须了解如何设置流的优先级,h2服务器需要支持server push。h2客户端需要尽量多的发送请求。

如果你的网站是从http1.x迁移过来的,那么之前对于http1.x所做的优化可能无任何帮助甚至更差。合并小文件不在需要,因为额外的小文件请求在h2看来只是开销很少。并且如果大文件的局部更改使得整个大文件缓存失效。在http1.0时代使用多个域名来并发http连接,在http2也毫无必要,因为http2天生就是并发的。http1.x做的优化比如说图片资源文件不使用cookie来减少请求大小,http2的header压缩功能也减少了这种影响。即使不做这种优化也亦可。像合并css、小图片带来的增益在http2.0也是可忽略的。

如果网页使用第三方网站组件,那么请尽可能减少使用第三方网站组件。第三方网站不能保证支持h2,所以它可能成为木桶理论的最大短板。

谨慎使用2.0-1.x的部署方案,h2流转化成h1请求。因为这样无法发挥h2性能。

图18、2.0-1.x的部署方案

CDN代理服务器的h2支持,可以屏蔽h2强制走tls的代理服务器。如图19,代理可以在与各种协议客户端的网络环境下,切断和客户端的tls连接,和服务器新建连接。也可以作为load balancer,相当于HTTP2.0用户和HTTP2.x服务器直接通信。

图19、带tls客户端功能的代理

图20列举如果绕过proxy到达h2服务器。此时的proxy相当于tcp转发的load balance功能的设备。如果该proxy支持tls的alpn协议,那么它也可以选择HTTP代理功能,和h2服务器可以建立加密连接。如果即不支持alpn,也不支持tcp转发。那么proxy只能用upgrade升级成h2协议。

图20、经过代理服务器的H2部署方案

第七话、十全九美

HTTP2.0是建立在TCP之上,所以TCP的所有缺点他都有,所以H2能发挥最大性能得益于调优的tcp协议栈。TCP的慢启动特性,决定h2一开始的并发流量不会太大,TCP以及SSL的握手连接也会拖慢h2的首包网络耗时。QUIC则完全地抛弃TCP,在UDP基础上实现了HTTP2的一系列特性。同时做了应用层的如TCP的可靠性保障。同时这些TLS1.3传输更快更简洁。这些都为HTTP2.0进化到HTTP3.0提供了一些思路。

总结

以上内容都来源于笔者的实践经验和理论总结。篇幅所限不能涵盖各个细节。具体可以继续参考RFC7540和RFC7541协议。

问答
没有“http | https”的网址怎么实现?
相关阅读
我是怎么一步步用go找出压测性能瓶颈
HTTP/2之服务器推送(Server Push)最佳实践
低于0.01%的极致Crash率是怎么做到的?
【每日课程推荐】新加坡南洋理工大学博士,带你深度学习NLP技术

此文已由作者授权腾讯云+社区发布,更多原文请点击

搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!

海量技术实践经验,尽在云加社区

原来你是这样的http2......的更多相关文章

  1. 前端性能优化的另一种方式——HTTP2.0

    最近在读一本书叫<web性能权威指南>谷歌公司高性能团队核心成员的权威之作. 一直听说HTTP2.0,对此也仅仅是耳闻,没有具体研读过,这次正好有两个篇章,分别讲HTTP1.1和HTTP2 ...

  2. HTTP、HTTP2

      HTTP.HTTP2.0.SPDY.HTTPS 你应该知道的一些事 原文链接:http://www.alloyteam.com/2016/07/httphttp2-0spdyhttps-readi ...

  3. 两年来的core折腾之路几点总结,附上nginx启用http2拿来即用的配置

    序:一年多没更新博客园的内容了,core已经发生了翻天覆地的变化,想起2014年这时候,我就开始了从当时还叫k的那套preview都不如的vnext搭建这套系统,陆陆续续它每一次升级,我也相应地折腾, ...

  4. HTTP2特性预览和抓包分析

    背景 近年来,http网络请求量日益添加,以下是httparchive统计,从2012-11-01到2016-09-01的请求数量和传输大小的趋势图: 当前大部份客户端&服务端架构的应用程序, ...

  5. HTTP2试用小记

    原文:https://www.clarencep.com/2016/11/17/upgrade-nginx-to-support-http2/ 这两天把公司的网站升级到了全站https. 顺便瞄到了H ...

  6. http2协议翻译(转)

    超文本传输协议版本 2 IETF HTTP2草案(draft-ietf-httpbis-http2-13) 摘要 本规范描述了一种优化的超文本传输协议(HTTP).HTTP/2通过引进报头字段压缩以及 ...

  7. HTTP2.0的二进制分帧

    1.帧的类型: 在二进制分帧的结构中,头部有8个字节(64Bit),其中有一个字节(8Bit)来标志帧的类型: HTTP2.0规定了如下帧类型: DATA: 用于传输HTTP消息体 HEADERS:用 ...

  8. HTTP2.0介绍

    HTTP2.0的所有通信都是在一个TCP连接上完成的. 1.信息传输术语 1.1 流 虚拟信道,可以承载双向消息,每个流都有一个唯一的整数标识符(1.2....N); 1.2 消息 逻辑上的HTTP消 ...

  9. 或许是 Nginx 上配置 HTTP2 最实在的教程了

    导读 从 2015 年 5 月 14 日 HTTP/2 协议正式版的发布到现在已经快有一年了,越来越多的网站部署了 HTTP2,HTTP2 的广泛应用带来了更好的浏览体验,只要是 Modern 浏览器 ...

  10. HTTP2 学习

    一.HTTP1.x存在的问题 Http1.0时Connection无法复用,同一时间一个Connection只能处理一个request.Http1.1引入了Request pipelining来解决这 ...

随机推荐

  1. 【Dnc.Api.Throttle】适用于.Net Core WebApi接口限流框架

    Dnc.Api.Throttle    适用于Dot Net Core的WebApi接口限流框架 使用Dnc.Api.Throttle可以使您轻松实现WebApi接口的限流管理.Dnc.Api.Thr ...

  2. Docker 修改镜像源地址

    Docker 官方中国区 https://registry.docker-cn.com 网易 http://hub-mirror.c.163.com ustc https://docker.mirro ...

  3. jzoj100029

    陪审团制度历来是司法研究中的一个热议话题,由于陪审团的成员组成会对案件最终的结果产生巨大的影响,诉讼双方往往围绕陪审团由哪些人组成这一议题激烈争夺. 小 W 提出了一个甲乙双方互相制衡的陪审团成员挑选 ...

  4. C语言中printf与i++,C++中的cout

    一,printf与i++ 1,C语言中的printf是自右向左输出,. 2,而i++与++i不同的 i++首先取得i的值,下一行时候i = i + 1: ++i,首先i = i + 1,再取得i的值. ...

  5. OO第二单元の小结

    第二单元(线程与电梯问题)总结博客 三次作业的设计策略 第一次:本次作业只有一部电梯,而且不用捎带.因此,我一共设计了两个线程:一个负责管理输入,一个负责电梯运行.同时,我将调度队列设置为单例模式,里 ...

  6. Protocol Buffers介绍及例子

    Protocol Buffers介绍及例子 Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化.它很适合做数据存储或数据交换格式.可用于通讯协 ...

  7. Unity脚本生命周期与执行顺序

    文章目录 脚本生命周期 MonoBehavior生命周期图 脚本执行顺序 自定义执行顺序 在Unity中,脚本可以理解为附加在游戏对象上的用于定义游戏对象行为的指令代码.必须绑定在游戏对象上才能开始它 ...

  8. spark中资源调度任务调度

    在spark的资源调度中 1.集群启动worker向master汇报资源情况 2.Client向集群提交app,向master注册一个driver(需要多少core.memery),启动一个drive ...

  9. 踩方格 OpenJ_Bailian - 4103

    有一个方格矩阵,矩阵边界在无穷远处.我们做如下假设:a.    每走一步时,只能从当前方格移动一格,走到某个相邻的方格上:b.    走过的格子立即塌陷无法再走第二次:c.    只能向北.东.西三个 ...

  10. HDU4641 || 6194多校 (后缀自动机-最少出现K次的字串个数 || 恰好出现K次字符串的个数)

    http://acm.hdu.edu.cn/showproblem.php?pid=4641 http://acm.hdu.edu.cn/showproblem.php?pid=6194 题意: 开始 ...