分类: javascript      标签:Comet, 长连接, 浏览器同步     

多窗口同步-基于Comet和LocalStorage

聊天、消息通知等Web应用需要在多个浏览器及其Tab之间同步信息,以便维持各个窗口相同的状态,并防止重复提醒。为了实现同步功能,各窗口之间需要能够共享数据,Web应用在这种情况下可用的方法只有前端的本地存储和后端的服务器存储。WebSocket这种双向通信机制,目前在浏览器和服务器端支持情况都不是很健全,因此不在考虑范围。如果信息能够推送到浏览器,而不是由浏览器定时轮询,那么就能保证内容的实时性,就能很好地实现同步功能。

多个窗口展现同步,最理想的状态当然是只依靠前端本地存储。这样只要有一个窗口变化,将变化记录在本地,并由其他窗口调用,维持同步即可。

前端存储

完整的前端本地存储,参见前端存储的前世今生。这里只讨论最成熟广泛的两种。

Cookie 可以被所有浏览器共享,支持广泛。但是它的缺陷也是很明显的,每个站点限制数目,且每个Cookie也被限制在4KB,每次传送到服务器等等。为了实现同步,只能不断轮询,实时与性能之间的反比关系,必然使这种同步不能真正完美。

localStorage 作为一项HTML5涉及到的新技术,它实现的本地存储数据只能在同一个浏览器内同域名共享,其他浏览器是不能访问到存储的内容的,更谈不上多浏览器之间的同步了。

尽管前端本地存储有很多种,但适合当前我们使用的技术,也是最被广泛使用的只有上面这两种。前端的种种限制下,实现同步肯定是不可能的,因此只能求助于后端服务器。

关于HTML5的本地存储,不止localStorage一种,其他详细介绍及区别参见HTML5 本地存储

服务器端推送

为了完成实时通信,最好服务器端有数据时向前端推送,而不是由前端去发起一次次的查询请求。

Comet 是基于HTTP长连接的“服务器推送”技术,之所以引号括起,是因为这不是真正的推送。HTTP协议规定了浏览器发起请求,服务器只能被动响应请求,这是B/S结构的特点。因此,服务器主动推送在HTTP协议下是不可能的。简单来讲,Comet 是指浏览器向后端发送一个请求,服务器在没有数据的时候不会返回结果,阻塞这个连接,即这个请求一直处于等待状态,等到服务器发现有要给浏览器的数据时,再返回数据,浏览器处理返回结果。

浏览器与服务器数据交互常用的只有两种方式。一种是Ajax技术,它不允许跨域。另一种是叫JSONP,即利用浏览器中可以请求任意域名的js文件的特性解决跨域问题,返回的js文件中直接调用现有的全局函数,由于长连接时js被阻断,需要在iframe中获取。JSONP另一个问题是iframe在IE中会导致窗口显示加载未完成状态,因此需要用htmlfile的流方式,具体参见Comet技术介绍

其实Comet只是一个技术的名字,这一技术没有添加任何新的东西,用的仍是原有的技术。

基于上述技术,可以用Comet技术应用“服务器推送”功能,前端有变化时,经过服务器推送结果完成同步。因为长连接不可避免地消耗服务器资源,因此对于多个Tab的浏览器,可以在同一浏览器内部用localStorage技术存储长连接的控制权限,保证只有一个获取长连接的请求权,当长连接返回时,更新localStorage数据,其他Tab监控它的变化,做出同步反应。

现实中应用

开心网的IM聊天是用Ajax完成前后端的交互,这点可以在Chrome的开发者工具中的Network中监视到。实际上,im的服务器与主站不同域,因此将长连接Ajax请求放到了一个iframe中,再改变这个iframe的document.domain属性值与主站一致,这样就能操作主站中的方法。

在web版的qq中,它采用和开心网同类的技术监听有其他qq登录的情况。这个iframe的路径是 http://d.web2.qq.com/proxy.html ,里面的代码没有压缩,可以清楚看到设置document.domain和用原生js封装ajax的方式,看这个会更明了其原生态的请求方式。

人人网的IM聊天,只是简单地使用了localStorage,只能在同一浏览器不同窗口间实现同步,不支持不同浏览器的同步。

新浪微博的IM聊天也是用Comet实现。它采用JSONP与后端交互,多个Tab窗口就采用多个长连接。如下图是它的创建iframe部分代码:

新浪微博im聊天Comet创建iframe
*新浪微博im聊天Comet创建iframe(IE中使用htmlfile流)

人人的服务器采用的是nginx;微博的是jetty;开心网返回的是Apache,但Apache显然不适用于大量的连接,不知是故意返回这个还是其他什么原因;qq干脆不输出服务器类型,但 s.web2.qq.com 仍然暴露出用的是nginx。

总结

使用Comet实现的长连接完成即时通讯,很容易就能看到现成的例子,但同一浏览器的多个窗口间我们可以用localStorage/userData实现共享一个长连接,以减轻服务器的负荷,也是对网站性能的一个优化。

具体实践将在新版im中探索。

-EOF-


blog comments powered by Disqus