[译] WebSockets 与长轮询的较量

WebSockets 与长轮询的较量

咱们要如何在二者之间作出选择?php

有时候,当信息一旦准备就绪,咱们就须要从服务器获取它们。而咱们一般使用的 AJAX 请求/响应模式没法为这类应用场景保持请求链接的创建。相反地,咱们须要一种基于推送的方法,例如 WebSockets 协议、长轮询、服务器推送事件(SSE)以及最近的 HTTP2 服务器推送。在本文中,咱们将对比两种方法:WebSockets 与长轮询。html

长轮询概述

1995 年,网景公司 聘请 Brendan Eich 为 Netscape Navigator 实现脚本功能,通过 10 天的时间,JavaScript 诞生了。做为一门编程语言,与现代 JavaScript 语言相比,那时诞生的 JavaScript 功能很是有限,而它与浏览器文档对象模型(DOM)的交互就更加有限了。JavaScript 主要用于提供有限的加强功能来丰富浏览器文档的使用性。例如,在浏览器中验证表单、将动态 HTML 轻便地插入现有文档。前端

随着 浏览器大战 的升温,微软的 Internet Explorer 版本到达了版本 4 及以上,对浏览器强大特性集的争夺战致使微软在 Internet Explorer 中引入了一个新特性,这一特性最终成为了 XMLHttpRequest 。十多年来,全部浏览器都广泛支持 XMLHttpRequest。html5

长轮询 本质上是原始轮询技术的一种更有效的形式。向服务器发送重复请求会浪费资源,由于必须为每一个新传入的请求创建链接,必须解析请求的 HTTP 头部,必须执行对新数据的查询,而且必须生成和交付响应(一般不提供新数据)。而后必须关闭链接并清除全部资源。长轮询是一种服务器选择尽量长的时间保持和客户端链接打开的技术,仅在数据变得可用或达到超时阙值后才提供响应,而不是在给到客户端的新数据可用以前,让每一个客户端屡次发起重复的请求。node

WebSockets 概述

大约在 2008 年中期,开发人员 Michael CarterIan Hickson 特别敏锐地感受到在实现真正健壮的东西时使用 Comet 的痛苦和局限性。经过 在 IRCW3C 邮件列表 上的合做,他们制定了一项计划,在网络上引入了现代实时双向通讯的新标准,从而 创造了“WebSocket”这个名字android

这个想法进入了 W3C HTML 草案标准,不久以后,Michael Carter 写了一篇文章,在 Comet 社区中介绍了 WebSockets。2010 年,谷歌的 Chrome 4 成为第一款彻底支持 WebSockets 的浏览器,其余浏览器供应商也在接下来的几年中纷纷效仿。2011 年,RFC 6455 —— WebSocket 协议 —— 在 IETF 网站上发布。ios

简而言之,WebSockets 是一个构建在设备 TCP/IP 协议栈之上的传输层。其目的是向 Web 开发人员提供本质上尽量接近原始的 TCP 通讯层,同时添加一些抽象概念,以消除 Web 工做中存在的一些阻力。它们还知足了这样一个事实:即网络具备额外须要考虑的安全因素,这些安全因素必须考虑在内以保护消费者和服务提供商。git

长轮询的利与弊

优势github

  • 长轮询是在 XMLHttpRequest 以后实现的,它几乎获得了设备的广泛支持,所以一般不多须要有进一步的备选方案。可是,在必须处理异常的状况下,或者在服务器可查询新数据但不支持长轮询(更不用说其余更现代的技术标准)的状况下,基本轮询有时仍然有些用处,而且可使用 XMLHttpRequest 或经过 JSONP 利用简单的 HTML 脚本标签。

缺点web

  • 长轮询大量占据服务器资源。
  • 可靠的消息排序 多是长轮询的一个问题,由于来自同一个客户端的多个 HTTP 请求可能同时运行。举个例子,若是一个客户端打开两个浏览器选项卡,使用相同的服务器资源,而且客户端应用程序正在将数据持久化到本地存储区(如 localStorage 或 IndexedDb),则没法保证重复数据不会被屡次写入。
  • 根据服务端实现的不一样,一个客户端对消息的确认接收也可能致使另外一个客户端根本不会收到预期的消息,由于服务端可能错误地认为客户端已经收到了它所指望的数据。

WebSockets 的利与弊

优势

  • WebSockets 保持一个惟一的链接打开,同时消除长轮询的延迟问题。
  • WebSockets 一般不使用 XMLHttpRequest,所以,当咱们每次须要从服务器获取更多的信息时,无需发送头部数据。反过来讲,这又减小了数据发送到服务器时须要付出的高昂的数据负载代价。

缺点

  • 当链接终止时,WebSockets 没法自动恢复链接 —— 这是须要你本身实现的部分,也是致使存在许多 客户端库 的缘由。
  • 早于 2011 年的浏览器没法支持 WebSocket 链接 —— 但这一点愈来愈可有可无。

为何 WebSocket 协议是更好的选择?

通常来讲,WebSockets 会是更好的选择。

长轮询在服务器上占用更多的资源,而 WebSockets 在服务器上占用的空间不多。长轮询还须要在服务器与许多设备之间进行屡次通讯。而不一样的网关对于一个常规链接容许保持打开的时间有不一样的标准。若是链接打开时间过久,其进程可能会被杀死,甚至当这个进程正在处理一些重要的事情时。

使用 WebSockets 构建应用的理由:

  • 全双工异步消息传送。换句话说,客户端和服务器均可以独立地相互传输消息。
  • WebSockets 无需任何配置便可经过大多数防火墙。
  • 良好的安全模式(基于原始的安全模式)。

WebSockets 开源解决方案

WebSocket 库有两个主要分类:一种只实现协议部分,把其他部分留给开发人员实现,另外一种构建在协议之上,它们具备实时消息通讯应用程序一般须要的各类附加功能,例如丢失链接的恢复,发布/订阅频道、身份认证、受权等。

后者一般要求开发人员在客户端使用本身的库,而不只仅是使用浏览器提供的原始 WebSocket API。所以,确保你对所选择方案的工做方式和所提供的服务感到满意就变得很是重要。一旦将所选择的解决方案集成到体系结构里,你可能会发现本身陷入了该方案的工做方式中,任何可靠性、性能和可扩展性方面的问题均可能会反过来影响你。

让咱们从第一类提及。

注意: 如下全部内容均是开源库。

ws

ws 是一个“简单易用、快速且通过全面测试的 WebSocket 客户端和 Node.js 服务器”。它绝对是一个准系统级别的实现,旨在完成执行协议上全部艰难的工做,可是恢复链接、发布/订阅等附加功能,必须由你本身来管理。

客户端 (绑定前的浏览器):

const WebSocket = require('ws');
const ws = new WebSocket('ws://www.host.com/path');
ws.on('open', function open() {
    ws.send('something');
});

ws.on('message', function incoming(data) {
    console.log(data);
});
复制代码

服务端 (Node.js):

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
    ws.on('message', function incoming(message) {
    console.log('received: %s', message);
    });
    ws.send('something');
});
复制代码

μWebSockets

μWSws 的直接替代品,它特别注重性能和稳定性。据我所知,μWS 离最快的 WebSocket 服务器仅有一步之遥。SocketCluster 就是由它驱动的,关于 SocketCluster 我将在下面说到。

因为做者出于哲学上的缘由试图将 μWS 从 NPM 中提取出来,近来围绕 μWS 引起了一些争议,但 μWS 最新的可运行版本仍然在 NPM 上,而且能够从 NPM 安装时明确指定该版本。也就是说,做者正在开发 一个新版本,其附带的 node.js 绑定 也在 开发中

var WebSocketServer = require('uws').Server;
var wss = new WebSocketServer({ port: 3000 });
function onMessage(message) {
    console.log('received: ' + message);
}

wss.on('connection', function(ws) {
    ws.on('message', onMessage);
    ws.send('something');
});
复制代码

客户端 —— 在浏览器中使用 WebSockets

WebSocket API 定义于 WHATWG HTML Living Standard,它使用起来很是简单。构建 WebSocket 只须要一行代码:

JS

const ws = new WebSocket('ws://example.org');
复制代码

注意,在一般使用 HTTP 方案的地方使用 ws。在一般使用 https 方案的地方,还能够选择 wss。这些协议是和 WebSocket 规范一块儿引入的,旨在表示一个 HTTP 链接,该链接中包括一个升级链接以使用 WebSockets 的请求。

建立 WebSocket 对象自己并无太大的做用。链接是异步创建的,因此在发送任何消息以前,你必须监听握手的完成状况,还须要一个从服务器接收消息的监听器:

ws.addEventListener('open', () => {
    // 向 WebSocket 服务器发送消息
    ws.send('Hello!');
});

ws.addEventListener('message', event => {
// `event` 对象是一个典型的 DOM 事件对象,
// 服务器发送的消息数据存储在 `data` 属性中
    console.log('Received:', event.data);
});
复制代码

还有错误事件和关闭事件。当链接终止时,WebSockets 不会自动恢复链接 —— 这须要你本身实现,这也是存在许多客户端库的缘由之一。虽然 WebSocket 类简单易用,但它实际上只是一个基本的构建块。对于不一样子协议或附加功能的支持,例如消息传输通道,必须单独实现。

长轮询 —— 开源解决方案

大多数库不会单独使用长轮询,由于长轮询一般与其余传输策略一块儿使用,或做为其余传输策略的备选方案,或是当长轮询不起做用时,将其余传输策略做为备选。在 2018 年及之后,独立的长轮询库尤为罕见,面对更先进的替代品对传输的普遍支持,长轮询这种技术很快就失去了相关性。不过,你能够将它做为传输的备选方案,如下是一些不一样语言的可选项:

Ably、WebSockets 与长轮询

大多数 Ably 的客户端库 SDK 使用 WebSocket 创建与 Ably 的实时链接,而后对包括身份验证在内的全部其余 REST 操做使用简单的 HTTP 请求。

可是,客户端库 SDK(例如咱们的 Javascript 浏览器库)被设计为根据可用浏览器和链接选择可用且最佳的传输方式。经过支持附加的传输方式,使其可以回退到最低的公共标准,Ably 确保如今几乎全部的浏览器都能与 Ably 创建实时链接。咱们的 Javascript 浏览器库目前支持如下传输方式,按照性能从优到劣排列:

在实现对 WebSocket 的支持且将长轮询做为备选方案时,须要涉及到不少方面 —— 不只涉及客户端和服务器实现细节,还涉及对其余传输方式的支持,以确保对不一样客户端环境的可靠支持,也涉及到更普遍的关注点,例如 身份验证和受权保证消息可交付可靠的消息排序历史消息保留,还有 更多方面

参考资料与扩展阅读

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索