每一个web开发者都应该了解的HTTP/2

自从我写了上一篇博文之后,就再也找不到空闲时间写文章了。今天我终于可以抽出时间写一些关于 HTTP 的东西。

我认为每一个 web 开发者都应该对这个支撑了整个 Web 世界的 HTTP 协议有所了解,这样才能帮助你更好的完成开发任务。

在这篇文章中,我将讨论什么是 HTTP,它是怎么产生的,它的地位,以及我们应该怎么使用它。

HTTP 是什么

首先我们要明白 HTTP 是什么。HTTP 是一个基于 TCP/IP
的应用层通信协议,它是客户端和服务端在互联网互相通讯的标准。它定义了内容是如何通过互联网进行请求和传输的。HTTP
是在应用层中抽象出的一个标准,使得主机(客户端和服务端)之间的通信得以通过 TCP/IP 来进行请求和响应。TCP 默认使用的端口是
80,当然也可以使用其它端口,比如 HTTPS 使用的就是 443 端口。

HTTP/0.9 - 单行协议 (1991)

HTTP 最早的规范可以追溯到 1991 年,那时候的版本是 HTTP/0.9,该版本极其简单,只有一个叫做 GET的请求方式。如果客户端要访问服务端上的一个页面,只需要如下非常简单的请求:


  1. GET /index.html 

服务端对应的返回类似如下:


  1. (response body) 
  2. (connection closed) 

就这么简单,服务端捕获到请求后立马返回 HTML 并且关闭连接,在这之中

  • 没有头信息(headers)
  • 仅支持 GET 这一种请求方法
  • 必须返回 HTML

如同你所看到的,当时的 HTTP 协议只是一块基础的垫脚石。

HTTP/1.0 - 1996

在 1996 年,新版本的 HTTP 对比之前的版本有了极大的改进,同时也被命名为 HTTP/1.0。

与 HTTP/0.9 只能返回 HTML 不同的是,HTTP/1.0
支持处理多种返回的格式,比如图片、视频、文本或者其他格式的文件。它还增加了更多的请求方法(如 POST 和
HEAD),请求和响应的格式也相应做了改变,两者都增加了头信息;引入了状态码来定义返回的特征;引入了字符集支持;支持多段类型(multi-part)、用户验证信息、缓存、内容编码格式等等。

一个简单的 HTTP/1.0 请求大概是这样的:


  1. GET / HTTP/1.0 
  2. Host: kamranahmed.info 
  3. User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) 
  4. Accept: */* 

正如你所看到的,在请求中附带了客户端中的一些个人信息、响应类型要求等内容。这些是在 HTTP/0.9 无法实现的,因为那时候没有头信息。

一个对上述请求的响应例子如下所示:


  1. HTTP/1.0 200 OK 
  2. Content-Type: text/plain 
  3. Content-Length: 137582 
  4. Expires: Thu, 05 Dec 1997 16:00:00 GMT 
  5. Last-Modified: Wed, 5 August 1996 15:55:28 GMT 
  6. Server: Apache 0.84 
  7. (response body) 
  8. (connection closed) 

从 HTTP/1.0 (HTTP 后面跟的是版本号)早期开始,在状态码 200 之后就附带一个原因短语(你可以用来描述状态码)。

在这个较新一点的版本中,请求和响应的头信息仍然必须是 ASCII
编码,但是响应的内容可以是任意类型,如图片、视频、HTML、文本或其他类型,服务器可以返回任意内容给客户端。所以这之后,HTTP
中的“超文本(Hyper Text)”成了名不副实。 HMTP (超媒体传输协议(Hypermedia transfer
protocol))可能会更有意义,但是我猜我们还是会一直沿用这个名字。

HTTP/1.0 的一个主要缺点就是它不能在一个连接内拥有多个请求。这意味着,当客户端需要从服务器获取东西时,必须建立一个新的 TCP
连接,并且处理完单个请求后连接即被关闭。需要下一个东西时,你必须重新建立一个新的连接。这样的坏处在哪呢?假设你要访问一个有 10 张图片,5
个样式表(stylesheet)和 5 个 JavaScript 的总计 20
个文件才能完整展示的一个页面。由于一个连接在处理完成一次请求后即被关闭,所以将有 20
个单独的连接,每一个文件都将通过各自对应的连接单独处理。当连接数量变得庞大的时候就会面临严重的性能问题,因为 TCP
启动需要经过三次握手,才能缓慢开始。

三次握手

三次握手是一个简单的模型,所有的 TCP 连接在传输应用数据之前都需要在三次握手中传输一系列数据包。

  • SYN - 客户端选取一个随机数,我们称为 x,然后发送给服务器。
  • SYN ACK - 服务器响应对应请求的 ACK 包中,包含了一个由服务器随机产生的数字,我们称为 y,并且把客户端发送的 x+1,一并返回给客户端。
  • ACK - 客户端在从服务器接受到 y 之后把 y 加上 1 作为一个 ACK 包返回给服务器。

一旦三次握手完成后,客户端和服务器之间就可以开始交换数据。值得注意的是,当客户端发出最后一个 ACK 数据包后,就可以立刻向服务器发送应用数据包,而服务器则需要等到收到这个 ACK 数据包后才能接受应用数据包。

请注意,上图有点小问题,客户端发回的最后一个 ACK 包仅包含 y+1,上图应该是 ACK:y+1 而不是 ACK:x+1,y+1

然而,某些 HTTP/1.0 的实现试图通过新引入一个称为 Connection: keep-alive 的头信息来克服这一问题,这个头信息意味着告诉服务器“嘿,服务器,请不要关闭此连接,我还要用它”。但是,这并没有得到广泛的支持,问题依然存在。

除了无连接之外,HTTP
还是一个无状态的协议,即服务器不维护有关客户端的信息。因此每个请求必须给服务器必要的信息才能完成请求,每个请求都与之前的旧的请求无关。所以,这增加了推波助澜的作用,客户端除了需要新建大量连接之外,在每次连接中还需要发送许多重复的数据,这导致了带宽的大量浪费。

HTTP/1.1 - 1999

HTTP/1.0 经过仅仅 3 年,下一个版本,即 HTTP/1.1 就在 1999 年发布了,改进了它的前身很多问题,主要的改进包括:

  • 增加了许多 HTTP 请求方法,包括 PUT、PATCH、HEAD、OPTIONS、DELETE。
  • 主机标识符 Host 在 HTTP/1.0 并不是必须的,而在 HTTP/1.1 是必须的。
  • 如上所述的持久连接。在 HTTP/1.0 中每个连接只有一个请求并在该请求结束后被立即关闭,这导致了性能问题和增加了延迟。
    HTTP/1.1 引入了持久连接,即连接在默认情况下是不关闭并保持开放的,这允许多个连续的请求使用这个连接。要关闭该连接只需要在头信息加入
    Connection: close,客户通常在最后一个请求里发送这个头信息就能安全地关闭连接。
  • 新版本还引入了“管线化(pipelining)”的支持,客户端可以不用等待服务器返回响应,就能在同一个连接内发送多个请求给服务器,而服务器必须以接收到的请求相同的序列发送响应。但是你可能会问了,客户端如何知道哪里是第一个响应下载完成而下一个响应内容开始的地方呢?要解决这个问题,头信息必须有
    Content-Length,客户可以使用它来确定哪些响应结束之后可以开始等待下一个响应。

。值得注意的是,为了从持久连接或管线化中受益, 头部信息必须包含 Content-Length,因为这会使客户端知道什么时候完成了传输,然后它可以发送下一个请求(持久连接中,以正常的依次顺序发送请求)或开始等待下一个响应(启用管线化时)。

。但是,使用这种方法仍然有一个问题。那就是,如果数据是动态的,服务器无法提前知道内容长度呢?那么在这种情况下,你就不能使用这种方法中获益了吗?为了解决这个问题,HTTP/1.1
引进了分块编码。在这种情况下,服务器可能会忽略 Content-Length
来支持分块编码(更常见一些)。但是,如果它们都不可用,那么连接必须在请求结束时关闭。

  • 在动态内容的情况下分块传输,当服务器在传输开始但无法得到 Content-Length
    时,它可能会开始按块发送内容(一块接一块),并在传输时为每一个小块添加
    Content-Length。当发送完所有的数据块后,即整个传输已经完成后,它发送一个空的小块,比如设置 Content-Length 为 0
    ,以便客户端知道传输已完成。为了通知客户端块传输的信息,服务器在头信息中包含了 Transfer-Encoding: chunked。
  • 不像 HTTP/1.0 中只有 Basic 身份验证方式,HTTP/1.1 包括摘要验证方式(digest authentication)和代理验证方式(proxy authentication)。
  • 缓存。
  • 范围请求(Byte Ranges)。
  • 字符集。
  • 内容协商(Content Negotiation)。
  • 客户端 cookies。
  • 支持压缩。
  • 新的状态码。
  • 等等。

我不打算在这里讨论所有 HTTP/1.1 的特性,因为你可以围绕这个话题找到很多关于这些的讨论。我建议你阅读 HTTP/1.0 和 HTTP/1.1 版本之间的主要差异,希望了解更多可以读原始的 RFC。

HTTP/1.1 在 1999
年推出,到现在已经是多年前的标准。虽然,它比前一代改善了很多,但是网络日新月异,它已经垂垂老矣。相比之前,加载网页更是一个资源密集型任务,打开一个简单的网页已经需要建立超过
30 个连接。你或许会说,HTTP/1.1 具有持久连接,为什么还有这么多连接呢?其原因是,在任何时刻 HTTP/1.1
只能有一个未完成的连接。 HTTP/1.1
试图通过引入管线来解决这个问题,但它并没有完全地解决。因为一旦管线遇到了缓慢的请求或庞大的请求,后面的请求便被阻塞住,它们必须等待上一个请求完成。为了克服
HTTP/1.1 的这些缺点,开发人员开始实现一些解决方法,例如使用 spritesheets、在 CSS 中编码图像、单个巨型 CSS /
JavaScript 文件、域名切分等。

SPDY - 2009

谷歌走在业界前列,为了使网络速度更快,提高网络安全,同时减少网页的等待时间,他们开始实验替代的协议。在 2009 年,他们宣布了 SPDY。

SPDY 是谷歌的商标,而不是一个缩写。

显而易见的是,如果我们继续增加带宽,网络性能开始的时候能够得到提升,但是到了某个阶段后带来的性能提升就很有限了。但是如果把这些优化放在等待时间上,比如减少等待时间,将会有持续的性能提升。这就是
SPDY 优化之前的协议的核心思想,减少等待时间来提升网络性能。

对于那些不知道其中区别的人,等待时间就是延迟,即数据从源到达目的地需要多长时间(单位为毫秒),而带宽是每秒钟数据的传输量(比特每秒)。

SPDY 的特点包括:复用、压缩、优先级、安全性等。我不打算展开 SPDY 的细节。在下一章节,当我们将介绍 HTTP/2,这些都会被提到,因为 HTTP/2 大多特性是从 SPDY 受启发的。

SPDY 没有试图取代 HTTP,它是处于应用层的 HTTP 之上的一个传输层,它只是在请求被发送之前做了一些修改。它开始成为事实标准,大多数浏览器都开始支持了。

2015年,谷歌不想有两个相互竞争的标准,所以他们决定将其合并到 HTTP 协议,这样就导致了 HTTP/2 的出现和 SPDY 的废弃。

HTTP/2 - 2015

现在想必你明白了为什么我们需要另一个版本的 HTTP 协议了。 HTTP/2 是专为了低延迟地内容传输而设计。主要特点和与 HTTP/1.1 的差异包括:

  • 使用二进制替代明文
  • 多路传输 - 多个异步 HTTP 请求可以使用单一连接
  • 报头使用 HPACK 压缩
  • 服务器推送 - 单个请求多个响应
  • 请求优先级
  • 安全性

1. 二进制协议

HTTP/2 通过使其成为一个二进制协议以解决 HTTP/1.x 中存在的延迟问题。作为一个二进制协议,它更容易解析,但可读性却不如 HTTP/1.x。帧(frames)和流(stream)的概念组成了 HTTP/2 的主要部分。

帧和流

现在 HTTP 消息是由一个或多个帧组成的。HEADERS 帧承载了元数据(meta data),DATA
帧则承载了内容。还有其他类型的帧(HEADERS、DATA、RST_STREAM、SETTINGS、PRIORITY 等等),这些你可以通过
HTTP/2 规范来了解。

每个 HTTP/2 请求和响应都被赋予一个唯一的流
ID,并切分成帧。帧就是一小片二进制数据。帧的集合称为流,每个帧都有个标识了其所属流的流
ID,所以在同一个流下的每个帧具有共同的报头。值得注意的是,除了流 ID 是唯一的之外,由客户端发起的请求使用了奇数作为流
ID,从来自服务器的响应使用了偶数作为流 ID。

除了 HEADERS 帧和 DATA 帧,另一个值得一提的帧是
RST_STREAM。这是一个特殊的帧类型,用来中止流,即客户可以发送此帧让服务器知道,我不再需要这个流了。在 HTTP/1.1
中让服务器停止给客户端发送响应的唯一方法是关闭连接,这样造成了延迟增加,因为之后要发送请求时,就要必须打开一个新的请求。而在
HTTP/2,客户端可以使用 RST_STREAM 来停止接收特定的数据流,而连接仍然打开着,可以被其他请求使用。

2. 多路传输

因为 HTTP/2 是一个二进制协议,而且如上所述它使用帧和流来传输请求与响应,一旦建立了 TCP 连接,相同连接内的所有流都可以同过这个
TCP 连接异步发送,而不用另外打开连接。反过来说,服务器也可以使用同样的异步方式返回响应,也就是说这些响应可以是无序的,客户端使用分配的流
ID 来识别数据包所属的流。这也解决了 HTTP/1.x 中请求管道被阻塞的问题,即客户端不必等待占用时间的请求而其他请求仍然可以被处理。

3. HPACK 请求头部压缩

RFC
花了一篇文档的篇幅来介绍针对发送的头信息的优化,它的本质是当我们在同一客户端上不断地访问服务器时,许多冗余数据在头部中被反复发送,有时候仅仅是
cookies 就能增加头信息的大小,这会占用许多宽带和增加传输延迟。为了解决这个问题,HTTP/2 引入了头信息压缩。

不像请求和响应那样,头信息中的信息不会以 gzip 或者 compress
等格式压缩。而是采用一种不同的机制来压缩头信息,客户端和服务器同时维护一张头信息表,储存了使用了哈夫曼编码进行编码后的头信息的值,并且后续请求中若出现同样的字段则忽略重复值(例如用户代理(user
agent)等),只发送存在两边信息表中它的引用即可。

我们说的头信息,它们同 HTTP/1.1 中一样,并在此基础上增加了一些伪头信息,如 :scheme,:host 和 :path。

4. 服务器推送

服务器推送是 HTTP/2
的另一个巨大的特点。对于服务器来说,当它知道客户端需要一定的资源后,它可以把数据推送到客户端,即使客户端没有请求它。例如,假设一个浏览器在加载一个网页时,它解析了整个页面,发现有一些内容必须要从服务端获取,然后发送相应的请求到服务器以获取这些内容。

服务器推送减少了传输这些数据需要来回请求的次数。它是如何做到的呢?服务器通过发送一个名字为 PUSH_PROMISE
特殊的帧通知到客户端“嘿,我准备要发送这个资源给你了,不要再问我要了。”这个 PUSH_PROMISE
帧与要产生推送的流联系在一起,并包含了要推送的流 ID,也就是说这个流将会被服务器推送到客户端上。

5. 请求优先级

当流被打开的时候,客户端可以在 HEADERS 帧中包含优先级信息来为流指定优先级。在任何时候,客户端都可以发送 PRIORITY 帧来改变流的优先级。

如果没有任何优先级信息,服务器将异步地无序地处理这些请求。如果流分配了优先级,服务器将基于这个优先级来决定需要分配多少资源来处理这个请求。

6. 安全性

在是否强制使用 TLS 来增加安全性的问题上产生了大范围的讨论,讨论的结果是不强制使用。然而大多数厂商只有在使用 TLS 时才能使用
HTTP/2。所以 HTTP/2 虽然规范上不要求加密,但是加密已经约定俗成了。这样,在 TLS 之上实现 HTTP/2
就有了一些强制要求,比如,TLS 的最低版本为 1.2,必须达到某种级别的最低限度的密钥大小,需要布署 ephemeral 密钥等等。

到现在 HTTP/2 已经完全超越了 SPDY,并且还在不断成长,HTTP/2 有很多关系性能的提升,我们应该开始布署它了。

如果你想更深入的了解细节,请访问该规范的链接和 HTTP/2 性能提升演示的链接。请在留言板写下你的疑问或者评论,最后如果你发现有错误,请同样留言指出。

这就是全部了,我们之后再见~

作者:Kamran Ahmed

来源:51CTO

时间: 2024-10-26 21:27:51

每一个web开发者都应该了解的HTTP/2的相关文章

Web开发者不可不知的15条编码原则

 HTML已经走过了近20的发展历程.从HTML4到XHTML,再到最近十分火热的HTML5,它几乎见证了整个互联网的发展.但是,即便到现在,有很多基础的概念和原则依然需要开发者高度注意.下面,向大家介绍这些应该遵循的开发原则. 1.善用DIV来布局 当开发一个Web页面时,要考虑第一件事就是区分页面重点.将这些内容用DIV标签包含起来,页面的代码会呈现出整洁.缩进良好的风格. <div id="header"></div> <div id="b

你应该成为 Web 开发者的 5 大理由

现在的职业选择是如此之繁多,很多人往往不知道自己要干什么工作.作为一个已经在软件行业淫浸差不多14年时间的资深人士,我发现web开发这个职 业选择,渐渐成为了大众普遍的首选.虽然,web开发在过去10年间发生了巨大的变化,但是web应用程序在市场范围和销售前景上的潜力依然超过桌面应用 程序.下面我将给出为什么你应该成为web开发者的5大理由. 1.可以独立工作 拥有良好的团队合作精神是就职于任何企业所需的必备技能,但是如果涉及到私人业务或者想赚取一些外快,那么除了全职工作,能够独立工作就变得很重

[深度好文]想成为一个高效的Web开发者吗?来看看大牛分享的经验吧~外加一些自己的理解

前言: 无意间浏览到此篇文章,发现这篇文章无论是对于新手程序员,还是学过几年的程序员,都是挺有帮助的.于是,在此分享,后面也有我自己的一些理解,希望能帮到更多的朋友. 作为一个软(ku)件(bi)工(de)程(ma)师(nong),你有没有觉得做什么事都没时间?没时间学习新东西,没时间去回顾.整理原来写的烂代码,没时间写单元测试,没时间给接管你项目的家伙写文档,没时间思考,没时间喘气,没!时!间! 额--如果你肯花点时间看看这篇文章,我相信你会明白应该把时间花在哪. 我曾以为成为一个技术大牛的唯

给微信好友发一个web页好友转发描述,图片,标题都不一样

问题描述 给微信好友发一个web页好友转发描述,图片,标题都不一样 给微信好友发一个web页好友转发描述,图片,标题都不一样给微信好友发一个web页好友转发描述,图片,标题都不一样

asp.net 高并发下 一个web页面同时调用2个方法,这2个方法都是查询数据库,查询的结果乱掉了

问题描述 asp.net 高并发下 一个web页面同时调用2个方法,这2个方法都是查询数据库,查询的结果乱掉了 问题描述:这个问题只有在高并发情况下才会出现. 具体如下: 后台.cs有2个方法,这2个方法在前端.aspx通过流的方式调用(<%%>),然而DaoJiShi()这个方法获得的是gd()方法的数据,gd()获得的是DaoJiShi()方法的数据,那么在相应的方法里遍历DaTable的时候就会报列不存的情况. 这个问题让我很困惑,这样写已经很多项目了,都没这个问题.这个项目有时候会高并

那些让Web开发者们深感意外的事情

作为Web开发者,对自己的行业前景,人人都有自己的看法,然而,任何行业都有出人意料的地方.著名的Web开发设计博客noupe.com曾向他们的读者做了一个调查,请他们列举Web开发领域那些让他们感到意外的事情,收到了各种各样的反馈,本文就是这次调查的结果与分析. 一个真正的符合Web标准的IE 这个消息让我们振奋,在经过9个版本更替后,我们终于看到了(或者说即将看到)一个真正符合Web标准的IE浏览器.在Web开发设计界,人们对IE的忍耐到达了极限,最终,很多人宣布不再支持IE的某些版本,IE9

优秀Web开发者提升开发能力必知的10件事

"开发工作不仅仅只是写代码"这句话来自3EV网站的Dan Frost,他在一篇文章中阐述了开发过程中应该注意的一些事项.原文内容如下: 开发者是创造数字世界的主力军,他们不应该只扮演编程工具的角色,而应该对开发工作有更高的要求.那么,开发者可以从哪些方面提高开发能力呢?下面我就谈一下我的想法.我的建议可能不全面,但希望能够给你带来一些帮助. 1. 不要只盯着代码 如今人人都会写代码.很多业余爱好者也可以搭建网站.编写应用程序,编程已经不再稀奇. 随着网络的普及,许多人只需通过自学就会编

优秀Web开发者必知的10个职业常识

众所周知,Web开发行业的持续发展和需求的不断变化给开发人员提出了新的要求与挑战.就网站而言,用户期望网站内容能更加丰富多彩.页面更加美观并且能提供良好的用户体验.如果Web开发人员一直止步不前,那终将会被淘汰. 站的高看到远,希望下面这十条能帮助你在开发这条道路上走的更远. 树立好印象 无论在何种场合,好的印象往往能使人记忆深刻.作为开发人员,忠于自己很重要,但与客户交流过程中,切勿向客户表达一些不切实际的想法或提出一些过分的要求,有时候一个坏印象就会流失很多订单甚至会给你的名声和事业带来很大

[译] 写给“老派” Web 开发者的“现代” JavaScript 指南

本文讲的是[译] 写给"老派" Web 开发者的"现代" JavaScript 指南, 用 JavaScript 学习 JavaScript.图片来自 learnyounode. 有这样一种守旧的后端 web 开发者,他们很久以前就掌握了诸如 Perl .Python.PHP 或 Java Server Pages 一类的东西,甚至还掌握了 Rails 或者 Django.他们使用巨大的关系型数据库构建 JSON API 服务,呃甚至是 XML. 他是个后端开发者,