HTTP 服务器推送也称为 HTTP 流,是一种客户端-服务器通信模式,它将信息从 HTTP 服务器异步发送到客户端,而无需客户端请求。在高度交互的 Web或移动应用程序中,一个或多个客户端需要连续不断地从服务器接收信息,服务器推送架构对这类应用程序特别有效。
传统“请求-响应”模式的局限性
网络上的客户端-服务器通信在过去曾是一种请求-响应模型,要求客户端(比如 Web 浏览器)向服务器请求资源。服务器通过发送所请求的资源来响应客户端请求。如果资源不可用,或者客户端没有权限访问它,那么服务器会发送一条错误消息。在请求-响应架构中,服务器绝不能向客户端发送未经请求的消息。
随着 Web 应用程序变得更强大和更具交互性,请求-响应模型的局限性也开始显现出来。需要更频繁更新的客户端应用程序被要求更频繁地发送 GET 请求。这种技术称为轮询,在高峰期间,这可能会使服务器不堪重负,并导致性能问题。该技术效率低下,因为客户端发送的许多请求都没有返回更新。此外,客户端只能按指定间隔进行轮询,这可能减缓客户端的响应速度。
HTTP 服务器推送技术的出现,就是为了解决与频繁轮询相关的性能问题和其他局限。尤其是对于交互式 Web 应用程序,比如游戏和屏幕共享服务,Web 服务器能更高效地在新数据可用时向客户端发送更新。
比较 WebSocket 与 SSE
1基于区别
WebSocket 和 SSE 都是传统请求-响应 Web 架构的替代方案,但它们不是完全冲突的技术。WebSocket 架构在客户端与服务器之间打开一个套接字,用于实现全双工(双向)通信。无需发送 GET 消息并等待服务器响应,客户端只需监听该套接字,接收服务器更新,并使用收到的数据来发起或支持各种交互。客户端也可以使用套接字与服务器通信,例如在成功收到更新时发送 ACK 消息。
SSE 是一种更简单的标准,是作为 HTML5 的扩展而开发的。尽管 SSE 支持从服务器向客户端发送异步消息,但客户端无法向服务器发送消息。对于客户端只需接收从服务器传入的更新的应用程序,SSE 的半双工通信模型最适合。与 WebSocket 相比,SSE 的一个优势是它是基于 HTTP 而运行的,不需要其他组件。
对于需要在客户端与服务器之间频繁通信的多用途 Web 应用程序,显然应该选择 WebSocket。对于希望从服务器向客户端传输异步数据,而不需要响应的应用程序,SSE 更适合一些。
2浏览器支持
在比较 HTTP 协议时,浏览器支持是一个必要的考虑因素。浏览表 1 中的数据,可以看到所有现代浏览器都支持 WebSocket 协议,包括移动浏览器。Microsoft IE 和 Edge 不支持 SSE。
对于必须在所有浏览器中运行的应用程序,WebSocket 目前是更好的选择。
3开发工作量
在比较协议时,尤其是 WebSocket 和 SSE 等较新的协议,工作量是要考虑的另一个因素。这里的 “工作量” 指的是代码行,或者您要花多少时间为使用给定协议的应用程序编写代码。对于具有严格的时间限制或开发预算的项目,这个指标特别重要。
实现 SSE 的工作量比 WebSocket 要少得多。它适合任何使用 HTML5 编写的应用程序,主要负责在从服务器发送到客户端的消息中添加一个 HTTP 标头。如果给定了正确标头,客户端就会自动将消息识别为服务器发送的事件。不同于 WebSocket,SSE 不需要在服务器与客户端之间建立或维护套接字连接。
WebSocket 协议要求配置一个服务器端套接字来监听客户端连接。客户端自动打开一个与服务器的套接字并等待消息,消息可以异步发送。每个应用程序都可以定义自己的消息格式、持久连接(脉动信号)策略等。表 2 总结了 SSE 和 WebSocket 协议的优劣。
开发 SSE 应用程序
SSE 是一种仅使用 HTTP 传送异步消息的 HTML5 标准。不同于 WebSocket,SSE 不需要在后端创建服务器套接字,也不需要在前端打开连接。这大大降低了复杂性。
1SSE 前端
清单 1 给出了一个使用 SSE 的简单 HTTP 服务器推送应用程序的 UI 代码:
var source = new EventSource(“/user-log-stream”);
source.onmessage = function(event) {
var message = event.data;
// do stuff based on received message
};
EventSource 是一个与服务器建立 HTTP 连接的接口。服务器使用事件流格式 将消息发送到客户端,这种格式是一种使用 UTF-8 编码的简单的文本数据流。建立联系后,HTTP 连接会对给定消息流保持开放,以便服务器能发送更新。在清单 1 中,我们创建了一个 HTTP 连接来接收与 /#tabs/user-log-stream URI 相关的事件。即时通讯聊天软件app开发可以加蔚可云咨询
2SSE 后端
在后端,我们为 URL /user-log-stream 创建了一个分派器。该分派器将从前端接收连接请求,并发回一条异步消息来发起通信。SSE 客户端不能向服务器发送消息。
清单 2 中的代码演示了后端代码。
UserLogStream 类的 stream 方法被分配给每个与 /user-log-stream URI 连接的 EventSource。任何待发送消息都将被发送到已连接的 EventSource。请注意,消息格式不是可选的:SSE 协议要求消息以 data: 开头,以 \n\n 结尾。尽管这些示例中使用了 HTTP 作为消息格式,但也可以使用 JSON 或另一种格式发送消息。
尽管这是一个非常基本的 SSE 实现示例,但它演示了该协议的简单性。
开发 WebSocket 应用程序
像 SSE 示例一样,WebSocket 应用程序基于 CherryPy(一种 Python Web 框架)而构建。Project WoK 是后端 Web 服务器,python-websockify 库插件处理套接字连接。该插件是 Kimchi 的一部分。
组件包括:
Project WoK:后端 Python 逻辑,提供要由推送服务器广播的消息。
Websockify 代理:此代理由 python-websockify 库实现,它使用 Unix 套接字从推送服务器接收消息,并将这些消息传送到 UI 上连接的 WebSocket。
推送服务器:一个普通 Unix 套接字服务器,它向已连接的客户端广播后端消息。
前端:UI 与 Websockify 代理建立 WebSocket 连接,并监听服务器消息。根据具体的消息,将会执行一个特定操作,例如刷新一个列表或向用户显示一条消息。
排除 WebSocket 前端的故障
后端设置和配置相对比较容易,但编写前端代码就没有那么简单了。从决定打开还是关闭浏览器连接到处理某些 API 的方式上的差异,WebSocket 前端带来了许多挑战。在解决这些挑战时,需要重新利用后端代码的某些元素。
第一个决定是为所有 UI 维护单个 WebSocket 连接,还是设置多个按需打开的 WebSocket 连接。我们可能会先考虑多个按需 WebSocket 连接。我们认为,仅在 UI 期望从后端收到一条特定消息时才应打开一个连接 — 例如等待某个任务完成。如果不想收到异步消息,则不需要打开连接。这会从 UI 和后端节约一些资源,但代价是需要管理每个已打开的 WebSocket 的生命周期。
对于期望从服务器收到大量异步消息的 UI,单个持久 WebSocket 连接可能更合适。这个持久连接被用作通知流,允许任何相关 UI 代码监听收到的消息并执行相关操作。
每个解决方案都各有用武之地,下面将试验每个解决方案。使用最适合您的应用程序的方法很重要。
对于只需要能向客户端传输异步服务器消息的 Web 应用程序,服务器发送的事件是一个优雅且简单的解决方案。作为半双工 HTTP 解决方案,SSE 不允许客户端向服务器回传消息。此外,在编写本文时,所有 Microsoft 浏览器都不支持 SSE。此限制是否是致命弱点,取决于应用程序的目标受众。
WebSocket 更复杂且要求更高,但全双工 TCP 连接使它适用于更广泛的应用场景。WebSocket 受大多数现代 Web 框架支持,而且兼容所有主要的 Web 和移动浏览器。尽管没有演示,但可以使用类似 Tornado 这样的服务器框架,它有助于快速配置推送服务器,而无需从头编写服务器代码。
SSE 是一个更简单且更快的解决方案,但它是不可扩展的。如果 Web 应用程序的需求发生了改变(例如,如果您认为前端应与后端交互),则需要使用 WebSocket 重构应用程序。WebSocket 需要做更多的前期工作,但它是一种更灵活的、可扩展的框架。它更适合会不断添加新功能的复杂应用程序。