标签 http 下的文章

一、HTTP/2概述

HTTP/2是HTTP协议的第二个大版本,相较于HTTP/1而言,HTTP/2的核心观念是“构建一个更快、更简单以及更强大”的web应用。

HTTP/2 will make our applications faster, simpler, and more robust—a rare combination—by allowing us to undo many of the HTTP/1.1 workarounds previously done within our applications and address these concerns within the transport layer itself.

HTTP/2的前身是SPDY协议,这个协议是由google开发出来的一个实验性协议,在2009年中旬发布,主要目的是为了减少网页加载延迟。这个协议的目标是:

  1. 减少50%的页面加载时间。
  2. 无需网站作者修改任何内容。
  3. 最小化部署的复杂性,避免对网络架构的更改。
  4. 与开源社区合作开发这个新协议。
  5. 收集真实的性能数据,验证此协议是否真的有效。

在2012年,SPDY协议得到了chrome、firefox和opera的支持,越来越多的大型网站(如google、twitter和facebook)以及小型网站都在其基础设施内部署SPDY。在被越来越多的行业采用后,SPDY已经具备了称为一个标准的条件。也正因为这样,HTTP-WG在SPDY的基础上制定了官方的HTTP/2协议。在2015年初,IESG批准了这个基于SPDY协议的HTTP/2协议。

其实很多应用目前(2020)就已经使用了SPDY协议,例如我在一个部署了阿里云CDN的网站上就看到了SPDY相关的身影:

虽然是个报错信息,但可以看出目前SPDY(HTTP/2)确实已经被广泛使用。而在实验数据上,通过模拟网络连接下载了25个最流行的网站之后,页面加载速度最高加快了55%。

So far we have only tested SPDY in lab conditions. The initial results are very encouraging: when we download the top 25 websites over simulated home network connections, we see a significant improvement in performance - pages loaded up to 55% faster.

——来源:Chromium Blog

HTTP/2没有改变HTTP的基本语义和操作,各种操作方法(如GET/POST/HEAD等)、请求头和请求体机制都没有改变,不同的只是传输的方式改变了。HTTP/1.x 协议以换行符作为纯文本的分隔符,而 HTTP/2 将所有传输的信息分割为更小的消息和帧,并采用二进制格式对它们编码。

HTTP/2的主要功能特性:

  1. 将HTTP协议中的纯文本传输修改成了二进制帧传输,分为头部帧和数据帧。
  2. 使用HPCAK对头部数据压缩,添加头部索引减少重复头部数据传输。
  3. 添加数据优先级,针对不同的数据源建立不同的优先级树,确保优先级高的数据不会被阻塞。
  4. 请求响应的复用,同一个数据流中可以同时以不同的顺序发送多个不同的帧,每个数据源只产生一个连接。
  5. 流控制,阻止发送方向接收方发送的大量数据,通过请求窗口限制请求速率。
  6. 服务端推送,打破严格的请求-响应机制,服务端也可以主动推送数据到客户端。

二、数据帧的传递方法

2.1 HTTP/2数据传输的基本元素

新的HTTP/2协议通过二进制帧来传输数据,改变了客户端与服务器之间交换数据的方式,主要涉及了三个概念:

  • 帧:HTTP/2数据传输的基本单位,通过把原始的文本数据处理成二进制帧来发送。
  • 消息:逻辑请求和响应构成的统一整体就是消息。
  • 数据流:建立的TCP连接,承载发送一条和多条消息。

帧和消息

和HTTP/1一样,HTTP/2依旧还是保留了请求头、请求体以及响应体的部分。只不过在传输的时候被处理成了二进制的内容格式,同时把请求帧和响应帧区分开来了,两者可以单独发送。

例如以下是一个GET请求的帧格式(实际传输是二进制的):

因为GET请求不包含请求体,因此只会有一个标头帧发送出去。而服务端响应的帧格式为:

上面的请求帧和响应帧构成的帧集合即为消息。

一个TCP连接上包含的多个消息即为流,它体现形式为:

2.2 每个来源一个连接

在浏览器中,一个页面存在很多请求,虽然HTTP/1.1中由连接保持机制,但是大量的连接还会使得浏览器不得不建立多个连接来连接复用。大部分的浏览器还会限制单个源的连接数量,并且由于HTTP/1.1中的keep-alive是基于pipeline的,所以一旦有些请求阻塞后会导致其他请求也阻塞。

当使用二进制帧传输后,HTTP/2不再依赖创建多个TCP连接进行连接复用。每个数据流都拆分成很多帧,而这些帧可以交错,还可以分别设定优先级。 因此,所有 HTTP/2 连接都是永久的,而且仅需要每个来源一个连接,随之带来诸多性能优势。

大多数 HTTP 传输都是短暂且急促的,而 TCP 则针对长时间的批量数据传输进行了优化。 通过重用相同的连接,HTTP/2 既可以更有效地利用每个 TCP 连接,也可以显著降低整体协议开销。 不仅如此,使用更少的连接还可以减少占用的内存和处理空间,也可以缩短完整连接路径(即,客户端、可信中介和源服务器之间的路径) 这降低了整体运行成本并提高了网络利用率和容量。 因此,迁移到 HTTP/2 不仅可以减少网络延迟,还有助于提高通量和降低运行成本。

连接数量减少对提升 HTTPS 部署的性能来说是一项特别重要的功能:可以减少开销较大的 TLS 连接数、提升会话重用率,以及从整体上减少所需的客户端和服务器资源。

三、标头压缩

每个 HTTP 传输都承载一组标头,这些标头说明了传输的资源及其属性。 在 HTTP/1.x 中,此元数据始终以纯文本形式,通常会给每个传输增加 500–800 字节的开销。如果使用 HTTP Cookie,增加的开销有时会达到上千字节。 (请参阅测量和控制协议开销。) 为了减少此开销和提升性能,HTTP/2 使用 HPACK 压缩格式压缩请求和响应标头元数据,这种格式采用两种简单但是强大的技术:

  1. 这种格式支持通过静态霍夫曼代码对传输的标头字段进行编码,从而减小了各个传输的大小。
  2. 这种格式要求客户端和服务器同时维护和更新一个包含之前见过的标头字段的索引列表(换句话说,它可以建立一个共享的压缩上下文),此列表随后会用作参考,对之前传输的值进行有效编码。

利用霍夫曼编码,可以在传输时对各个值进行压缩,而利用之前传输值的索引列表,我们可以通过传输索引值的方式对重复值进行编码,索引值可用于有效查询和重构完整的标头键值对。

图片来源:Introduction to HTTP/2

作为一种进一步优化方式,HPACK 压缩上下文包含一个静态表和一个动态表:静态表在规范中定义,并提供了一个包含所有连接都可能使用的常用 HTTP 标头字段(例如,有效标头名称)的列表;动态表最初为空,将根据在特定连接内交换的值进行更新。 因此,为之前未见过的值采用静态 Huffman 编码,并替换每一侧静态表或动态表中已存在值的索引,可以减小每个请求的大小。

注:在 HTTP/2 中,请求和响应标头字段的定义保持不变,仅有一些微小的差异:所有标头字段名称均为小写,请求行现在拆分成各个 :method:scheme:authority:path 伪标头字段。

四、服务端推送

HTTP/2 新增的另一个强大的新功能是,服务器可以对一个客户端请求发送多个响应。 换句话说,除了对最初请求的响应外,服务器还可以向客户端推送额外资源,而无需客户端明确地请求。

注:HTTP/2 打破了严格的请求-响应语义,支持一对多和服务器发起的推送工作流,在浏览器内外开启了全新的互动可能性。 这是一项使能功能,对我们思考协议、协议用途和使用方式具有重要的长期影响。

为什么在浏览器中需要一种此类机制呢?一个典型的网络应用包含多种资源,客户端需要检查服务器提供的文档才能逐个找到它们。 那为什么不让服务器提前推送这些资源,从而减少额外的延迟时间呢? 服务器已经知道客户端下一步要请求什么资源,这时候服务器推送即可派上用场。

事实上,如果您在网页中内联过 CSS、JavaScript,或者通过数据 URI 内联过其他资产(请参阅资源内联),那么您就已经亲身体验过服务器推送了。 对于将资源手动内联到文档中的过程,我们实际上是在将资源推送给客户端,而不是等待客户端请求。 使用 HTTP/2,我们不仅可以实现相同结果,还会获得其他性能优势。

五、参考

High Performance Browser Networking

HTTP/2

Introduction to HTTP/2

一、长连接和短连接

长连接和短链接的概念:

  • 短连接:传输完数据后连接立刻关闭。
  • 长连接:传输完数据后不会立刻关闭连接,下次传输数据继续复用这个连接。

很容易看出,长连接和短连接的主要区别就是连接完成后是否会关闭连接,长连接不会在完成后立马关闭。

众所周知,HTTP是基于TCP的,TCP连接的建立和释放需要经过三次握手和四次挥手,这个过程相较于数据传输是极度费时的。而一个web页面往往包含许多个页面请求,因此,使用长连接的优势在于不会频繁创建和释放连接。连接的保持在高并发或者大量请求的情况下能大大减少连接建立的时间,这点对于客户端和服务端来说都能提升性能。

长连接除了可以减少连接建立的时间以外,还有一个重要的用处就是可以检测网络中端到端的故障。因为TCP本身是没有故障检测机制的,默认的keep-alive机制检测时间太长,如果线路的一端异常导致连接没有释放,会导致另一端一直占用连接资源,浪费资源。

二、HTTP的keep-alive

HTTP的长连接保持通过指定Connection头部完成,如果值为keep-alive表明希望建立长连接,而如果值为close则表示短连接。当然,长连接还需要服务端软件也支持,目前广泛使用的apache/nginx等HTTP服务程序都能支持长连接。

在HTTP1.0中,Keep-Alive默认是关闭的,需要手动指定才能开启。而HTTP1.1开始,Keep-Alive默认是开启的。

当开启keep-alive后,同一个TCP连接会发送多个HTTP请求到对方。但是由于HTTP协议的无状态特性,只有一个HTTP连接完成之后才能继续在这个连接上发送下一个HTTP请求,即必须是pipeline模式。

以下是一个keep-alive抓包的示例,在nginx上开启keep-alive,使用浏览器访问web页面,同时使用wireshark抓包。抓到的数据包如下:

以上都是由客户端端口50591发送到服务端80的数据,数据包已经被分为了5段,分别是:

  1. TCP三次握手
  2. HTTP请求+响应
  3. HTTP请求+响应
  4. TCP的keep-alive
  5. TCP四次挥手

其中第二段和第三段都是单独的HTTP请求,说明一个TCP连接中确实可以完成多个HTTP请求。HTTP数据交互过程wireshark已经帮我们解析出来了,很明显就能看出。

同时,通过追踪HTTP流也可以看到HTTP的交互过程:

红色框出来的是第二次HTTP请求,它是在第一个HTTP请求的响应发送完成之后才执行的。

一、关于Basic Authentication

HTTP本身提供了一种基础的认证方式Basic Authentication,使得访问者在访问时需要输入账号密码认证之后才能访问到页面:

如果没有输入密码访问,服务器将会返回401

当服务端开启认证后,通过认证的方式有两种:

  1. 在访问URL的时候主动代码账号和密码信息,如http://user:password@www.baidu.com,其中user是账户名,password是密码。
  2. 在请求头部加上Authorization头部,并将值设置为Basic xxxx,xxxx是账号和密码的base64编码结果。

一般我们使用的是第二种方式:如果网站需要认证,浏览器会自动会弹出登录框,手动输入账号密码后浏览器在头部带上认证信息访问。以下是一个抓包示例:

最下面一行的Authorization是授权信息,最后的bWFxaWFuOnF3ZTEyMw==即是经过base64加密后的账号密码。使用base64命令解码即可得到认证信息:

> printf "bWFxaWFuOnF3ZTEyMw==" | base64 -d
maqian:qwe123

关于认证状态保持

浏览器如何做到保持状态的呢?根据抓包分析发现,认证成功后,浏览器会自动记住当前页面的账号信息。后续的每一个请求,浏览器都自动加上认证头部,无需每次都再输入账号密码,这样就达到了认证状态保持的效果。

二、使用nginx开启basic authentication

nginx默认提供了ngx_http_auth_basic_module模块以支持basic authentication,该指令为:

location / {
    auth_basic "auth test";
    auth_basic_user_file conf/htpasswd;
}

其中auth_basic认证信息的提示语句,他的值可以是一个字符串或者off,如果值是off表示访问无需认证,如果值是一个字符串,表示需要提供认证信息才能访问。并且大部分浏览器会把这个字符串返回到前端,测试发现chrome不会,edge会:

auth_basic_user_file的值是密钥文件的路径,里面保存了所有的账号密码,内容格式为:

# comment
name1:password1
name2:password2:comment
name3:password3

nginx支持以下密码类型:

  1. encrypted with the crypt() function; can be generated using the “htpasswd” utility from the Apache HTTP Server distribution or the “openssl passwd” command;
  2. hashed with the Apache variant of the MD5-based password algorithm (apr1); can be generated with the same tools;
  3. specified by the “{scheme} data” syntax (1.0.3+) as described in RFC 2307; currently implemented schemes include PLAIN (an example one, should not be used), SHA (1.3.13) (plain SHA-1 hashing, should not be used) and SSHA (salted SHA-1 hashing, used by some software packages, notably OpenLDAP and Dovecot).

通常,我们可以使用htpasswdopenssl命令来生成密码,如:

> openssl passwd -crypt qwe123 # qwe123是生成的密码信息
wMEiqshd7n3YQ

指令放置上下文

这个指令可以放置在:

http, server, location, limit_except

三、参考

Module ngx_http_auth_basic_module