Linux 运维手册之 HTTP 服务

分类:Linux 评论: 0

HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP 是 WWW(World Wide Web,全球广域网)万维网的数据通信基础。

HTTP 协议简介

发展起源

设计 HTTP 协议最初的目的是为了提供一种发布和接收 HTML 页面的方法,现在已然是短连接通信的事实工业标准。通过 HTTP 或者 HTTPS 协议请求的资源由 URI (Uniform Resource Identifiers,统一资源标识符)来标识。

HTTP 协议是由 蒂姆·伯纳斯-李(Tim Berners-Lee)于 1989 年在 CERN (European Organization
for Nuclear Research,欧洲核子研究组织)所发起。HTTP 的标准制定由 W3C (World Wide Web Consortium,万维网协会)和 IETF (Internet Engineering Task Force,互联网工程任务组)进行协调,最终发布了一系列的 RFC(Request For Comments,请求评论)可以理解为“协议规范”。

发展历程

版本演进

超文本传输协议已经演化出了很多版本,它们中的大部分都是向下兼容的。在 RFC 2145 中描述了 HTTP 版本号的用法。客户端在请求的开始告诉服务器它采用的协议版本号,而后者则在响应中采用相同或者更早的协议版本。

HTTP/0.x

这是 HTTP 于 1991 年推出的首个可用协议标准,此版本协议内容很简单,支持的方法更是廖少,仅支持 GET 一种方法,使用 80 端口,服务器仅可响应 HTML 格式的数据,发送完毕后关闭 TCP 连接。

序号 方法 引入版本 描述
1 GET HTTP/0.9 请求指定的页面,并返回页面内容

HTTP/1.x

在 1996 年 5 月 HTTP 协议迎来一次重大升级:HTTP/1.0 发布(详见 RFC1945)。首先传输内容不再受限于 HTML,图片、二进制文件等也可传输,这为互联网的大发展奠定了基础。其次方法不再仅有 GET 方法,额外支持了 POST 和 HEAD 方法,增加了于浏览器间的互动手段。

序号 方法 引入版本 描述
2 HEAD HTTP/1.0 与 GET 相同,但只返回 HTTP 报头,返回的响应并不包含页面内容。
3 POST HTTP/1.0 向指定的资源提交要被处理的数据(提交表单,上传文件),数据将被包裹在请求体中。

在 1999 年 6 月,HTTP/1.1 发布(详见 RFC2616),【实际上 HTTP/1.0 发布后半年的 1997 年 1 月,HTTP/1.1 草案就已提交】进一步完善了 HTTP 协议,此版本一直沿用至今,截至目前仍是互联网中主流版本。

小贴士:在此版本中添加了很多特性,解决了 HTTP/1.0 中的缺点,以下选择部分重点特性进行说明。

持久连接,在 HTTP/1.0 中每个请求都会发起一个 TCP 连接,发送完成后立即关闭,因为 TCP 协议复杂的握手流程导致每个连接的新建成本很高(主要指延迟),在 HTTP/1.1 中,TCP 连接默认不中断,可被多个请求复用,客户端和服务端发现对方长时间无活动后才会关闭连接,不过,规范的做法是:客户端在最后一个请求时,发送 Connection: close 标识,要求服务器关闭连接,这个特性极大提高了连接的利用率,但是随之而来的问题就是会明显提高服务端的资源占用,部分请求即使请求并不活跃也会占用一个或多个 TCP 连接。

内容长度,因为持久连接特性的加入,引发了一个新问题:就是一个连接被多个请求复用,客户端无法确认当前的数据属于哪一个请求,因此引入了 Content-Length 字段,用来声明本次回应的数据长度,以区分不同的请求内容。

分块传输,使用内容长度字段的前提条件是,服务器返回响应之前,必须知道响应的数据长度。因此对于一些很耗时的动态操作来说,这意味着服务器要等到所有操作完成,才能发送数据,显然这样的效率不高。于是引入了分块传输编码 Transfer-Encoding: chunked 字段,产生一块数据,就发送一块(即“数据流模式”),在 HTTP/1.1 中可以不指定 Content-Length 字段,只要请求或回应的头信息有分块传输编码字段,就表明接下来的响应将由数量未定的数据块组成,在每个非空的数据块之前,会有一个16进制的数值,表示这个块的长度,最后使用一个大小为零的块表示本次响应数据发送完毕。

队头阻塞,HTTP/1.1 的缺点也因为 TCP 连接复用导致同一个连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。如果前面的回应特别慢,后面就会有许多请求排队等着,这称为"队头堵塞"(Head-of-line blocking)。

主机字段,客户端请求的头信息新增了主机 Host: example.com 字段,用来指定请求服务器的域名,有了主机字段,就可以将请求发往同一台服务器上的不同网站,为虚拟主机的兴起打下了基础。

此外还引入了更多的请求方法:

序号 方法 引入版本 描述
4 PUT HTTP/1.1 向指定资源位置上传其最新内容。
5 DELETE HTTP/1.1 请求服务端删除指定资源。
6 CONNECT HTTP/1.1 把请求连接转换到透明的 TCP/IP 通道,通常用于SSL加密服务器的链接。
7 OPTIONS HTTP/1.1 返回服务器支持的 HTTP 方法。
8 TRACE HTTP/1.1 回显服务器收到的请求,用于诊断和测试。

小贴士:至此,HTTP/1.1 中定义的八种方法(也称为动作)介绍完毕,这些新增的方法扩充了操作指定资源的方式,完善了使用的体验。方法名称是区分大小写的。当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码 405 (Method Not Allowed) ,当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码 501 (Not Implemented) 。一般情况下服务器至少应该实现 GET 和 HEAD 方法,其他方法皆为可选。此外,除了上述方法,特定的 HTTP 服务器还能够扩展自定义的方法。例如:在 2010 年引入的 PATCH 方法(详见 RFC5789)用于将局部修改应用到资源,类似于补丁。

HTTP/2

在 2014 年 12 月,互联网工程任务组的 HTTPBIS (Hypertext Transfer Protocol Bis)工作组将 HTTP/2 标准提议递交至 IESG 进行讨论,于 2015 年 2 月 17 日被批准。直至 2015 年 5 月 HTTP/2 发布(详见 RFC7540)取代 HTTP/1.1 成为 HTTP 的实现标准。

说到 HTTP/2 协议就不得不提到谷歌的 SPDY 协议,在 2009 年谷歌公开了自行研发的 SPDY 协议(含义及发音皆为 SPeeDY)主要解决 HTTP/1.1 效率不高的问题。世界上最大的视频网站 YouTube 最先进行了 SPDY 协议的尝鲜,这个协议在 Chrome 浏览器上证明可行以后,就被当作 HTTP/2 的基础,主要特性都在 HTTP/2 之中得到继承。

小贴士:在 2015 年,互联网标准委员会不打算再发布 HTTP/2 子版本了,于是 HTTP/2 不写作 HTTP/2.0 ,下一个新版本将是 HTTP/3。

在此版本中添加了很多特性,解决了 HTTP/1.1 中的缺点,以下选择部分重点特性进行说明。

纯二进制,在 HTTP/1.1 中,头信息为文本格式,使用 ASCII 编码,数据体则不做限制,既可为文本,也可为二进制格式。在 HTTP/2 中则彻底变为二进制,头信息和数据体都是二进制,并且统称为"帧"(frame):头信息帧和数据帧。二进制协议的一个好处是,可以定义额外的帧。HTTP/2 定义了近十种帧,为将来的高级应用打好了基础。如果使用文本实现这种功能,解析数据将会变得非常麻烦,二进制解析则方便得多。

多路复用,HTTP/2 复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了"队头堵塞"。举例来说,在一个 TCP 连接里,服务器同时收到了 A 请求和 B 请求,于是先回应 A 请求,结果发现处理过程非常耗时,于是就发送 A 请求中已经处理好的部分,接着回应 B 请求,完成后,再发送 A 请求剩下的部分,这种双向的、实时的通信,就叫做多工(Multiplexing)。

说明:举一个实际的例子来解释这个图,每个 TCP 连接为一个班级,连接中的每一个帧可看做是一个学生,流可以认为是一些学生组成的小组,一个班级(一个连接)内学生被分为若干个小组,每一个小组分配不同的具体任务。

  • HTTP/1.0 一次请求对应一个响应,建立一个连接,用完关闭;也就是每个任务安排一个小组进行执行,每个小组单独成立一个班级,即每个班级安排一个任务,只做单一工作。
  • HTTP/1.1 若干个小组任务排队串行化单线程处理,后面小组任务等待前面小组任务完成才能获得执行机会,一旦有任务处理超时,后续任务只能被阻塞,毫无办法,即队头堵塞。
  • HTTP/2 一个班级内的多个小组任务可同时并行(严格意义上是并发)在班级内执行。一旦某个小组任务耗时严重,就会被置后,其他小组提前,但不会影响到其它小组任务正常执行。

数据流,因为 HTTP/2 的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的请求回应。因此,必须要对数据包做标记,指出它属于哪一个请求。HTTP/2 将每个请求或回应的所有数据包,称为一个数据流(stream)。每个数据流都有一个独一无二的编号。数据包发送的时候,都必须标记数据流 ID,用来区分它属于哪个数据流。另外还规定,客户端发出的数据流 ID 一律为奇数,服务端发出的数据流 ID 一律为偶数。数据流发送途中,客户端和服务端都可以发送信号(RST_STREAM帧),取消这个数据流。HTTP/1.1 取消数据流的唯一方法,就是关闭 TCP 连接。这就是说,HTTP/2 可以取消某一次请求,同时保证 TCP 连接还打开着,可以被其他请求使用。客户端还可以指定数据流的优先级:优先级越高,服务端优先进行处理和回应。

头信息压缩,由于 HTTP 协议是无状态协议,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,比如 cookie 和 User Agent,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。HTTP/2 对这一点做了优化,引入了头信息压缩机制。一方面,头部信息压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就极大提高速度,且节约带宽。

小贴士:为什么说 HTTP 协议是无状态协议?无状态是指协议对于事务处理没有记忆功能。缺少状态意味着,假如后面的处理需要前面的信息,则前面的信息必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要前面信息时,应答就较快。直观地说,就是每个请求都是独立的,与前面的请求和后面的请求都是没有直接联系的,就是因为如此,所以 HTTP 协议中引入了 cookie 和 session 等来实现有状态的连接。

服务器推送,HTTP/2 允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送(Server Push)。常见场景是客户端请求一个网页,这个网页里面包含很多静态资源。正常情况下,客户端必须收到网页后,解析 HTML 源码,发现有静态资源,再发出静态资源请求。在 HTTP/2 中服务器可以预期到客户端请求网页后会继续请求静态资源,所以就主动把这些静态资源随着网页一起发给客户端。

安全性提升,在 HTTP/1.1 及之前,协议自身无加密,若实现加密需要借助 TLS 层实现,也称 HTTPS ,在 HTTP/2 中强制要求必须包含 TLS 安全传输层,如图(引用自微信公众号前端工匠);相对的在 HTTP/1.1 之中 TLS 为可选,非必选项。

HTTP/3

在说 HTTP/3 之前先说一说 HTTP/2 的不足,每次版本更新都是为了解决前一代存在的问题,或者扩充其特性,由于 HTTP/2 及之前的协议都是基于 OSI 七层模型中第四层传输层协议 TCP 协议实现的。因此 TCP 协议存在的问题在 HTTP/2 中也都存在。

小贴士:此处提到了 OSI 七层模型,这七层分别为:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。

具体的请看下图:

在日常使用中,一般不使用七层模型,都将其简化为五层模型,将类似的或者不重要的层合并,于是产生了下图:

HTTP/2 的缺点主要有如下两点:

小贴士:为什么是三次握手?因为三次握手是建立可靠连接(双向确认)所需的最少次数。

很显然,这些问题主要都是底层支撑的 TCP 协议造成的。HTTP/2 都是使用 TCP 协议来传输的,而如果使用 HTTPS (HTTP over TLS) 的话,还需要使用 TLS 协议进行安全传输,而使用 TLS 也需要一个握手过程,这样就需要有两个握手延迟过程:

小贴士:RTT(Round-Trip Time,往返时延),是指数据从网络一端传到另一端所需的时间。

总之,在真正传输数据之前,就已经消耗了 3~4 个 RTT 周期,以在中国大陆访问美国的本土网站为例,中美直连的链路一般至少需要 100ms 时间(根据所处大陆地域不同存在些许差别,在北上广等国际出口城市)。因此在传输网络数据前,仅仅是建立 HTTPS 连接的 RTT 时间已经消耗了近 500ms 的时间。

上文在介绍 HTTP/2 时就提到了,HTTP/2 的一大改进就是多路复用,让多个请求存在于同一个 TCP “管道”中,如果网络连接差,比如存在大量丢包时,此时 HTTP/2 的性能还不如 HTTP/1 ,因为 TCP 协议的可靠特点,当出现丢包时,会触发重传机制,丢失的包会重新传输一次或者多次,直到接收方确认为止,当 HTTP/2 出现丢包时,整个 TCP 连接都要开始等待重传,就会阻塞此 TCP 管道中的所有请求。而对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。

实际上谷歌在开发 SPDY 协议时就意识到它存在的不足,因此同时开发了一个基于另一个传输层协议 UDP 的的 QUIC 协议,此协议就是 HTTP/3 的基础模型。在 2018 年 11 月互联网正式批准了 HTTP-over-QUIC 成为下一代 HTTP/3 协议,其在 HTTP/2 的基础上实现了很多飞跃,较完美的解决了队头阻塞问题。

由于 HTTP/3 基于用户数据报协议(UDP,User Datagram Protocol),此协议相比较于 TCP 可靠连接,最大的特点就是无状态、不可靠。因此可以实现 O 个 RTT 周期完成连接并开始传输数据,也就解决了队头阻塞问题。


常用 HTTP 服务

常用网页服务

市场占有率

Developer June 2018 Percent July 2018 Percent Change
Apache 76,721,597 35.23% 76,312,577 34.60% -0.63
Microsoft 56,030,491 25.73% 57,395,894 26.02% 0.29
NGINX 50,860,718 23.35% 50,246,244 22.78% -0.57
Google 1,909,658 0.88% 1,948,858 0.88% 0.01

表格数据来源


附录

状态码表

更多阅读

参考链接

回复