【问题标题】:Server Scalability - HTML 5 websockets vs Comet服务器可扩展性 - HTML 5 websockets vs Comet
【发布时间】:2012-02-24 19:34:26
【问题描述】:

Caplin 等许多 Comet 实现都提供服务器可扩展解决方案。

以下是来自Caplin 站点的统计数据之一:

单个 Caplin liberator 实例可以支持多达 100,000 个客户端,每个客户端每秒接收 1 条消息,平均延迟小于 7 毫秒。

这与任何网络服务器上的 HTML5 websockets 相比如何?谁能指出任何 HTML 5 websockets 统计数据?

【问题讨论】:

  • 关于“HTML 5 websockets vs Comet”的评论:正如 Caplin 的 Liberator 下面的其他 cmets 所述,以及许多其他“Comet”服务器,都支持 WebSockets 作为连接机制。服务器何时不再是 Comet 服务器?如果它使用 WebSockets,它仍然是 Comet 服务器吗? Comet 是 HTTP-Long Polling 和 HTTP Streaming 的总称吗?我建议阅读The Rumours of Comet’s Death Have Been Greatly Exaggerated
  • 我暂时将其保留为评论。但是你真的应该考虑 EventSource(又名服务器发送事件)作为一个选项。由于它是单向的(仅推送),因此可以更轻松地扩展到多台服务器。

标签: html websocket push comet


【解决方案1】:

披露 - 我为 Caplin 工作。

这个页面有一些错误信息,所以我想试着让它更清楚..

我认为我们可以将我们正在讨论的方法分成三个阵营..

  1. Comet HTTP 轮询 - 包括长轮询
  2. Comet HTTP 流式传输 - 服务器到客户端的消息使用单个持久套接字,初始设置后没有 HTTP 标头开销
  3. Comet WebSocket - 单双向套接字

我将它们都视为 Comet,因为 Comet 只是一种范式,但自从 WebSocket 出现以来,有些人希望将它视为不同的或取代 Comet - 但它只是另一种技术 - 除非你很高兴只支持最新的浏览器,那么你不能只依赖 WebSocket。

就性能而言,大多数基准测试都集中在服务器到客户端的消息上——用户数、每秒消息数以及这些消息的延迟。对于这种情况,HTTP Streaming 和 WebSocket 之间没有根本区别 - 两者都将消息写入一个开放的套接字,几乎没有或没有标头或开销。

如果消息的频率较低,长轮询可以提供良好的延迟。但是,如果您有两条消息(服务器到客户端)快速连续,那么在收到第一条消息后发出新请求之前,第二条消息不会到达客户端。

我认为有人提到了 HTTP KeepAlive。这显然可以改善长轮询 - 您仍然有往返和标头的开销,但并不总是创建套接字。

在有更多客户端到服务器消息的情况下,WebSocket 应该改进 HTTP 流。与每个人都能理解的简单易懂的“向大量客户发送大量消息”相比,将这些场景与现实世界联系起来会产生稍微随意的设置。例如,在交易应用程序中,创建一个包含执行交易的用户(即客户端到服务器消息)的场景很容易,但结果比基本的服务器到客户端场景的意义要小一些。交易者不会尝试每秒进行 100 次交易 - 因此您最终会得到类似“10000 名用户每秒收到 100 条消息,同时每 5 分钟发送一次客户消息”这样的结果。客户端到服务器的消息更有趣的部分是延迟,因为与服务器到客户端的消息相比,所需的消息数量通常是微不足道的。

上面有人提出的另一点,关于 64k 客户端,你不需要做任何聪明的事情来支持服务器上超过 64k 的套接字——除了配置数字文件描述符等。如果你试图从一个 64k 连接单个客户端机器,这是完全不同的,因为它们每个都需要一个端口号 - 但在服务器端它很好,即监听端,你可以超过 64k 套接字。

【讨论】:

  • +1,正是我在之前的 cmets 中所指的。 HTTP 流和 WebSockets 的实现在功能上是相同的,但是你失去了 WebSockets 的全双工能力。长轮询和短轮询与 WebSockets 的比较并不公平,因为连接会不断重新初始化。
【解决方案2】:

忽略任何形式的轮询,如其他地方所述,当更新率很高时会引入延迟,JavaScript 流式传输最常见的三种技术是:

  1. WebSocket
  2. 彗星 XHR/XDR 流媒体
  3. Comet Forever IFrame

WebSocket 是迄今为止最干净的解决方案,但仍然存在浏览器和网络基础设施不支持它的问题。越早依赖越好。

XHR/XDR 和 Forever IFrame 都适用于将数据从服务器推送到客户端,但需要进行各种 hack 才能在所有浏览器中始终如一地工作。根据我的经验,这些 Comet 方法总是比 WebSockets 稍慢,这不仅是因为需要更多的客户端 JavaScript 代码才能使其工作 - 但是从服务器的角度来看,通过线路发送数据的速度是相同的。

这里还有更多WebSocket benchmark graphs,这次是我们的产品my-Channels Nirvana

跳过多播和二进制数据图,直到页面上的最后一个图(JavaScript 高更新率)

总结 - 结果显示 Nirvana WebSocket 以 800 微秒的延迟向 2,500k 用户提供 50 个事件/秒。在 5,000 个用户(总共 250k 事件/秒流式传输)时,延迟为 2 毫秒。

【讨论】:

    【解决方案3】:

    理论上,WebSocket 可以比 HTTP 更好地扩展,但也有一些注意事项和一些解决这些注意事项的方法。

    HTTP 与 WebSockets 的握手标头处理的复杂性大致相同。 HTTP(和初始 WebSocket)握手很容易超过 1K 的数据(由于 cookie 等)。重要的区别在于 HTTP 握手会再次发生每个消息。一旦建立了 WebSocket 连接,每条消息的开销只有 2-14 个字节。

    @David Titarenco 的回答(12)中发布的出色 Jetty 基准测试链接表明,与 Comet 相比,WebSockets 可以轻松实现延迟降低一个数量级

    有关扩展 WebSocket 与 HTTP 的更多信息,请参阅 this answer

    注意事项

    • WebSocket 连接的生命周期很长,而 HTTP 连接的生命周期很短。这显着降低了开销(无需为每个请求/响应创建和管理套接字),但这确实意味着要将服务器扩展到超过 64k 个单独的同时客户端主机,您将需要在同一服务器上使用多个 IP 地址等技巧。

    • 由于网络中介的安全问题,浏览器到服务器的 WebSocket 消息对所有有效负载数据进行了 XOR 屏蔽。这为服务器增加了一些 CPU 利用率来解码消息。但是,XOR 是大多数 CPU 架构中最有效的操作之一,并且通常有可用的硬件辅助。服务器到浏览器的消息没有被屏蔽,由于 WebSockets 的许多用途不需要从浏览器发送到服务器的大量数据,这不是一个大问题。

    【讨论】:

    • 在 HTTP 流(Caplin 使用的)中,HTTP 握手不会发生在每条消息中——我自己已经实现了很多次。它本质上只是一个开放的(单向)套接字连接。 HTTP 流式传输性能将与 WebSockets 非常相似(但有几点需要注意,其中之一是缺少双工)。
    • Caplin 现在提供 WebSocket 支持。 HTTP Streaming 是一个不错的选择(正如我们都说过的那样),但是对于不支持 multipart/x-mixed-replace 内容类型的浏览器(除了 Firefox 之外的所有东西?)这意味着 XHR.responseText 会继续增长,并且在某个时候流连接必须断开并重新启动,否则浏览器最终会耗尽内存。
    • @DavidTitarenco,我很想知道您对为什么 Comet 延迟几乎 2 个数量级 在该基准测试中 Comet/long-poll 与 WebSocket 不同的看法.
    • @leggetter,你有任何关于 Caplin 的 HTTP 流延迟(往返)与 Caplin WebSockets 的数据吗?好奇的人想知道。
    • @kanaka 不幸的是,没有。但我会问。
    【解决方案4】:

    很难知道它与任何东西相比如何,因为我们不知道(平均)有效负载大小有多大。在底层(就像服务器的实现方式一样),HTTP 流和 websockets 几乎是相同的——除了最初的握手,显然在使用 HTTP 时更复杂。

    如果你用 C (ala Caplin) 编写了自己的 websocket 服务器,你可能很容易达到这些数字。大多数 websocket 实现都是通过现有的服务器包(如 Jetty)完成的,因此比较并不公平。

    一些基准测试
    http://webtide.intalio.com/2011/09/cometd-2-4-0-websocket-benchmarks/
    http://webtide.intalio.com/2011/08/prelim-cometd-websocket-benchmarks/

    但是,如果您查看 C 事件库基准,例如 libev 和 libevent,这些数字看起来更加性感:
    http://libev.schmorp.de/bench.html

    【讨论】:

    • 很好的链接,谢谢!实际上,HTTP 和 WebSockets 是完全不同的。 WebSocket 握手旨在与 HTTP 兼容,以便两个服务可以共享同一个端口。它们通常在同一台服务器中实现,但之后它们就大不相同了。一旦建立了 WebSocket 连接,它就是一个全双工和双向的原始通道(更类似于常规套接字)。在某些方面,WebSocket 握手实际上比普通 HTTP 更复杂,因为它允许 CORS 验证,并且握手中需要 SHA1 质询-响应。
    • 延迟,就从服务器到客户端的消息传递而言,在初始连接后即使不相同,也会非常相似。正如@kanaka 所指出的,WebSockets 的好处是,在建立连接之后,通道是全双工和双向的。因此,如果您要使用 HTTP 流对双向消息传递进行基准测试,其中客户端到服务器的通信需要第二个短暂的 HTTP,对于 WebSocket 连接,WebSocket 选项可能会非常优越。 Caplin 现在提供 WebSocket 支持,所以我相信他们可以击败 100k 连接。
    • @leggetter,我的理解是,通过 HTTP/1.0 的长轮询,您可以在两个方向上设置套接字和 HTTP 请求/响应标头。带有 keep-alive 的 HTTP/1.1 上的长池允许重新使用套接字,但我的理解是仍然发送/接收 HTTP 请求/响应标头。找到一个确凿的答案比我预期的要困难得多。我会对比较各种 Comet/AJAX/long-poll 解决方案的有线转储非常感兴趣,这样我就可以准确地看到正在发生的事情。
    • @kanaka 我对 HTTP/1.0 和 HTTP/1.1 上的 HTTP 长轮询的理解与您的相同。但是,HTTP Streaming 不同,因为消息之间的连接保持打开状态。如果使用 multipart-replace,则发送附加数据来分隔消息部分。但是标准流只是通过网络发送新数据而没有额外的标头,所以我认为它一旦建立起来就会像 WebSocket 连接一样高效。问题是responseText 缓冲区增长,最终需要断开连接并重新连接。
    • @kanaka 根据Wikipedia's entry on HTTP Pipelining keep-alive 仅在 Opera 中启用。通过this post about SPDY找到。