超文本传输协议 (HTTP) 是 Internet 上最普遍和广泛采用的应用程序协议之一: 它是客户端和服务器之间的通用语言, 可实现现代 web.
本文转载自微信公众号「Java 大厂面试官」, 作者 laker . 转载本文请联系 Java 大厂面试官公众号.
目录
什么是 HTTP 协议
HTTP 协议发展简史
HTTP 0.9 单行协议
HTTP 1.0 构建扩展性
HTTP 1.1 标准化协议
HTTP 2.0 更高性能的协议
问题
1.http1.1 长连接 keep-alive 和 http2.0 的多路复用有什么区别?
2. 什么是流水线(管道机制)?
3. 现代浏览器在与服务器建立了一个 TCP 连接后是否会在一个 HTTP 请求完成后断开?
4. 一个 TCP 连接可以对应几个 HTTP 请求?
5. 一个 TCP 连接中 HTTP 请求发送可以一起发送么(比如一起发三个请求, 再三个响应一起接收)?
什么是 HTTP 协议
超文本传输协议 (HTTP) 是 Internet 上最普遍和广泛采用的应用程序协议之一: 它是客户端和服务器之间的通用语言, 可实现现代 Web. 从简单的一个关键字和文档路径开始, 它不仅成为浏览器的选择协议, 而且几乎成为所有与 Internet 连接的软件和硬件应用程序的选择协议.
HTTP 具有四个版本:
- HTTP / 0.9
- HTTP / 1.0
- HTTP / 1.1
- HTTP / 2.0
时至今日, 常用的版本是 HTTP / 1.1, 未来的发展版本是 HTTP / 2.0.
HTTP 协议发展简史
HTTP 0.9 单行协议
HTTP 协议的第一个简单实现, 仅支持获取网页. 一开始都没有版本号, 为了区分其他版本, 后来被称为 0.9.HTTP / 0.9 非常简单: 请求由一行组成, 并以唯一的方法 GET 开头, 后跟资源的路径.
GET /mypage.html
A very simple HTML page
从 1991 年开始, HTTP 就有了自己的生命, 并在接下来的几年中迅速发展.
缘分呐, 我也是 1991 年破壳.
核心功能:
简单的客户端 - 服务器, 请求 - 响应协议.
支持的方法: 只有 GET.
ASCII 协议, 通过 TCP / IP 链接运行.
设计用于传输超文本文档(HTML).
每次请求后, 服务器和客户端之间的连接都会关闭.
没有 HTTP 标头(无法传输其他内容类型文件), 没有状态 / 错误代码, 没有 URL, 没有版本控制
这个我表示我生活在现代, 目前我所经历的都没碰到过... 直接跳过, 不研究
HTTP 1.0 构建扩展性
HTTP / 0.9 协议非常有限, 浏览器和服务器都迅速增加扩展性, 使其变的更加通用.
1996 年 5 月, HTTP 工作组 (HTTP-WG) 发布了 RFC 1945, 此版本添加了许多补充数据字段, 称为规范的标头. 这允许其他信息在客户端和服务器之间以及请求和后续页面之间传递.
- GET /mypage.HTML HTTP/1.0
- User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)
- 200 OK
- Date: Tue, 15 Nov 1994 08:12:31 GMT
- Server: CERN/3.0 libwww/2.17
- Content-Type: text/HTML
- A page with an image
核心功能:
提供的标头字段包括有关请求和响应的丰富元数据(HTTP 版本号, 状态代码, 内容类型)
响应: 不限于超文本(Content-Type 标头提供了传输普通 HTML 文件以外的文件的功能, 例如脚本, 样式表, 媒体)
支持的方法: GET,HEAD,POST
请求可能包含多个换行符分隔的标头字段.
响应对象以响应状态行为前缀.
响应对象具有自己的一组由换行符分隔的标头字段.
每次请求后, 服务器和客户端之间的连接都会关闭.
nginx 默认还是 http1.0 协议, 刚好前几天碰到个问题[1.0 不支持分块传输] , 具体可参见:
nginx 下载文件 upstream sent invalid chunked response while reading upstream 错误
HTTP 1.1 标准化协议
HTTP / 1.1 标准解决了早期版本中的许多协议歧义, 并引入了许多关键的性能优化:
保持活动连接
分块编码传输
字节范围请求
附加缓存机制
传输编码
请求流水线(管道机制)
如今, 大多数浏览器都支持 1.0 和 1.1 的实现, 新浏览器默认使用 1.1, 但是如果有需要的话, 还可以回退到早期版本. RFC 定义明确指出的一件事是, HTTP 协议的所有实现都应向后兼容. 也就是说, 实现 HTTP /1.1 规范的浏览器应该能够从服务器接收 1.0 响应. 相反, 服务器端的 1.1 实现也应该能够响应来自 1.0 浏览器的请求.
将 HTTP 转变为正式的 IETF Internet 标准的工作与围绕 HTTP / 1.0 的文档编制工作并行进行, 大约持续了四年时间: 1995 年至 1999 年.
实际上, 第一个正式的 HTTP / 1.1 标准是在 RFC 2068, 于 1997 年 1 月正式发布, 距 HTTP / 1.0 发布大约六个月. 然后, 两年半之后的 1999 年 6 月, 许多改进和更新被纳入该标准, 并以 RFC 2616 的形式发布.
- GET /static/img/header-background.PNG HTTP/1.1
- Host: developer.cdn.mozilla.NET
- User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
- Accept: */*
- Accept-Language: en-US,en;q=0.5
- Accept-Encoding: gzip, deflate, br
- Referer: https://developer.mozilla.org/en-US/docs/Glossary/Simple_header
- 200 OK
- Age: 9578461
- Cache-Control: public, max-age=315360000
- Connection: keep-alive
- Content-Length: 3077
- Content-Type: image/PNG
- Date: Thu, 31 Mar 2016 13:34:46 GMT
- Last-Modified: Wed, 21 Oct 2015 18:27:50 GMT
- Server: Apache
- (image content of 3077 bytes)
核心功能:
可以重用连接, 从而节省了多次重新打开连接的时间.
添加了流水线, 允许在完全传输第一个请求的答案之前发送第二个请求, 从而降低了通信延迟.
现在也支持分块编码传输.
已经引入了其他缓存控制机制.
引入了内容协商, 包括语言, 编码或类型, 并使客户端和服务器可以就最适当的内容达成共识.
现在可以在同一 IP 地址托管不同域的功能允许服务器托管.
HTTP / 1.1 更改了 HTTP 协议的语义, 默认情况下使用长连接. 这意味着, 除非另有说明(通过 Connection: close 标头), 否则 服务器应默认保持连接打开.
但是, 此功能也已反向移植到 HTTP / 1.0, 并通过 Connection: Keep-Alive 标头启用. 因此, 如果您使用的是 HTTP / 1.1, 从技术上讲, 您不需要 Connection: Keep-Alive 标头, 但是许多客户端仍然选择提供它.
自 2005 年以来, 可用于网页的 API 集大大增加, 其中一些 API 创建了针对特定目的的 HTTP 协议扩展, 主要是新的特定 HTTP 标头:
服务器发送的事件, 服务器可以将偶尔的消息推送到浏览器.
WebSocket, 可以通过升级现有的 HTTP 连接来设置的新协议.
这里可以参考我之前写的 系统设计基础 长轮询, WebSocket, 服务器发送事件 (SSEs) 协议
HTTP / 1.0 和 HTTP / 1.1 之间的短链接, 长连接区别
在这里插入图片描述
上图右边 HTTP1.1 通过建立长连接, 中间的几次 TCP 握手都省掉了
HTTP 管道传输和多个并行连接
由于 Keep-Alive 标头的行为, HTTP 流水线, 多个连接和更多改进已得到实现.
在这里插入图片描述
HTTP 2.0 更高性能的协议
多年以来, 网页变得更加复杂, 甚至成为独立的应用程序. 显示的视觉媒体数量, 增加交互性的脚本的数量和大小也有所增加: 通过明显更多的 HTTP 请求传输更多的数据. HTTP / 1.1 连接需要以正确的顺序发送请求. 从理论上讲, 可以使用几个并行连接(通常在 5 到 8 之间), 从而带来相当大的开销和复杂性. 例如, HTTP 管道已成为 Web 开发中的一种资源负担.
在 2010 年代上半年, Google 通过实施实验性协议 SPDY 展示了一种在客户端和服务器之间交换数据的替代方法. 这引起了同时使用浏览器和服务器的开发人员的兴趣. SPDY 定义了响应能力的提高, 并解决了传输数据重复的问题, 是 HTTP / 2 协议的基础.
HTTP / 2 协议与 HTTP / 1.1 版本有几个主要区别:
它是二进制协议, 而不是文本. 不再可以手动读取和创建它. 尽管有这个障碍, 现在仍可以实施改进的优化技术.
它是一个多路复用协议. 可以在同一连接上处理并行请求, 从而消除了 HTTP / 1.x 协议的顺序和阻塞约束.
压缩头文件. 由于这些请求在一组请求中通常很相似, 因此消除了重复和传输数据的开销.
它允许服务器通过称为服务器推送的机制, 在需要之前在客户端缓存中填充数据.
请求和响应如何并行发生
上面的照片显示了请求和响应如何并行发生. 还显示了如何将多个请求 / 响应拆分为单独的帧, 并以异步方式一一发送.
正式标准化后, 在 2015 年 5 月, HTTP / 2 取得了很大的成功. 到 2016 年 7 月, 所有网站有 8.7% 已在使用它, 占所有请求 68% 以上. 高流量的网站采用速度最快, 大大节省了数据传输开销和后续预算.
由于 HTTP / 2 不需要适应网站和应用程序, 因此这种快速的采用率很可能是: 使用 HTTP / 1.1 或 HTTP / 2 对它们是透明的. 使用最新的服务器与最新的浏览器进行通信就足以启用它: 仅需要有限的一组组即可触发采用, 并且随着旧版浏览器和服务器版本的更新, 使用量自然增加了, 而无需使用其他 Web 开发人员的努力.
Http2.0 必须建立在 TLS 的基础上, 也就是必须是 Https 的请求.
问题
1.http1.1 长连接 keep-alive 和 http2.0 的多路复用有什么区别?
http1.1 keep-alive 是不关闭 TCP 连接, 也就是长连接;
在不使用管道机制的情况下, 交互是单工的, 即客户端必须要等前一个请求的响应返回之后, 新的请求才能发过去.
在使用管道机制的情况下, 请求发送可以非阻塞, 但是响应返回必须依然严格按照请求的顺序.
http2.0 多路复用则是基于流的, 那么在传输的时候, 无论请求还是响应, 只要逻辑上允许就可以传输, 如果两个请求没有依赖关系可以不必等待前一个返回而直接发送, 虽说用的是同一条连接.
2. 什么是流水线(管道机制)?
默认情况下, HTTP 请求是按顺序发出的. 下一个请求只有在当前请求收到应答过后才会被发出. 由于会受到网络延迟和带宽的限制, 在下一个请求被发送到服务器之前, 可能需要等待很长时间.
流水线是在同一条长连接上发出连续的请求, 而不用等待应答返回. 这样可以避免连接延迟. 理论上讲, 性能还会因为两个 HTTP 请求有可能被打包到一个 TCP 消息包中而得到提升. 就算 HTTP 请求不断的继续, 尺寸会增加, 但设置 TCP 的 MSS(Maximum Segment Size) 选项, 仍然足够包含一系列简单的请求.
并不是所有类型的 HTTP 请求都能用到流水线: 只有 idempotent 方式, 比如 GET,HEAD,PUT 和 DELETE 能够被安全的重试: 如果有故障发生时, 流水线的内容要能被轻易的重试.
今天, 所有遵循 HTTP/1.1 的代理和服务器都应该支持流水线, 虽然实际情况中还是有很多限制: 一个很重要的原因是, 目前没有浏览器默认启用这个特性.
问题 RFC 2616 中规定了:
一个支持持久连接的客户端可以在一个连接中发送多个请求(不需要等待任意请求的响应). 收到请求的服务器必须按照请求收到的顺序发送响应.
至于标准为什么这么设定, 我们可以大概推测一个原因: 由于 HTTP/1.1 是个文本协议, 同时返回的内容也并不能区分对应于哪个发送的请求, 所以顺序必须维持一致. 比如你向服务器发送了两个请求 GET/query?q=A 和 GET/query?q=B, 服务器返回了两个结果, 浏览器是没有办法根据响应结果来判断响应对应于哪一个请求的.
Pipelining 这种设想看起来比较美好, 但是在实践中会出现许多问题:
一些代理服务器不能正确的处理 HTTP Pipelining.
正确的流水线实现是复杂的.
Head-of-line Blocking 连接头阻塞: 在建立起一个 TCP 连接之后, 假设客户端在这个连接连续向服务器发送了几个请求. 按照标准, 服务器应该按照收到请求的顺序返回结果, 假设服务器在处理首个请求时花费了大量时间, 那么后面所有的请求都需要等着首个请求结束才能响应.
所以现代浏览器默认是不开启 HTTP Pipelining 的.
由于这些原因, 流水线已经被更好的算法给代替, 如 multiplexing 多路复用, 已经用在 HTTP/2.
HTTP1.X 链接管理
左边短链接
中间长连接
右边管道机制
在这里插入图片描述
3. 现代浏览器在与服务器建立了一个 TCP 连接后是否会在一个 HTTP 请求完成后断开?
其实看了上面的内容的话就知道答案了, 现代浏览器默认是 HTTP1.1 协议, 保持长连接是不会断开的, 那么我们来验证下: Chrom 浏览器 F12,2 次访问一个网站的结果: 第一次:
第二次:
结果分析:
初始化连接和 SSL 开销消失了, 说明使用的是同一个 TCP 连接
4. 一个 TCP 连接可以对应几个 HTTP 请求?
一个 TCP 连接是可以发送多个 HTTP 请求的.
从问题 3 的截图就可以证明了.
5. 一个 TCP 连接中 HTTP 请求发送可以一起发送么(比如一起发三个请求, 再三个响应一起接收)?
HTTP/1.1 存在一个问题, 单个 TCP 连接在同一时刻只能处理一个请求, 意思是说: 两个请求的生命周期不能重叠, 任意两个 HTTP 请求从开始到结束的时间在同一个 TCP 连接里不能重叠.
虽然 HTTP/1.1 规范中规定了 Pipelining 来试图解决这个问题, 但是这个功能在浏览器中默认是关闭的. 原因的话问题 2 已经阐述过了,
HTTP2 提供了 Multiplexing 多路传输特性, 可以在一个 TCP 连接中同时完成多个 HTTP 请求.
参考:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Evolution_of_HTTP
- http://qnimate.com/what-is-multiplexing-in-http2/
- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Connection_management_in_HTTP_1.x
- https://medium.com/platform-engineer/evolution-of-http-69cfe6531ba0
- https://blog.csdn.net/ywlmsm1224811/article/details/96436768
来源: http://network.51cto.com/art/202101/640780.htm