我正在学习 HTTP/2 协议。这是一个带有小消息帧的二进制协议。它允许通过单个 TCP 连接进行流多路复用。从概念上讲,它似乎与 WebSockets 非常相似。
是否有计划淘汰 websocket 并用某种无头 HTTP/2 请求和服务器发起的推送消息替换它们?或者 WebSockets 会补充 HTTP/2 吗?
在刚刚读完 RFC 7540之后,除了将二进制数据从服务器推送到JS webclient之外,HTTP/2 确实为所有用例淘汰了 websockets 。HTTP/2 完全支持二进制双向流(继续阅读),但浏览器 JS 没有用于使用二进制数据帧的 API,AFAIK 没有计划使用这样的 API。
对于双向流的所有其他应用程序,HTTP/2 与 websockets 一样好或更好,因为(1)规范为您做了更多的工作,(2)在许多情况下,它允许打开到源的 TCP 连接更少。
PUSH_PROMISE
(俗称服务器推送)不是这里的问题。那只是性能优化。
浏览器中 Websocket 的主要用例是启用双向数据流。所以,我认为 OP 的问题变成了 HTTP/2 在浏览器中启用双向流方面是否做得更好,我认为是的,确实如此。
首先,它是bi-di。只需阅读流部分的介绍:
“流”是在 HTTP/2 连接中客户端和服务器之间交换的独立的双向帧序列。流有几个重要的特征:
单个 HTTP/2 连接可以包含多个同时打开的流,其中任一端点交错来自多个流的帧。
流可以单方面建立和使用,也可以由客户端或服务器共享。
流可以由任一端点关闭。
像这样的文章(链接在另一个答案中)关于 HTTP/2 的这个方面是错误的。他们说这不是比迪烟。看,HTTP/2 不可能发生的一件事:连接打开后,服务器无法发起常规流,只能发起推流。但是一旦客户端通过发送请求打开流,双方可以随时通过持久套接字发送 DATA 帧 - 完整双向。
这与 websocket 没有太大区别:客户端必须在服务器也可以发送数据之前发起 websocket 升级请求。
最大的区别在于,与 websockets 不同,HTTP/2 定义了自己的多路复用语义:流如何获取标识符以及帧如何携带它们所在的流的 id。HTTP/2 还定义了流优先级的流控制语义。这在比迪烟的大多数实际应用中都很重要。
(那个错误的文章还说Websocket标准有多路复用。不,它没有。这并不难发现,只需打开Websocket RFC 6455并按⌘-F,然后输入“multiplex”。阅读后
该协议旨在可扩展;未来的版本可能会引入额外的概念,例如多路复用。
您会发现 Websocket 多路复用有 2013草案扩展。但我不知道哪些浏览器(如果有的话)支持它。我不会尝试在该扩展的后面构建我的 SPA webapp,尤其是随着 HTTP/2 的到来,支持可能永远不会到来)。
多路复用正是您通常在为 bidi 打开 websocket 时必须自己做的事情,例如,为响应式更新的单页应用程序提供动力。我很高兴它在 HTTP/2 规范中,得到一劳永逸的照顾。
如果你想知道 HTTP/2 能做什么,看看 gRPC。gRPC 是跨 HTTP/2 实现的。具体查看 gRPC 提供的半双工和全双工流选项。(请注意,gRPC 目前在浏览器中不起作用,但这实际上是因为浏览器 (1) 不会将 HTTP/2 框架暴露给客户端 javascript,并且 (2) 通常不支持 Trailers,这些用于gRPC 规范。)
websockets 还能在哪里占有一席之地?最大的是服务器->浏览器推送二进制数据。HTTP/2 确实允许服务器->浏览器推送二进制数据,但它没有在浏览器 JS 中公开。对于像推送音频和视频帧这样的应用程序,这是使用 websockets 的一个原因。
编辑:2020 年 1 月 17 日
随着时间的推移,这个答案逐渐上升到顶部(这很好,因为这个答案或多或少是正确的)。但是仍然偶尔有评论说由于各种原因它不正确,通常与PUSH_PROMISE
在单页应用程序中如何实际使用面向消息的服务器 -> 客户端推送的一些混淆有关。
如果您需要构建一个实时聊天应用程序,比方说,您需要向聊天室中具有打开连接的所有客户端广播新的聊天消息,您可以(并且可能应该)在没有 websocket 的情况下执行此操作。
您将使用 Server-Sent Events 向下推送消息,并使用Fetch api 向上发送请求。服务器发送事件(SSE) 是一个鲜为人知但得到很好支持的 API,它公开了一个面向消息的服务器到客户端的流。虽然它看起来不像客户端 JavaScript,但在后台,您的浏览器(如果它支持 HTTP/2)将重用单个 TCP 连接来多路复用所有这些消息。没有效率损失,实际上它比 websockets 有所收获,因为您页面上的所有其他请求也共享相同的 TCP 连接。需要多个流?打开多个事件源!它们会自动为您多路复用。
除了比 websocket 握手更节省资源和减少初始延迟之外,服务器发送事件还具有很好的特性,它们可以自动回退并在 HTTP/1.1 上工作。但是当你有一个 HTTP/2 连接时,它们工作得非常好。
这是一篇很好的文章,其中包含完成响应式更新 SPA 的真实示例。
据我了解,HTTP/2 不是 websocket 的替代品,而是旨在标准化 SPDY 协议。
在 HTTP/2 中,在后台使用服务器推送来改善客户端从浏览器加载资源。作为开发人员,您在开发过程中并不真正关心它。但是,使用 Websocket,开发人员可以使用 API,该 API 能够通过唯一的全双工连接来消费和推送消息。
这些不是一回事,应该相辅相成。
我说不(Websockets 并没有过时)。
第一个也是最常被忽略的问题是HTTP/2 推送不可强制执行,并且可能被代理、路由器、其他中介甚至浏览器忽略。
即(来自 HTTP2 草案):
中介可以接收来自服务器的推送并选择不将它们转发给客户端。也就是说,如何利用推送的信息,就看那个中介了。同样,中介可能会选择向客户端进行额外的推送,而不需要服务器采取任何行动。
因此,HTTP/2 Push 不能替代 WebSockets。
此外,HTTP/2 连接会在一段时间后关闭。
确实,标准规定:
HTTP/2 连接是持久的。为了获得最佳性能,预计客户端不会关闭连接,直到确定不需要与服务器进行进一步通信(例如,当用户导航离开特定网页时)或直到服务器关闭连接。
但...
鼓励服务器尽可能长时间地保持打开的连接,但在必要时允许终止空闲连接。当任一端点选择关闭传输层 TCP 连接时,终止端点应该首先发送一个 GOAWAY(第 6.8 节)帧,以便两个端点都可以可靠地确定先前发送的帧是否已被处理并优雅地完成或终止任何必要的剩余任务。
即使同一个连接允许在打开时推送内容,即使 HTTP/2 解决了 HTTP/1.1 的“保持活动”引入的一些性能问题...... HTTP/2 连接也不会无限期地保持打开状态.
网页一旦关闭也不能重新启动 HTTP/2 连接(除非我们回到长拉,也就是说)。
编辑(2017 年,两年后)
HTTP/2 的实现表明多个浏览器选项卡/窗口共享一个 HTTP/2 连接,这意味着push
永远不会知道它属于哪个选项卡/窗口,从而消除了push
作为 Websockets 的替代品的使用。
编辑 (2020)
我不确定为什么人们开始对答案投反对票。如果有的话,自最初发布答案以来的这些年证明 HTTP/2 不能替代 WebSockets,并且并非旨在这样做。
当然,HTTP/2 可能用于隧道WebSocket 连接,但这些隧道连接仍然需要 WebSocket 协议,它们会影响 HTTP/2 容器的行为方式。
答案是不。两者之间的目标非常不同。甚至还有一个基于 HTTP/2 的 WebSocket RFC,它允许您通过单个 HTTP/2 TCP 管道建立多个 WebSocket 连接。
WS over HTTP/2 将通过减少打开新连接的时间并允许更多通信通道而不增加更多套接字、软 IRQ 和缓冲区的费用来节省资源。
https://datatracker.ietf.org/doc/html/draft-hirano-httpbis-websocket-over-http2-01
好吧,引用这篇 InfoQ文章的话:
好吧,答案显然是否定的,原因很简单:正如我们在上面看到的,HTTP/2 引入了服务器推送,它使服务器能够主动向客户端缓存发送资源。但是,它不允许将数据下推到客户端应用程序本身。服务器推送仅由浏览器处理,不会弹出到应用程序代码,这意味着应用程序没有 API 来获取这些事件的通知。
因此 HTTP2 推送实际上是您的浏览器和服务器之间的东西,而 Websockets 真正公开了可供客户端(javascript,如果它在浏览器上运行)和应用程序代码(在服务器上运行)都可以使用的 API,用于传输实时数据。
到今天为止,没有。
与 HTTP 相比,HTTP/2 允许您保持与服务器的连接。从那里,您可以同时拥有多个数据流。目的是即使没有客户端请求,您也可以同时推送多个内容。例如,当浏览器请求 aindex.html
时,服务器可能还想推送index.css
和index.js
。浏览器没有要求它,但服务器可能会在不被要求的情况下提供它,因为它可以假设你会在几秒钟内想要它。
这比获取、index.html
解析它、发现它需要然后index.js
为这些文件构建 2 个其他请求的 HTTP/1 替代方法更快。HTTP/2 允许服务器推送客户端甚至没有请求的数据。index.css
在这种情况下,它类似于 WebSocket,但并不是真正的设计。WebSocket 应该允许类似于 TCP 连接或串行连接的双向通信。这是一个相互通信的套接字。此外,主要区别在于您可以以原始字节发送任意数据包,而不是封装在 HTTP 协议中。标头、路径、查询字符串的概念仅在握手期间发生,但 WebSocket 会打开数据流。
另一个区别是您可以在 Javascript 中对 WebSocket 进行更精细的访问,而使用 HTTP,它是由浏览器处理的。您使用 HTTP 获得的只是您可以放入XHR
/的任何内容fetch()
。这也意味着浏览器将在您无法控制的情况下拦截和修改 HTTP 标头(例如:Origin
、、Cookies
等)。此外,HTTP/2 能够推送的内容会发送到浏览器。这意味着 JS 并不总是(如果有的话)知道事情正在被推动。再一次,它是有意义的index.css
,index.js
因为浏览器会缓存它,但对于数据包来说就不那么重要了。
一切都在名义上。HTTP 代表超文本传输协议。我们专注于转移资产的概念。WebSocket 是关于构建一个套接字连接,其中二进制数据可以双向传递。
我们没有真正讨论的是 SSE(服务器发送事件)。将数据推送到应用程序 (JS) 不是 HTTP/2 的意图,而是用于 SSE。SSE 通过 HTTP/2 得到了真正的加强。但是当重要的是数据本身而不是到达的可变端点时,它并不是 WebSockets 的真正替代品。对于使用 WebSocket 的每个端点,都会创建一个新的数据流,但使用 SSE,它会在已经存在的 HTTP/2 会话之间共享。
这里总结了每个目标:
消息交换和简单的流式传输(不是音频、视频流式传输)可以通过 Http/2 多路复用和 WebSockets 完成。所以有一些重叠,但 WebSockets 有完善的协议、很多框架/API 和更少的标头开销。 这是关于该主题的好文章。
目前,2020 年 4 月,HTTP/2 并没有让 WebSockets 过时。WebSockets 相对于 HTTP2 的最大优势在于
HTTP/2 works only on Browser Level not Application Level
意味着 HTTP/2 不提供任何像 WebSockets 这样的 JS API 来允许通信并将某种 JSON 或其他数据直接从应用程序(例如网站)传输到服务器。因此,据我所知,只有当 HTTP/2 开始提供像 WebSockets 这样的 API 来与服务器通信时,它才会让 WebSockets 过时。直到它刚刚更新和更快的 HTTP 1.1 版本。
不,WebSockets 并没有过时。但是,HTTP/2 破坏了为 HTTP/1.1 定义的 websocket(主要是通过使用 Upgrade 标头禁止协议更新)。这就是为什么这个 rfc:
https://datatracker.ietf.org/doc/html/rfc8441
为 HTTP/2 定义了一个 websocket 引导过程。