理解HTML5数据推送应用开发问题

一、数据推送

  SSE是一种允许服务端向客户端推送新数据(通常称作数据推送)的HTML5技术。那么,究竟什么是数据推送?它与我们可能用过的其他技术有什么不同呢?

  让我先来回答什么不是数据推送。数据推送有两种替代方案:无更新方案和数据拉取方案。

  无更新方案是最简单的。这几乎是所有网络内容的运作方式。

  替代方案之一:无更新方案

  在浏览器中输入一个URL,然后你就会得到一个HTML页面。之后浏览器会请求图片、CSS文件、JavaScript文件等。它们每一个都是浏览器可以缓存的静态文件。如果你正使用的是后端语言,比如PHP、Ruby、Python或其他许多为用户动态生成HTML的语言,就浏览器而言,它接收到的HTML文件与手写的静态HTML文件没什么区别。(是的,我知道你会说你可以命令浏览器不缓存内容,但这不是重点,它还是静态的。)

  另一种方案是数据拉取

  替代方案之二:数据拉取

  浏览器会基于一些用户行为,或在一定时间之后,或基于某种别的触发方式,向服务端请求部分或全部最新数据。使用这种简单粗暴的方式,通过JavaScript或者一个meta标签都能命令整个页面重新加载。要做到这一点,页面要么是由服务端语言自动生成,要么是定期更新的静态HTML页面。

  在更复杂的情况下,Ajax技术只被用于请求最新数据,当收到数据时,JavaScript函数会利用它来局部更新DOM。这里有一个很重要的概念:仅请求最新数据,而不是整个HTML页面结构。这正是我们所说的数据拉取的要义:仅拉取新数据,并且只更新页面中受到影响的部分。

  这些都不是数据推送。数据推送不是静态文件,也不涉及浏览器为最新数据而发起请求。数据推送是由服务端选择向客户端发送新数据

 

  数据推送

  当数据源有新数据时,服务端能立刻将它发送给一个或多个客户端,而不用等客户端来请求。这些新数据可以是突发新闻、最新的股价、来自线上朋友的聊天信息、新的天气预报、策略游戏中的下一步等。

  数据拉取和数据推送的功能目标是一样的:让用户看到最新数据。但数据推送有一些优势。或许最大的优势就是更低的延迟。假设一个数据包在服务端与客户端之间的传输时间是100毫秒,数据拉取客户端以10秒为间隔轮询拉取。用数据推送方式,客户端会在服务端读入数据100毫秒后看到数据;用数据拉取方式,客户端会在服务端读入数据100至10 100毫秒(平均5100毫秒)后看到数据:这都取决于轮询请求的时间选择。平均来看,数据拉取方式的延迟是数据推送方式的51倍多。如果数据拉取方式改为每2秒轮询一次,平均时间会下降到1100毫秒,仅仅是数据推送方式的11倍多。但是,如果没有新数据到达,这也会导致更多的请求和资源(比如带宽、CPU等)浪费。

  在数据拉取方式中,权衡会让你很纠结:要缩短延迟就要提高轮询频次;要节省带宽和连接就要降低轮询频次。延迟和带宽,哪个更重要?当你说“都重要”,那就是你需要数据推送技术的时候了。

  数据推送的其他名称

  对数据推送的需求可以追溯到Web诞生的时候(注:如果你认为数据推送和数据拉取只在Ajax(在2005年开始流行)出现之后才成为可能,再想想,Flash 6在2002年3月发布,提供了可以实现数据推送和数据拉取的Flash Remote技术,并且还不用因浏览器不同而烦恼(那时几乎每个人的浏览器上都装了Flash)

  多年来,人们找到了很多新奇的解决方案,其中大部分都存在人们不期望看到的折中方式。你也许听说过一些其他的技术:Comet、Ajax Push、Reverse Ajax、HTTP Streaming,还一直在想它们之间有什么不同。实际上,这些都属于我们之前探讨的向后兼容解决方案。后来又增加了SSE,它是一种兼具易用性和高效性的新增HTML5技术。如果你的浏览器支持SSE,它总是比Comet技术优越。(注:并不是总是,要根据不同情况,参考上一篇讨论的长轮询是否总是比常规轮询好

二、可能会用到SSE的应用

  SSE对什么有用?当你需要用新数据局部更新网络应用时,SSE便脱颖而出,它不会要求用户执行任何操作。我们将以一个推送外汇价格的应用为例,探索如何实现数据推送和SSE。我们的目标是每当经纪商那里的欧元/美元(欧元兑美元)汇率变化时,新的价格会出现在浏览器上,尽可能像现实中一样及时

  这个例子完全适用于SSE处理资料传送的标准:更新频繁、低延迟,并且数据都是从服务端到客户端(客户端不需要将价格数据推送回服务器)。我们示例中的后端会使用杜撰的价格数据,但使用真实数据(无论是汇率还是其他数据)的时候其实都一样。

  稍微有点想象力,你就能明白这个例子如何应用到其他领域:在拍卖网络应用中推送最新出价,在售书网站推送新评论,在在线游戏中推送新高分,推送你感兴趣的最新微博或用你感兴趣的关键词推送新闻类文章。

  还有一些应用会发一些提示,比如像Facebook这样的社交网站,当有新消息到来时,应用的某个位置会出现一个浮层,然后渐渐隐去;或者像Gmail这样的邮箱服务界面,每当有新邮件时会在你的收件箱里现新的提示;或者连上日历,在会议即将开始前给你发送一条通知;或者在你的某个服务器上的磁盘使用率增高时向你提出警示……尽情畅想吧!

  聊天类应用呢?聊天由两部分组成:在聊天室中接收其他人的信息(也可以是其他动态,比如成员进出聊天室、资料修改等);发送你自己的信息。这种双向沟通一般非常适合用WebSocket(稍后我们会具体看一下),但这并不意味着它不适合用SSE。发送自己信息的部分,用古老的Ajax请求的方式就挺好。

  作为适合用SSE的聊天类应用范例,它可用来推送你感兴趣的微博,与此同时,用一个独立的连接供你撰写自己的微博。或者想象一个在线游戏:用SSE将新分数发送给所有玩家,而你只需设法在游戏结束时把每个玩家的最终分数发到服务器。或者想象一个多人的实时策略游戏:当前面板位置不断更新,并且通过SSE分发给所有玩家,然后在你需要将某个玩家的动作发送到中央服务器时使用Ajax通道。

三、和WebSocket的对比

  你可能听说过另一种叫做WebSocket的HTML5技术,它也能从服务端向客户端推送数据。那如何决定你是用SSE还是WebSocket呢?概括来说,WebSocket能做的,SSE也能做,反之亦然,但在完成某些任务方面,它们各有千秋。

  WebSocket是一种更为复杂的服务端实现技术,但它是真正的双向传输技术,既能从服务端向客户端推送数据,也能从客户端向服务端推送数据。

  WebSocket和SSE的浏览器支持率差不多,大多数主流桌面浏览器两者都支持。在Android 4.3以及更早的版本中,系统默认浏览器两者都不支持,Firefox和Chrome则完全支持;Android 4.4中,系统默认浏览器两者都支持;Safari从5.0开始支持SSE(iOS系统从4.0开始),但直到6.0才正确地支持WebSocket(6.0之前的Safari所实现的WebSocket协议存在安全问题,所以一些主流浏览器已经禁用了基于这个协议的实现)。IE是个例外,即便IE11都还不支持原生SSE,IE10添加了WebSocket支持。

  与WebSocket相比,SSE有一些显著的优势。我认为它最大的优势就是便利:不需要添加任何新组件,用任何你习惯的后端语言和框架就能继续使用。你不用为新建虚拟机、弄一个新的IP或新的端口号而劳神,就像在现有网站中新增一个页面那样简单。我喜欢把这称为既存基础设施优势

  SSE的第二个优势是服务端的简洁,服务端代码只需几行。相对而言,WebSocket则很复杂,不借助辅助类库基本搞不定(我试过,令人痛苦)。

  因为SSE能在现有的HTTP/HTTPS协议上运作,所以它能直接运行于现有的代理服务器和认证技术。而对WebSocket而言,代理服务器需要做一些开发(或其他工作)才能支持。

  SSE还有一个优势:它是一种文本协议,脚本调试非常容易。

  不过,这就引出了WebSocket相较SSE的一个潜在优势:WebSocket是二进制协议,而SSE是文本协议(通常使用UTF-8编码)。当然,我们可以通过SSE连接传输二进制数据:在SSE中,只有两个具有特殊意义的字符,它们是CR和LF,而对它们进行转码并不难。但用SSE传输二进制数据时数据会变大,如果需要从服务端到客户端传输大量的二进制数据,最好还是用WebSocket。

  WebSocket相较SSE最大的优势在于它是双向交流的,这意味向服务端发送数据就像从服务端接收数据一样简单。用SSE时,一般通过一个独立的Ajax请求从客户端向服务端传送数据。相对于WebSocket,这样使用Ajax会增加开销,但也就多一点点而已。如此一来,问题就变成了“什么时候需要关心这个差异?”如果需要以1次/秒或者更快的频率向服务端传输数据,那应该用WebSocket。0.2次/秒到1次/秒的频率是一个灰色地带,用WebSocket和用SSE差别不大;但如果你期望重负载,那就有必要确定基准点。频率低于0.2次/秒左右时,两者差别不大。

  从服务端向客户端传输数据的性能如何?如果是文本数据而非二进制数据(如前文所提到的),SSE和WebSocket没什么区别。它们都用TCP/IP套接字,都是轻量级协议。延迟、带宽、服务器负载等都没有区别,除非……呃?除非什么?

  当你在享用SSE的既存基础设施优势,并在客户端和服务端脚本之间设了一个网络服务器,区别就显现出来了。一个SSE连接不仅使用一个套接字,还会占用一个Apache线程或进程,如果用PHP,它会为这个连接专门创建一个PHP新实例。Apache和PHP会使用大量的内存,这会限制服务器所能支持的并行连接数。所以,要做到用SSE在数据传输性能上和WebSocket完全一样,需要写一个你自己的后端服务器,当然,那些在任何情况下都会用自己的服务器并使用Node.js的人,会觉得这有什么稀奇的。

四、如何选择

  前面两节从正反两方面探讨了数据拉取、SSE和WebSocket,但怎么知道哪个更适用?这个问题很复杂,它是以应用的表现、用户对延迟的预期相关的商业决策、主机费用方面的商业决策以及用户和你的开发人员使用的技术为基础的。这里有一些你需要自行思考的问题。

1、服务端事件发生得有多频繁?

  频率越高越适合用数据推送(不论SSE还是WebSocket)。

2、客户端事件发生得有多频繁?

  如果事件触发的频率低于0.2次/秒,尤其是低于1次/秒,用WebSocket比用SSE好。如果频率低于0.1次/秒到0.2次/秒左右,那用哪个都可以。

3、服务端事件是不是不但发生频率不高而且还发生在可预见的时刻?

  当这些事件的触发频率低于1次/分钟,用数据拉取更好,因为它不需要保持一个套接口一直打开。需要注意大量客户端同时试图连接服务器的情况。

4、延迟问题有多关键?给个量化的数据

  半秒延迟是否会让用户烦躁?60秒的延迟是否也不是什么问题?用户越介意延迟,数据推送比数据拉取就越有优势。

5、是否需要从服务端向客户端推送二进制数据?

  如果有大量的二进制数据,用WebSocket比用SSE更好(在这方面XHR轮询也比SSE更好)。如果是少量的二进制数据,可以对它进行编码,然后用SSE,区别(相对WebSocket)是会多几百字节。

6、是否需要从客户端向服务端推送二进制数据?

  用XMLHttpRequest9(比如Ajax,这是SSE从客户端向服务端发送信息的方式)和用WebSocket处理二进制数据没什么区别。

7、大部分的用户是用有线连接还是移动连接

  使用LTE WiFi路由器或者被限速的笔记本用户,视为移动连接用户;通过很强的WiFi连接到光纤上游连接的手机用户,视为有线连接用户。重要的是连接,而不是电脑性能或屏幕尺寸。

  要知道,移动连接会有更多的延迟,尤其是当连接需要唤醒的时候。这使得在移动连接上使用数据拉取(轮询)方案比在有线连接上要糟糕。

  同样,一个超载的WiFi连接(比如在一个有很多人的咖啡馆)会丢失越来越多的数据包,其表现更像一个移动连接,而不是有线连接。

8、耗电量是否是移动用户关注的重点?

  需要在延迟和耗电量之间做出权衡。数据拉取(除非你知道数据什么时候出现,从而预知什么时候开始轮询)通常比数据推送(SSE或WebSocket)要糟糕。

9、推送的数据是否相对来说比较小?

  一些3G移动连接有一个专门的低电量模式,可以用来传输小数据(200 bit/s到1000 bit/s)。但那不是重点,更重要的是一个大的消息会被分割成TCP/IP片段发送,有一个片段丢失,就要重发。TCP会确保按发送数据的顺序接收数据,所以这个丢失的数据包会阻碍整个数据的处理,同时也会阻塞后面数据的接收。所以,在不稳定的连接中(比如,移动连接,超载的WiFi连接),发送的数据越大,需要发送的额外数据包就越多。考虑一下用数据推送作为控制通道,告诉浏览器直接请求大文件,浏览器很有可能用自己的套接字处理,因此不会阻塞数据推送套接口(这之所以存在,是因为你认为延迟问题很重要)。

10、数据推送是Web应用的次要特性还是主要特性?是否有开发者资源的短缺?

  SSE用起来简单,而且是简洁地运行在现有的基础设施上,比如Apache。这就节省了测试时间。项目越大,你拥有的开发人员越多,这个问题就越不重要。

posted @ 2018-06-18 21:28  古兰精  阅读(417)  评论(0编辑  收藏  举报