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

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

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


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

翻译成现实示例时,这似乎很奇怪:


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

我认为解决方案应该如下:


用户1在他/她的桌子上拿着一堆照片。
摄影师拍摄一张新照片。
摄影师走到

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

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

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

评论

暂时忽略Web浏览器不是可以接收传入连接的服务器...等等,不,我们不要忽略它。

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

Websockets怎么样?

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

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

#1 楼

推送适合1个用户或数量有限的用户。

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

摄影师将一直忙于行走,而不是拍摄新照片。

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

评论


我非常喜欢您与1000位用户的比喻,有些人在度假,有些人不感兴趣。 +1。

–riwalk
2014年7月22日18:59



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

– slebetman
2014年7月23日14:11在

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

–back2dos
2014年7月23日15:00

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

–back2dos
2014年7月23日下午16:48

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

–特拉维斯J
2014年7月23日在23:02

#2 楼

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

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

在您的示例中,您可以想象以下内容:


用户让摄影师知道他想知道所有将来的照片
摄影师在扬声器上说有新照片可用
用户要求摄影师提供照片

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

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

评论


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

–FabrícioMatté
14年7月22日在22:58

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

–FabrícioMatté
2014年7月22日23:01



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

–korylprince
2014年7月23日在2:48

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

–trysis
2014年7月23日下午5:10

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

– Lie Ryan
2014年7月23日下午13:22

#3 楼

有时足够好就足够了。

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

评论


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

– corsiKa
2014年7月24日在18:19

这是一个足够好的答案

– Zain R
2014年11月25日的1:39

#4 楼

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

因此,为了调整您的实际示例,请添加以下约束:


用户2可以仅用一句话回答用户1的问题,然后用户1必须离开。用户2没有其他通信方式。

有了这种新的限制,除了轮询之外,您还可以怎么做?

评论


HTTP 2.0将支持服务器推送。 “按下允许服务器发送表示到客户端,而无需明确请求。” zh.wikipedia.org/wiki/HTTP_2.0

–卡普坦
2014年7月22日在19:28

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

–riwalk
2014年7月22日在20:36

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

– Tim B
2014年7月22日在21:02



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

– slebetman
2014年7月23日下午4:16

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

–user253751
2014年7月23日在11:04



#5 楼

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

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

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

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

所以最好的方法可能是长轮询:

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

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

评论


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

– Allon Guralnek
2014年7月25日在6:29

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

– Falco
2014年7月25日在7:26

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

– Falco
2014年7月25日在7:30

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

–赞·山猫
2014年11月24日13:57

#6 楼

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

如果X不是轮询Y,而是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年7月23日在2:51



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

–超级猫
2014年7月23日在3:42

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

– slebetman
2014年7月23日下午4:19

两者都很正确。一切都取决于情况。

–korylprince
2014年7月23日11:30

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

–超级猫
2014年7月23日14:34在

#7 楼

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

如果您民意调查:


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

如果按下:


,则可以在正确的情况下立即提供答案,从而可以在客户端立即进行处理。
您可能会将数据传递给对这些数据不感兴趣的客户端,从而导致线路上无意义的负载。
每次有新数据时(只有在有新数据时)才将负载放在线路上。

对于如何处理各种情况及其缺点,有几种解决方案,例如两次轮询之间的最短时间,仅轮询代理可以减轻主系统的负载,或者-推送-一种注册和指定所需数据的规则,然后在注销时注销。通常,您不能说哪种最合适,取决于系统。

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

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

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

#8 楼

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


带宽是一个问题
连接可能是断断续续的。
浏览器没有那么多的计算能力。
还有其他访问内容的方法。网络不是w3。面对这些限制,您可能没有恒定的双向通信。而且,如果您查看OSI模型,您会发现大多数注意事项都是为了将​​持久性与基础连接脱钩。

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