服务器向客户端发消息,Comet--pushlet

介绍

        server端向浏览器client发送通知这种通讯模式在J2EE应用中很常见,通常使用采用 RMICORBA或 者自定义TCP/IP信息的applet来实现。这些技术往往由于复杂而产生诸多不利之处:技术难以实现、存在防火墙限制(因为需要打开非HTTP的通讯 端口)、需要额外的server开发和维护。并且除了刷新整个页面或者完全采用applet展示内容之外,很难找到别的方法将client端applet 的状态和浏览器的页面内容集成在一起。

         Pushlet是一种 comet实 现:在Servlet机制下,数据从server端的Java对象直接推送(push)到(动态)HTML页面,而无需任何Java applet或者插件的帮助。它使server端可以周期性地更新client的web页面,这与传统的request/response方式相悖。浏览 器client为兼容JavaScript1.4版本以上的浏览器(如Internet Explorer、FireFox),并使用JavaScript/Dynamic HTML特性。而低层实现使用一个servlet通过Http连接到JavaScript所在的浏览器,并将数据推送到后者。有关JavaScript版 本的知识请参看Mozilla开发中心提供的 《JavaScript核心参考》Stephen Chapman编写的 《What Version of Javascript》

         这种机制是轻量级的,它使用server端的servlet连接管理、线程工具、javax.servlet API,并通过标准Java特性中Object的wait()和notify()实现的生产者/消费者机制。原则上,Pushlet框架能够运行在任何支 持servlet的server上、防火墙的后面。当在client中使用JavaScript/DHTML时,Pushlet提供了通过脚本快速建立应 用、使用HTML/CSS特性集成和布局新内容的便利方法。

动机

        目前越来越多的servlet和JSP用来部署web,于是便出现了在页面已经装载完毕后由于server端某些对象的状态变化而产生对client浏览器进行通知和同步的需要。

        这些状态变化的原因很复杂:可能由于用户通过访问servlet或者修改数据库记录、更新EJB造成,或是在多用户应用(比如聊天室和共享白板)中的事件导致数据状态变化。这些类型的应用常常使用一种分布式的 MVC模板:模式层位于server上(可能缓存在client中),控制层和视图层位于client中(这两个层可能合为一体)。

         当然,这里也存在需要订阅server端动态内容的应用:那些动态内容不停地从server端推送过来。例如股票实时情报、系统状态报告、天气情况或者其 它的监测应用。它遵循观察者(Observer)模板(也称为发布/订阅模板),这种模板中的远程client注册成为关注于server端对象变化的观 察者。关于设计模板的知识请看 Matrix Wiki上的介绍

        那么在HTML页面已经被装载后如何通知浏览器客户端?或者如果有选择地更新页面中一些部分的话,那该怎么做?比如只更新在HTML Table中的那些价格发生变化的股票列?

多种通知解决方案

         让我们对应用进行这样的假设:拥有一个Java web server或者Java应用server,我们试图从server发送通知给client端浏览器。这里的解决方案可以分为:“轮询 (polling)”、“服务端回调(server-side callbacks)”和“消息(messaging)”三类。

轮询

         最简单的解决方案便是“定时刷新页面”。在HTML文档的头部使用HTML META标签,页面便可以每隔N秒自动reload一次。如果在此期间server端数据发生变化,那么我们可以获得新的内容,否则将得到相同的页面。虽 然方法很简单,但是如何设置刷新间隔是让人头疼的大问题。

服务端回调

        因为我们是“身经百战”的Java开发老手,所以经常会用到“服务端回调”。这种方式通过RMI或者CORBA将Server端的对象传输到Java applet客户端。

消息(MOM)

        这种解决方案采用一个作为client的applet,它使用TCP/IP或者无连接的UDP、甚至多播协议来建立与消息中间键server的通讯,然后由server推送消息给client。你可以从例如 SoftWired的iBusIBM的MQSeriesBEA的WebLogic Event这些消息产品中直接挑选,或者自己使用基于socket的java.io.ObjectStream定制开发消息软件。

讨论(MOM)

        上面三种解决方案在复杂性、安全性、性能、可测量性、浏览器兼容性、防火墙限制上各有优势、劣势。最佳解决方案依赖于你的应用需求。例如,在共享白板应用中,用户需要直接与“状态”交互,那么server端回调或者消息很可能会大显身手。

         但在浏览器环境下,除非完全使用applet作为整个client应用,否则把来自于server的更新信息集成到页面中并非易事。如何在applet收 到回调或者消息时变更页面相关内容?一个很“痛快”而又“痛苦”的解决方案就是在回调方法中使用AppletContext.showDocument (URL)方法来刷新整个页面。

        由于HTML代码可以直接影响页面布局,直接使用来自server的数据更改HTML部 分内容不是更好吗?这是web应用的理想方案,在server上内容动态改变时,从用户到server所需的交互是最小化的。作为对上面的解决方案的补 充,我开发了Pushlet这种轻量级、瘦客户端的技术,它无需applet或者插件而直接与脚本/HTML集成在一起、使用标准HTTP连接、理论上可 以部署到任何支持Java servlet的server上。但这并不意味着它将替换对前面解决方案,而是在你的开发“工具箱”中添加另一种选择。作为Java构架者/开发者,你可 以自行权衡、选择、决定哪种适合应用的解决方案。

Pushlet原理

        Pushlet的基本使用形式是极为简单的。后面的一些示例会说明这一点。

HTTP流
Pushlet 基于HTTP流,这种技术常常用在多媒体视频、通讯应用中,比如QuickTime。与装载HTTP页面之后马上关闭HTTP连接的做法相反, Pushlet采用HTTP流方式将新数据源源不断地推送到client,再此期间HTTP连接一直保持打开。有关如何在Java中实现这种Keep- alive的长连接请参看Sun提供的 《HTTP Persistent Connection》W3C的《HTTP1.1规范》。

示例1

        我们利用HTTP流开发一个JSP页面(因为它易于部署,而且它在web server中也是作为servlet对待的),此页面在一个定时器循环中不断地发送新的HTML内容给client:
 
 
java 代码
 
  1. <%   
  2.   int i = 1;  
  3.       
  4.   try {  
  5.     while (true) {  
  6.        out.print("

    "+(i++)+"

    "
    );  
  7.        out.flush();  
  8.         
  9.        try {  
  10.             Thread.sleep(3000);  
  11.        } catch (InterruptedException e) {  
  12.        out.print("

    "+e+"

    "
    );  
  13.         }  
  14.      }  
  15.    } catch (Exception e) {  
  16.        out.print("

    "+e+"

    "
    );  
  17.    }  
  18. %>  

        在Pushlet源代码中提供了此页面(examples/basics/push-html-stream.jsp)。上面的页面并不是十分有用,因为在我们刷新页面时,新内容机械地、持续不断地被添加到页面中,而不是server端更新的内容。

示例2

        现在让我们步入Pushlet工作机理中一探究竟。通过运行Pushlet的示例源代码(examples/basics/ push-js-stream.html),我们会看到这个每3秒刷新一次的页面。那么它是如何实现的呢?

        此示例中包含了三个文件:push-js-stream.html、push-js-stream-pusher.jsp、push-js-stream-display.html。

        其中push-js-stream.html是主框架文件,它以HTML Frame的形式包含其它两个页面。

        push-js-stream-pusher.jsp是一个JSP,它执行在server端,此文件内容如下: 
 
 
java 代码
 
  1.  7: <%   
  2.  8:   /** Start a line of JavaScript with a function call to parent frame. */  
  3.  9:   String jsFunPre = "<script LANGUAGE=JAVASCRIPT >parent.push('";  
  4. 10:     
  5. 11:   /** End the line of JavaScript */  
  6. 12:   String jsFunPost = "') ";  
  7. 13:     
  8. 14:   int i = 1;  
  9. 15:   try {  
  10. 16:     
  11. 17:     // Every three seconds a line of JavaScript is pushed to the client  
  12. 18:     while (true) {  
  13. 19:       
  14. 20:        // Push a line of JavaScript to the client   
  15. 21:        out.print(jsFunPre+"Page "+(i++)+jsFunPost);  
  16. 22:        out.flush();  
  17. 23:          
  18. 24:        // Sleep three secs  
  19. 25:        try {  
  20. 26:             Thread.sleep(3000);  
  21. 27:        } catch (InterruptedException e) {  
  22. 28:             // Let client display exception   
  23. 29:             out.print(jsFunPre+"InterruptedException: "+e+jsFunPost);  
  24. 30:        }  
  25. 31:      }  
  26. 32:    } catch (Exception e) {  
  27. 33:             // Let client display exception   
  28. 34:             out.print(jsFunPre+"Exception: "+e+jsFunPost);  
  29. 35:    }  
  30. 36: %>   


         注 意在示例1和示例2中使用JSP时都存在一个问题:一些servlet引擎在某个client离开时会“吃掉”IOException,以至于JSP页面 将永不抛出此异常。所以在这种情况下,页面循环将会永远执行下去。而这正是Pushlet实现采用servlet的原因之一:可以捕获到 IOException。

        在上面代码的第21行中可以看到在一个定时器循环(3秒/周期)中打印了一些HTML并将它们输出到client浏览器。请注意,这里推送的并非HTML而是Javascript!这样做的意义何在?

         它把类似“
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值