2006-10-23
Comet,下一代Ajax?
关键字: ajax, comet
最近在看comet(server push)技术,经过一番google之后,大致理清了头绪,目前已经研究完一个开源的comet实现:pushlet([url]http://www.pushlets.com),包括前台的js,html代码以及后台的java代码,也基本搞清楚了关于pushlet的处理机制并且胡乱写了一部分pushlet的学习笔记,目前还在整理中,到时候将与大家分享!
接下来的打算看另外两个开源的comet实现:dwr 2.0的reverse ajax和dojo的io.bind(),
如果有志同道合者大家可以一起来研究共同提高!
概念
关于comet的最初定义来自这篇blog文章:http://alex.dojotoolkit.org/?p=545。
简单的说就是客户端发送一个请求,服务器接收它,并使用一个无限循环,将客户端需要的数据push到response中,并进行刷新,但是该response并不关闭,继续接收新的数据并刷新,直到客户端断开连接,该循环才结束退出。
我们可以认为ajax解决了单用户响应的问题,而comet则解决了在保证性能的前提下进行协同多用户的响应问题。
comet的优点在于它可以在任何时候向客户端发送数据,而不仅仅只是响应用户的输入请求。而发送数据是在一个已有的单连接上进行的。因此可以大大降低发送数据的延迟时间(建立connection的开销,以及客户端发送请求的等待时间)。
关于Event Web Server
comet技术的一个重要组成部分就是event-drived web server,目前商用的实现已经出现,如lightstreamer(http://www.lightstreamer.com),这个我没有仔细看,只是跑了一下他给出的demo,还行!开源的实现就是apache+jetty(还要加一个mod)这个我还没有具体用过!
有国人声称实现了comet server(http://cnc.agile.com/read.php?tid=319),还没去仔细研究,不知道如何。
pushlet目前使用的是client pull做法,当然它的server push也将在不久的将来实现,它没有声称只能在专用的event-drived web server上运行,目前在tomcat运行环境下是可以的,不知道最多能承受多少用户同时访问还有待考证!
comet新体验
我看了一下comet技术的一些实现,其中meebo(http://wwwm.meebo.com/)算是做的比较好的一个(gmail有谁用过?说说感受),我觉得它的web版本msn messager,比微软自己的web msn就要好用的多。
接下来的打算看另外两个开源的comet实现:dwr 2.0的reverse ajax和dojo的io.bind(),
如果有志同道合者大家可以一起来研究共同提高!
概念
关于comet的最初定义来自这篇blog文章:http://alex.dojotoolkit.org/?p=545。
简单的说就是客户端发送一个请求,服务器接收它,并使用一个无限循环,将客户端需要的数据push到response中,并进行刷新,但是该response并不关闭,继续接收新的数据并刷新,直到客户端断开连接,该循环才结束退出。
我们可以认为ajax解决了单用户响应的问题,而comet则解决了在保证性能的前提下进行协同多用户的响应问题。
comet的优点在于它可以在任何时候向客户端发送数据,而不仅仅只是响应用户的输入请求。而发送数据是在一个已有的单连接上进行的。因此可以大大降低发送数据的延迟时间(建立connection的开销,以及客户端发送请求的等待时间)。
关于Event Web Server
comet技术的一个重要组成部分就是event-drived web server,目前商用的实现已经出现,如lightstreamer(http://www.lightstreamer.com),这个我没有仔细看,只是跑了一下他给出的demo,还行!开源的实现就是apache+jetty(还要加一个mod)这个我还没有具体用过!
有国人声称实现了comet server(http://cnc.agile.com/read.php?tid=319),还没去仔细研究,不知道如何。
pushlet目前使用的是client pull做法,当然它的server push也将在不久的将来实现,它没有声称只能在专用的event-drived web server上运行,目前在tomcat运行环境下是可以的,不知道最多能承受多少用户同时访问还有待考证!
comet新体验
我看了一下comet技术的一些实现,其中meebo(http://wwwm.meebo.com/)算是做的比较好的一个(gmail有谁用过?说说感受),我觉得它的web版本msn messager,比微软自己的web msn就要好用的多。
- 23:57
- 浏览 (26718)
- 论坛浏览 (30850)
- 评论 (38)
- 分类: Ajax&客户端技术
- 相关推荐
评论
结合Continuations使用Comet,效率能够提高10倍。详见
http://www.ibm.com/developerworks/cn/java/j-jettydwr/index.html
http://www.ibm.com/developerworks/cn/java/j-jettydwr/index.html
zyihang789
2007-07-09
回复
我现在的项目是一个实时监控系统,需要将server端的数据push到client端,push的条件是页面元素的数据发生变化。现在正在研究jetty6中的实现。第一次接触comet方式的东西。大家能不能给些建议阿?
hexiaodong
2007-01-22
回复
通过一个iframe连接到服务器上,服务器在需要的时机发送js代码,iframe收到代码的时候,会即时执行。
引用
pushlet 作弊的说,实际上它是pull,用js包装了下客户端,让界面程序开发者觉得是push过来的而已。我开发的那个是真正推送消息到浏览器的,不会有可感知的延迟。
不同意这种说法,pushlet只是针对ajax浏览器client使用pull,这也是无奈之举,主要是由于client没有接收服务的概念,只能别动接收某个request相关的response,而无法持续地listen数据。
我想知道你的comet是如何在client上实现真正的push?
hexiaodong
2007-01-07
回复
普通的符合servlet规范的web容器,并不支持真正的comet服务。因此,我怀疑dwr2.0的reverse ajax只能通过client poll来模拟comet 连接。
基于 NIO 的web server 能够有效的解决线程分配的问题.
但由于是长连接, 客户端数量越来越多时候,仍然难以解决负载问题.
http://www.javaeye.com/topic/39063
但由于是长连接, 客户端数量越来越多时候,仍然难以解决负载问题.
http://www.javaeye.com/topic/39063
hexiaodong
2006-12-27
回复
常常有Javaeyer通过短信问我要CometServer的源代码,特发一个附件在此
mineral 写道
另外,http协议本身是无状态的,只是容器实现加上了httpSession的具体不同实现,而CometServer(不单指楼主的CometServer)要实时地保持着客户端连接的socket,保存request的状态信息,用户标识信息等,在性能上和实现难度上,是不是比http 的server实现,难度还要更高一点。欢迎大家热烈讨论
在看jetty的相关文档的时候,我发现了continuation(延续)这个东东,而continuation是java语言本身不支持的,jetty做到了这一点,目前我还没搞懂它与长连接之间是什么关系,在dwr 2.0的DwrSystem.java中看到了这样的代码:
static
{
try
{
continuationClass = LocalUtil.classForName("org.mortbay.util.ajax.Continuation"); //$NON-NLS-1$
suspendMethod = continuationClass.getMethod("suspend", new Class[] { Long.TYPE }); //$NON-NLS-1$
resumeMethod = continuationClass.getMethod("resume", new Class[] {}); //$NON-NLS-1$
getObject = continuationClass.getMethod("getObject", new Class[] {}); //$NON-NLS-1$
setObject = continuationClass.getMethod("setObject", new Class[] { Object.class }); //$NON-NLS-1$
}
catch (Exception ex)
{
log.debug("No Jetty ContuniationSupport class, using standard Servlet API"); //$NON-NLS-1$
}
}
... ...
public void pollPreStreamWait()
{
WebContext context = WebContextFactory.get();
Container container = context.getContainer();
final ScriptSession scriptSession = context.getScriptSession();
ServerLoadMonitor monitor = (ServerLoadMonitor) container.getBean(ServerLoadMonitor.class.getName());
long sleepTime = monitor.timeWithinPollPreStream();
synchronized (scriptSession.getScriptLock())
{
// Don't wait if there are queued scripts
if (scriptSession.getQueuedScripts() > 0)
{
return;
}
// If this is Jetty then we can use Continuations
HttpServletRequest request = context.getHttpServletRequest();
boolean useSleep = true;
Object continuation = request.getAttribute(ATTRIBUTE_JETTY_CONTINUATION);
if (continuation != null)
{
useSleep = false;
try
{
// create a listener
ScriptConduit listener = (ScriptConduit) getObject.invoke(continuation, new Object[0]);
if (listener == null)
{
listener = new SystemScriptConduit(continuation);
setObject.invoke(continuation, new Object[] { listener });
}
scriptSession.addScriptConduit(listener);
// continuation.suspend(sleepTime);
// NB. May throw a Runtime exception that must propogate to the container!
suspendMethod.invoke(continuation, new Object[] { new Long(sleepTime) });
}
catch (InvocationTargetException ex)
{
ContinuationUtil.rethrowIfContinuation(ex.getTargetException());
log.warn("Error in Reflection", ex.getTargetException()); //$NON-NLS-1$
useSleep = true;
}
catch (Exception ex)
{
log.warn("Exception", ex); //$NON-NLS-1$
useSleep = true;
}
}
if (useSleep)
{
// create a listener // TODO avoid the expense of creation and registration
ScriptConduit listener = new ScriptConduit()
{
public boolean addScript(String script)
{
try
{
synchronized (scriptSession.getScriptLock())
{
scriptSession.getScriptLock().notifyAll();
}
}
catch (Exception ex)
{
log.warn("Failed to notify all ScriptSession users", ex); //$NON-NLS-1$
}
// just listening!
return false;
}
public void flush()
{
}
};
scriptSession.addScriptConduit(listener);
// The comet part of a poll request
try
{
scriptSession.getScriptLock().wait(sleepTime);
}
catch (InterruptedException ex)
{
log.warn("Interupted", ex); //$NON-NLS-1$
}
finally
{
scriptSession.removeScriptConduit(listener);
}
}
}
}
不过目前我还没有搞清楚这些代码是起什么作用的。
ppeter 写道
我没有看过comet是什么东西,按照各位的讨论看来,这种技术可以运用到基于web的聊天程序实现.
最近几个月一直在做基于web的聊天程序,主要是运用了xmpp协议,关于这个协议的详细信息,大家不妨可以google一下.运用这个协议,有很多开源的组件包,比如smack.聊天的客户端和服务器端都有开源项目,我用过webchat,wildfire,等.推荐大家有需要的话可以去看看.
最近几个月一直在做基于web的聊天程序,主要是运用了xmpp协议,关于这个协议的详细信息,大家不妨可以google一下.运用这个协议,有很多开源的组件包,比如smack.聊天的客户端和服务器端都有开源项目,我用过webchat,wildfire,等.推荐大家有需要的话可以去看看.
基于其他协议(非http协议)不在研究范围之内。
jerry.li 写道
pushlet 性能并不是很好,不知道dwr2.0的push做的怎么样。如果性能得不到提升,那末comet技术还是不用的好。
pushlet性能好不好咱没测试不好下结论,不过他的实现还是比较容易理解的,dwr 2.0所谓reverse ajax部分的源代码刚看完,在reverseAjax中使用的是poll方式来模拟push操作的,也就是在客户端使用setTimeout,不断的发送请求获取新的内容。至于性能好不好我不说大家也应该知道了
下面是dwr的java-chat demo中使用reverse ajax的几个关键代码点:
首先是在onload中进行设置
引用
<body onload="DWREngine.setReverseAjax(true);">
然后是engine.js中的代码
引用
DWREngine.setReverseAjax = function(reverseAjax) {
DWREngine._reverseAjax = reverseAjax;
if (DWREngine._reverseAjax) {
DWREngine._triggerNextPoll(0);
}
};
DWREngine._triggerNextPoll = function(pause) {
setTimeout("DWREngine._poll()", pause);
}
_poll函数中就是发送请求了。
hexiaodong
2006-10-25
回复
另外有一个线程,仅仅把新接受到的request加入到任务队列中,这个线程对请求不做分和分析处理,因此它能够高效及时地把请求插入队列中,而不至请求的丢失。
hexiaodong 写道
唉,再说详细点,希望这次你能看懂。普通web服务器不能保持长连接,因为如果你想保持长连接,只有一个办法:那就要在servlet的service()方法内执行类似下面的代码:
service()方法里是有死循环的,这样的话,接受了一个长连接请求之后,这个线程就不能去处理其他的请求了。
但在我的CometServer中不是这样的,request中有run(),这个方法内不必有死循环来发送消息。而是线程池中的多个线程循环访问request对列中的request对象,并调用run()方法,run()执行完后立即从对列中取下一个request并执行run()。在request的run方法内,会根据url去执行一个action的execute方法。因此,你可以写一个不包含while(true){}的action来处理当前的request对象。这样,CometServer何须为每个request分配一个固定的线程?CometServer只是在执行run()的片刻为它分配了线程,但不象普通web 容器那样线程执行完的同时也把request销毁了。CometServer会把request重新插入对列,并在不久的将来再次执行它run()方法,以便把新的需要写入浏览器的消息发送出去。
while(true){
if(hasMsg()){
sendMsgToBrowser();
}
}
service()方法里是有死循环的,这样的话,接受了一个长连接请求之后,这个线程就不能去处理其他的请求了。
但在我的CometServer中不是这样的,request中有run(),这个方法内不必有死循环来发送消息。而是线程池中的多个线程循环访问request对列中的request对象,并调用run()方法,run()执行完后立即从对列中取下一个request并执行run()。在request的run方法内,会根据url去执行一个action的execute方法。因此,你可以写一个不包含while(true){}的action来处理当前的request对象。这样,CometServer何须为每个request分配一个固定的线程?CometServer只是在执行run()的片刻为它分配了线程,但不象普通web 容器那样线程执行完的同时也把request销毁了。CometServer会把request重新插入对列,并在不久的将来再次执行它run()方法,以便把新的需要写入浏览器的消息发送出去。
你这里写的我明白你的意思。可能是我上一贴没有说清楚我的问题。我换个方式说:
hexiaodong 写道
执行run()的片刻为它分配了线程
虽然我相信你的run里执行只需要比较短的一个时间片断,如果使用小型机以上高配置的服务器,甚至速度更快;我问的问题是:存在线程池里所有的线程都是busy的状态,这个时候进来新的Request的时候,是不是就停止响应,这个问题。
准备用压力工具去验证一下我的想法。
以前做一个消息平台的时候也看到过这样的技术,所谓的pullet其实就是一个长线程(servlet或者jsp)一直在遍历等待,有内容就写出去。这样子对于服务器的线程是个考验,尤其是对于大并发的情况下,几乎服务器没有能力去处理其他的请求了。
但是对于客户端刷新获取消息的方式对与服务器的压力也不小,就算每分钟请求一下(心跳机制),如果有几千人同时在线,心跳一致了也是很夸张的。尤其是心跳形成共振,瞬间并发量增加对于服务器也是相当可怕的事情。
有几种解决办法:
1.flash+sockets+servlet
2.applet+sockets+servlet+守护进程
3.前台刷新+后台cache
我们当时采用的是2,applet+sockets+servlet进行通行,工作良好,使用后台推的方式。
对于第三种方式可能是目前的一种中和解决办法,后台的守护进程将需要的信息读取出来放在一个cache container里面,前台来访问的时候只需要从容器中读取对应的信息,这样子不需要重新到数据库中或者其他的地方去进行复杂的业务逻辑处理,减小心跳的处理时间,降低心跳共振的机率。同时可以通过一些算法,将心跳的时间平均摊开,不至于同时对服务器发起请求。
但是对于客户端刷新获取消息的方式对与服务器的压力也不小,就算每分钟请求一下(心跳机制),如果有几千人同时在线,心跳一致了也是很夸张的。尤其是心跳形成共振,瞬间并发量增加对于服务器也是相当可怕的事情。
有几种解决办法:
1.flash+sockets+servlet
2.applet+sockets+servlet+守护进程
3.前台刷新+后台cache
我们当时采用的是2,applet+sockets+servlet进行通行,工作良好,使用后台推的方式。
对于第三种方式可能是目前的一种中和解决办法,后台的守护进程将需要的信息读取出来放在一个cache container里面,前台来访问的时候只需要从容器中读取对应的信息,这样子不需要重新到数据库中或者其他的地方去进行复杂的业务逻辑处理,减小心跳的处理时间,降低心跳共振的机率。同时可以通过一些算法,将心跳的时间平均摊开,不至于同时对服务器发起请求。
发表评论
该博客是同时发布到论坛的,无法评论在论坛已被锁定的帖子
我的相册
P1100915
共 146 张
共 146 张
最近加入圈子
链接
最新评论
-
电话面试收集
不错,博主应该已经面试过很多人了吧!
-- by sunrise353 -
28岁转行做IT(zt)
25岁以前我还在见证钢铁是怎样炼成的,25岁的时候我想知道网站是怎么回事,于是我 ...
-- by wangyifeng -
28岁转行做IT(zt)
26岁 办证刻章
-- by hellowei -
28岁转行做IT(zt)
我26岁半,以前做通信,见到此文,很高兴.另外,我在ext论坛上好像看到一个 e ...
-- by llp20_2000 -
jms学习笔记
你好,我正在研究JMS,其中有很多问题不明白,希望你能够教我一下,谢谢了,我的q ...
-- by 6308907







评论排行榜