为什么在Web编程中接受轮询?


108

我目前正在研究一个Ruby on Rails项目,该项目显示图像列表。

该项目的必备条件是它可以实时显示新帖子,而无需刷新网页。经过一段时间的搜索,我偶然发现了一些JavaScript解决方案和服务,例如PubNub;但是,所有提供的解决方案都没有道理。

在JavaScript解决方案(polling)中,会发生以下情况:

  • 用户1查看照片列表。
  • JavaScript后台在后台每秒轮询一次端点,以查看是否有新帖子。
  • 用户2添加了新照片。
  • 在触发新周期并获取新数据之前,存在50 ms的延迟。
  • 新内容已加载到DOM中

当转换为真实示例时,这似乎很奇怪:

  • 用户1在他/她的桌子上拿着一堆照片。
  • 他/她每秒走到摄影师那里,问他是否有新摄影师。
  • 摄影师拍了一张新照片。
  • 当他/她走进来的那一秒钟,她可以拍照并将其放在桩上。

我认为解决方案应如下:

  • 用户1在他/她的桌子上拿着一堆照片。
  • 摄影师拍摄了一张新照片。
  • 摄影师走到那堆,其余的放在一起。

PubNub解决方案基本相同,但是这次双方之间有一个实习生步行以共享数据。

不用说,这两种解决方案都非常耗能,因为即使没有数据要加载,它们也会被触发。

据我所知,没有(逻辑)解释为什么几乎在每个实时应用程序中都使用这种实现方式。


195
暂时忽略Web浏览器不是可以接收传入连接的服务器...等等,不,不要忽略这一点。
GrandmasterB

17
@dennis:服务器和客户端之间的有状态的持久连接可能会摆脱轮询的需要,但这不是Web设计的方式。
FrustratedWithFormsDesigner 2014年

58
Websockets呢?
I.devries 2014年

25
或看看长时间的投票。基本上,您可以轮询,但是服务器在显示任何新数据之前不会响应。
Matsemann

53
在计算机空间中,有许多完全明智的解决方案和算法在肉类空间中是完全荒谬的。
whatsisname 2014年

Answers:


179

推送对于1个用户或数量有限的用户来说效果很好。

现在,请一位摄影师和1000位用户都想要照片的副本来更改场景。摄影师将不得不走到1000堆。他们中的一些人可能在上锁的办公室,或者散布在整个地板上。或他们的用户正在休假,对当前的新照片不感兴趣。

摄影师将一直忙于行走,并且不会拍摄新照片。

从根本上讲:拉取/投票模型可以更好地扩展到实时要求宽松的许多不可靠的读者(如果图片需要10秒钟才能到达一堆,那么有什么大不了的)。

也就是说,在许多情况下,推送模型仍然更好。如果您需要低延迟(在拍摄新照片后需要5秒钟),或者更新很少且要求频繁且可预测(每天拍摄新照片时请每10秒询问一次摄影师),那么拉动是不合适的。这取决于您要执行的操作。纳斯达克:推。气象服务:拉。婚礼摄影师:大概拉。新闻摄影社:可能会推。


32
我非常喜欢您与1000个用户的类比,有些人正在休假,有些人不感兴趣。+1。
riwalk

4
@EsbenSkovPedersen:套接字限制不是由于IP地址引起的。这是由于最大的打开文件描述符。因此,打开套接字的最大数量与您使用的IP地址无关。
slebetman 2014年

10
坦率地说,这是一个可怕的比喻。为了使推送生效,任何用户的客户端都必须维护某种开放连接。实际上,轮询是对连接的仿真。并非因为某些客户端正在轮询,所以通知了所有客户端。同样,当某些客户端打开用于推送通知的连接时,不会通知所有客户端。这是非常可怜的建议,邀请您将资源扔出窗外。每秒受到10000个请求的轰炸实际上比维护10000个开放套接字便宜,或者说比它更好。
back2dos 2014年

8
@ptyx:1s间隔是此处讨论的间隔。每秒10k请求意味着10k TCP握手和10k HTTP请求(每个请求可以轻松达到2KB),这使服务器受到的背景噪声增加了多个数量级。有许多经过战斗测试的库使推送订阅与进行轮询一样容易。甚至有像meteor.js之类的框架都可以完全抽象出整个问题。在没有任何进一步解释的情况下呼吁可伸缩性也几乎不是争论。无论如何,我已经表达了我的疑问,并且不想开始讨论;)
back2dos 2014年

5
我同意back2dos的上述意见。如果pull的规模要比push更好,则google,堆栈交换,facebook,在线股票服务等将使用pull技术。但是他们没有。从根本上讲,锤击服务器而不是设置侦听站会产生巨大的规模。主要服务避免轮询。
特拉维斯J

106

我真的很惊讶只有一个人提到WebSockets。支持基本上在所有主流浏览器中实现

实际上,PubNub使用它们。对于您的应用程序,浏览器可能会订阅一个套接字,只要有新照片可用,该套接字就会广播。请注意,套接字不会发送照片,而只是发送链接,以便浏览器可以异步下载照片。

在您的示例中,设想如下:

  1. 用户让摄影师知道他想知道所有未来的照片
  2. 摄影师在扩音器上说有一张新照片可用
  3. 用户要求摄影师拍照

这有点像您的原始示例解决方案。它比轮询更有效,因为客户端无需将任何数据发送到服务器(也许心跳除外)。

另外,正如其他人提到的那样,还有其他一些方法比在较旧的浏览器中运行的简单轮询更好(longpolling等)


43
@RobertHarvey为什么WebSockets与问题无关?这个问题问轮询是否是可以接受的策略,如今显然已经不能接受(或者至少不是最佳选择)。WebSockets,服务器发送的事件和长时间轮询在几乎每个用例上的性能要好得多。
法布里西奥磨砂

7
@RobertHarvey只是我的解释,据我所知,没有重新定义。当然,这个问题问为什么它仍然被接受,而不是什么是最佳策略,但是这些仍然紧密相关。
法布里西奥磨砂

25
WebSocket(以及类似的东西)是您最接近实现OP的“解决方案”的方法,因此,尽管他没有特别提及,但我认为这非常相关。
korylprince 2014年

6
更不用说,StackExchange像您现在所访问的网站(除非您正在缓存/保存的该网页)这样的网站都可以使用WebSockets。这就是为什么我也想知道为什么直到@korylprince都没有人提到WebSockets
trysis

6
@FabrícioMatté:实际上,不是每个用例。长轮询要求为每个用户打开一个套接字,这会占用系统资源。对于时间不是很紧迫但有很多用户的服务,保持套接字打开通常比不时为短的304服务更昂贵。对于大多数服务而言,轻微的延迟不是问题。一台机器通常可以通过轮询而不是推送来服务更多的客户端。
Lie Ryan

42

有时足够好就足够了。

在实现“实时”通信过程的所有可能方式中,轮询可能是最简单的方式。当轮询间隔较长时(即秒,分钟或小时,而不是瞬时),并且通过检查连接或资源消耗的时钟周期并不重要,可以有效地使用轮询。


3
这个,这个的一千倍。之所以被接受是因为它通常足够好。
corsiKa 2014年

1
这是一个足够好的答案
Zain R

31

HTTP协议受到限制,因为客户端必须是发起请求的客户端。除非响应客户端的请求,否则服务器无法与客户端通信。

因此,要调整您的真实示例,请添加以下约束:

  • 用户2只能用一句话答复来回答用户1的问题,此后用户1必须离开。用户2没有其他通信方式。

有了这种新的约束,除了轮询之外,您将如何做?


6
HTTP 2.0将支持服务器推送。“推送允许服务器在没有明确请求的情况下将表示发送给客户端。” en.wikipedia.org/wiki/HTTP_2.0
Kaptan酒店

5
@kaptan,太好了,但是不可用。利用您已有的资源。
riwalk

7
现在还提供长轮询功能,并使用拉力模拟推力模型。
Tim B

24
@dennis:编写了工业自动化软件后,我只想评论一下您对传感器的轮询示例。轮询传感器有两个目的-最明显的是获取新数据。不太明显的是检测传感器是否还活着,没有因为错误或由于工厂火灾而燃烧或由于工业事故而融化而崩溃。沉默(您没有收到回复)也是有价值的数据。
slebetman 2014年

3
@dennis传感器的感应速度通常比您对数据感兴趣的速度快得多。通过轮询,您可以准确地在需要的时候获取传感器值,而不会被无关紧要的更新所淹没。(想象一下,如果每次文件更改到磁盘上的任何位置时,操作系统都会通知您的应用程序,而不是您的应用程序需要打开和读取文件)
immibis

13

为什么接受轮询?因为实际上每个解决方案实际上都是底层轮询!

如果服务器应在有新图片可用时立即更新您,则它通常必须与您建立连接-因为IP地址经常更改,并且您永远不知道某人是否不再感兴趣,因此客户端必须发送某种形式的保持活动状态信号,例如,“我还在这里,我没有离线”

所有状态连接(例如TCP / IP)的工作方式相同,因为您只能通过Internet发送单个数据包;你永远不知道对方是否还在。

因此,每个协议都有超时。如果一个实体在X秒钟内没有回答,则认为该实体已死亡。因此,即使您在服务器和客户端之间只有打开的连接而没有发送任何数据,服务器和客户端也必须发送常规的保持活动数据包(如果您在它们之间打开连接,则会处理低级数据包)-以及如何这到底和投票有什么不同?

因此,最好的方法可能是长轮询:

客户端在加载网站后立即发送请求(例如,告诉摄影师“告诉我是否有新照片”),但是如果没有新照片,服务器不响应。一旦请求超时,客户端就会再次询问。

如果服务器现在有任何新图片,它可以立即回答排队等待新图片的所有客户端。因此,新图片后的响应时间比推入时间还要短,因为客户端仍在等待打开的连接中的答复,而您不必建立与客户端的连接。而且,来自客户端的轮询请求不会比客户端和服务器之间不断连接的答案带来更多的流量!


我不同意每个解决方案最终都是低级轮询。您将发送数据所需的轮询与了解客户端何时丢失所需的轮询相混淆。是的,后者总是会在协议栈的某个地方进行轮询,但是轮询频率可能非常低(例如每五分钟一次),而每秒轮询实际数据是浪费,可以通过真正的推送通知来避免不会在堆栈的任何级别进行轮询。
Allon Guralnek 2014年

首先,最活跃的数据包以相当高的频率运行,因为您要避免常见的超时间隔,因此TCP / IP很少会出现几秒钟的情况,并且几乎所有不使用tcp的内容都可能被防火墙阻止。因此,当我需要每X秒发送一次数据包时,为什么不以几乎没有任何费用的方式填充一些数据呢?
Falco 2014年

1
即使您的连接保持间隔为5分钟,@ Guralnek的超时也会更高,因为您必须添加实际的延迟和丢失的数据包。并且在客户端断开连接后,服务器将在5分钟内保持许多连接,因此总体而言,这可能会花费更多的服务器资源,同时仅节省最少的带宽
Falco 2014年

1
+1表示长时间轮询。查找彗星en.wikipedia.org/wiki/Comet_%28programming%29
Zan Lynx

9

轮询的一个优点是,它限制了消息丢失或某些状态出现故障时可能造成的危害。如果X每五秒钟向Y询问其状态一次,那么丢失请求或答复只会导致X的信息过期而不是5秒钟,而不是5秒钟。如果Y重新启动,X可以在下一个发现它时间Y能够响应X的消息之一。如果X重新启动,则以后可能再也不需要向Y询问任何事情,但是观察X状态的人应该认识到X已经重新启动。

如果在状态改变时X依赖X而不是X轮询Y,则如果Y的状态改变并向X发送了一条消息,但是由于某种原因未收到该消息,X可能永远不会意识到这种改变。同样,如果Y重新启动并且从没有任何理由向X发送有关任何消息的消息。

在某些情况下,X可以请求Y周期性地或在其更改时自动发送状态信息,并且仅在X轮询时间过长而没有听到Y的任何信息时才进行X轮询可能会有所帮助。这种设计可以消除X需要发送其大部分消息(通常,X至少应至少偶尔通知Y,它仍然有兴趣接收消息,如果Y太长而没有任何兴趣,则Y应该停止发送消息)。但是,这样的设计需要Y 持续地维护有关X的信息,而不是简单地向答复它的人发送答复,然后立即忘记是谁。如果Y是嵌入式系统,则这种简化可以帮助减少内存需求,从而允许使用更小,更便宜的控制器。

当使用可能不可靠的通信介质(例如UDP或无线电)时,轮询可以具有其他优势:它可以在很大程度上消除对链路层确认的需求。如果X向Y发送状态请求Q,Y则以状态报告R进行响应,并且X听到R,X不需要听到任何形式的链路层确认,Q便可以知道它已被接收。相反,一旦Y发送了R,就不需要知道或关心X是否收到了R。如果X发送状态请求而没有响应,则可以发送另一个。如果Y发送报告而X听不到,则X将发送另一个请求。如果每个请求都发出一次,或者没有响应,那么任何一方都不需要知道或关心是否收到了任何特定的消息。由于发送确认可能消耗的带宽几乎等于状态请求或报告的带宽,使用来回的请求报告不会比未经请求的报告和确认花费更多。如果X发送了一些请求而没有得到答复,则它可能在某些动态路由的网络上需要启用链路级确认(并在其请求中询问Y也这样做),以便基础协议堆栈可以识别传递问题并搜索一条新的路线,但是当一切正常时,请求报告模型将比使用链接级确认更为有效。


您将Y消息推送到X(第二段)时遇到的问题可以通过在每个消息上附加一个序列号来解决。如果消息丢失,X将知道是因为它没有收到该序列号。到那时,还可以采取其他措施与Y同步。DNS主站->从站复制以这种方式工作。
korylprince 2014年

@korylprince:如果另一方有机会发送某事(并且成功发送),或者有理由期望另一方从没收到任何消息,则任何一方都可以找到丢失的消息。如果一方发送状态更新,或者不需要确认或在重试几次后放弃,而另一方不希望进行预定的传输,则另一方将不知道该连接已消失。
超级猫

2
@korylprince-问题是,如果没有定期发送的消息,X可能会晚一天或晚一年或晚10年检测到丢失的消息。为了在合理的时间内检测到丢失的数据包,您需要进行某种轮询。您可以“拉”投票,也可以“推”投票。第一个称为“轮询”,第二个称为“心跳”
slebetman 2014年

两者都很真实。一切都取决于情况。
korylprince 2014年

@slebetman:如果没有定期的消息,如果Y重新启动,则X可能没有发现它的机制。
超级猫2014年

1

问题是要平衡不必要的民意测验量与不必要的推送量。

如果您轮询:

  • 这时您会得到答案。如果您此时仅询问或需要数据集,则很好。
  • 您可能会得到“无内容”答案,从而导致线路上无意义的负载。
  • 仅在轮询时才将负载放在线路上,但始终在轮询时才施加负载。

如果您按下:

  • 您可以正确地提供答案,从而可以在客户端立即进行处理。
  • 您可能将数据传递给对此数据不感兴趣的客户端,从而导致线路上无意义的负载。
  • 每当有新数据时,就将负载放在线路上,但只有在有新数据时才进行。

关于如何处理各种情况及其缺点,有多种解决方案,例如,两次轮询之间的最短时间,从轮询中移除主要系统的代理,或者-对于推送-一项注册和指定的法规所需数据,然后在注销时注销。通常,您无法说出最合适的那个,这取决于系统。

在您的示例中,轮询不是最有效的解决方案,而是最实用的解决方案。用JavaScript编写轮询系统非常容易,并且在交付端也很容易实现。制作图像数据的服务器应该能够处理额外的请求,如果没有,则可以线性扩展,因为数据大部分是静态的,因此可以轻松缓存。

一种实现登录,所需数据描述并最终注销的推送方法将是最有效的,但对于一般的“脚本骗子”来说可能太复杂了,并且需要处理以下问题:如果用户只是关闭浏览器并且无法执行注销?

也许拥有更多的用户(因为访问很容易)比在其他缓存服务器上节省一些钱更好?


1

由于某些原因,这些天来,所有年轻的Web开发人员似乎都忘记了过去的教训,以及为什么某些事情以他们的方式发展了。

  1. 带宽是一个问题
  2. 连接可能是间歇性的。
  3. 浏览器没有那么多的计算能力
  4. 还有其他访问内容的方法。网络不是w3。

面对这些限制,您可能无法保持恒定的双向通讯。而且,如果您查看OSI模型,则会发现大多数注意事项都是为了将​​持久性与基础连接脱钩。

考虑到这一点,提取信息的轮询方法是减少客户端带宽和计算的一种好方法。实际上,推送的兴起实际上只是客户端进行持续轮询或Web套接字。就我个人而言,如果我和其他人一样,我会喜欢轮询作为流量分析手段的常规性,在这种情况下,超时的GET / POST请求将使一个人处于某种中间状态。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.