2010年6月19日星期六

2010/6/9 Ajax的同步调用场景——使用同步Ajax在 onunload 事件时通知服务器

在一种场景下,server端维护了浏览器客户端的状态信息,当浏览器关闭时需要立刻通知server用户已经离开了,server端好清理状态。这种情况的典型案例就是:浏览器客户端的即时通信,即web IM。因为用户关闭了窗口就表明用户已经不在线了,所有对话都结束了,server端和对话的另一方需要立刻知道这个状态变化。而一般情况下,server端并不需要立刻通知的情况,那就无所谓了,只要过期超时即可。

要想在用户关闭浏览器后,server端得到通知,一般有两种做法:
方法一:通过 javascript 轮询的方式向 server 发送心跳(使用异步ajax),server端记下上次心跳时间,server端Timer定期检查会话的心跳时间是否超时来判断用户是否已经离开了。这种方法的缺点是,要想即时通知就得把心跳间隔时间设得短,server端检查的时间也要短,这样会导致server端的负担大大增加,所能承载的用户就会较少。
方法二:不采用发送心跳的方式,而是在用户关闭浏览器时,通知server即可。这样server端的负担就小了。
window.onunload = function(){
ajax.callServer();
}

重点说一下方法二的实现。
首先,页面上注册 onunload 事件。如 body元素的 onunload 属性,或直接设置 window.onunload 。
注意是 onunload 而不是 onbeforeunload 事件,因为用户可以在 onbeforeunload 事件时选择“取消”从而不关闭窗口,而onunload 事件意味着窗口肯定是要被关闭的。

其次,在onunload事件中调用同步的ajax请求通知server,而非默认的异步ajax请求。因为调用异步的ajax请求在一些浏览器下无法真正使server得到通知,实际上根本无法保障!在发出了这个异步请求之后,浏览器就要关闭窗口并清理所有相关资源,因为这是异步请求,所以浏览器有理由认为无需等待该请求完成即可开始关闭窗口,这样该请求就可能失败(IE上就是这样,用异步请求无法通知到server)。这也解释了为什么异步callServer()调用后面再加个alert语句就没问题了,因为alert语句在后面挡着,需要人来点确认,所以可以完成对server的请求。而用同步的ajax请求则浏览器会等待该请求完成后才关闭窗口清理资源,因而就不会有这个问题。

这就是一种使用同步ajax的场景,ajax中第一个a指的是 asynchronous 异步,当初引入ajax的主要目的就是引入一种异步执行的方法,绝大部分情况下应该使用异步方式执行ajax。同步ajax也许应该叫做 sjax ( s 是 synchronously)。
XMLHttpRequest 的 open 方法中的第三参数表示是同步还是异步,xmlHttp.open("GET", url, true); 各种支持Ajax的框架,如prototype、ext、dwr等肯定都有选项设置是否为异步模式。

最后,实际情况的做法是要结合方法一和方法二。方法二由于各种原因(比如用户电脑突然停电、浏览器崩溃等,这你总没办法吧),总是难免浏览器在关闭时没有成功的通知server。尽管是少数情况,但server端是要保证长期运行稳定的,要避免资源泄漏,所以必然server端还是需要定期检查会话是否过期的。但因为已经保证了大部分情况下server得到了通知,所以server端的定期检查可以时间长一些,心跳间隔也可以时间长一些(但要小于server检查的时间间隔),因而对server也不造成什么负担。

相关参考资料:
Making AJAX calls onUnload: http://www.livelearncode.com/archives/11

没有评论: