反向Ajax,第5部分:事件驱动的Web开发

标签: ajax 事件驱动 web | 发表时间:2011-09-30 10:35 | 作者:Elaine.Ye Taozi
出处:http://www.yeeyan.org

译者 Elaine.Ye

前言   

这一文章系列展示了如何使用反向Ajax(Reverse Ajax)技术开发事件驱动的web应用,第1部分内容介绍了反向Ajax、轮询(polling)、流(streaming)、Comet和长轮询(long polling);第2部分内容说明了如何使用WebSocket来实现反向Ajax,并讨论了使用Comet和WebSocket的web服务器的局限性;第3部分内容说明了如果需要支持多种服务器,或是给用户提供部署在他们自己的服务器上的独立的web应用的话,那么实现自己的Comet或是WebSocket通信系统会有哪些难点,该部分内容还讨论了Socket.IO;第四部分内容谈到了Atmosphere和CometD——最知名的用于Java技术服务器的开源反向Ajax库。

到目前为止你已经了解了创建通过事件来通信的组件,在本系列的最后一部分内容中,我们把事件驱动开发的原则应用到实践中,构建一个示例性的事件驱动web应用。

你可以下载本文中使用的源代码。

前提条件

理想情况下,要充分体会本文的话,你应该对JavaScrpit和Java有一定的了解,并且要有一些web开发经验。若要运行本文中的例子,你还需要最新版本的Maven和JDK(参见参考资料)。

术语


你可能对事件驱动架构(event-driven architecture,EDA)、EventBus系统、消息系统、复杂事件处理(complex event processing,CEP)和信道(channel)这些说辞并不陌生,这些术语和概念已出现多年。随着技术的成熟,你可能会更频繁地听到这类说法。本节内容为这些概念提供一些简短的解释。

事件(event)

在系统中会发生的一些事情的出现,事件通常具有属性,比如说出现的时间(时间戳)、来源或位置(我们点击的组件),以及一些描述事件的数据。根据系统的不同,事件还可以有其他的一些属性选择。

事件驱动架构(Event-driven architecture,EDA)

也称作基于事件的编程,这是一种架构设计,在这种设计中,应用由通过发送和接收事件来通信和执行的组件构成。Swing的图形化用户界面(GUI)就是一个EDA例子,每个Swing组件都可以监听事件、对事件作出反应、发送其他事件等。EDA由几个部分组成:事件生产者、事件消费者、事件和处理软件。

  1. 事件生产者(event producer)——该组件发出事件。在本文的例子中,表单的提交按钮就是一个事件生产者。

  2. 事件消费者(event consumer)——监听特定事件的组件。例如,例子中的表单提交这种情况,浏览器监听表单的提交按钮上的点击操作,把表单数据发送给服务器。

  3. 事件处理软件(event-processing software)——这是系统的核心,事件生产者发布事件,事件消费者注册自身以接收事件。根据软件的不同,处理过程可以很简单(只是把接收到的事件转发给消费者),或很复杂(CEP)。有了CEP,软件就可以支持各种各样的处理方式,比如说事件的汇集、过滤和转换等。

  Esper就是这样的一个软件例子。事件处理软件不仅可以表现成一个独立的运行应用,其还可以是整合到你的应用中的库。

消息系统(messaging system)

一种事件驱动应用类型,在这种应用中,事件生产者把消息发布到信道中,事件消费者则通过信道进行订阅。事件生产者和消费者彼此之间没有链接,是完全独立开来的。在这种类型的事件驱动应用中,通常用到的术语是消息(message)而不是事件(event)

信道(channel)

消息系统中分类事件的一种方式。其代表了事件生产者希望事件发送到的目的地。例如,在一个聊天室应用中,某个信道可能会是 /chatapplication/chatrooms/asdrt678,该信道标识了一个事件生产者可以发送消息的特定的聊天室,图形化的组件应该要订阅该信道,目的是显示最新到达的消息。

某些消息系统提供了两种类型的信道:队列(queue)和主题(topic)。

  1. 队列(queue)——当某条消息被发送到队列中时,只有一个事件消费者拿到并处理该条消息,其他消费者不会看到它。队列可被持久化,以保证交付。最好的队列例子是邮递请求,某个web应用在用户注册时发布一条消息到队列 /myapp/mail/user-registration中,可能有多个邮件应用订阅了这一队列,如果没有的话,消息也不会丢失。

  2. 主题(topic)——当某条消息发送到某个主题上时,每个订阅者都可以接收到它,主题通常是没有持久化的。一个例子是监控软件的一个主题/event/system/cpu/usage,生产者定期往其中发送CPU的使用情况;另一方面,这一主题可能会没有或是有多个订阅者,这取决于他们的兴趣所在。

发布/订阅(publish/subscribe)

事件驱动的解决方案实现了发布/订阅模式。事件生产者在处理软件中发布事件,事件消费者通过订阅来接收它们。事件消费者订阅的方式依赖于软件。在消息应用中,它们订阅信道(比如说,还可以有选择地把过滤规则应用在事件类型上)。使用诸如Esper一类的CEP,可通过类SQL的请求来定义你所感兴趣的事件,完成订阅操作。

为什么使用事件驱动的解决方案


在一个传统的通信方案中,如果系统A需要来自系统B的信息,就会发送一个请求给B。系统B会处理该请求,系统A则会停在那里等待响应。在处理完成时,响应会送回给系统A。在这一同步的通信模式中,资源的消耗是低效的,因为在等待响应时浪费掉了处理时间。

异步模式中,系统A会从系统B中订阅它响应的信息。然后系统A可以选择性地给系统B发送通知,并立刻返回,系统A可以继续处理其他事情,这一步骤是可选的。通常情况下,在事件驱动的应用中,你不需要请求其他系统发送事件,因为你不知道它们是谁。当系统B发布响应时,系统A会立刻接收到。

事件驱动架构的一个优点是其允许更好的伸缩性。可伸缩性是系统在满足其目标的同时适应需求、容量或是强度变化的能力。通过消除暂停时间,事件驱动的架构通常有着更好的表现,以及有更高的处理效率。

另一个优点表现在应用的开发和维护方面。使用事件驱动的解决方案,应用的每个组件都可以是完全独立和解耦的。

事件驱动的解决方案允许有更好的反应时间,因为通信有着更低的延迟。

把事件驱动的解决方案应用在web上


web框架过去依赖于传统的请求-响应模式,这导致了页面的刷新。随着Ajax、反向Ajax以及诸如CometD和Atmosphere一类的功能强大的框架的出现,现在把事件驱动架构的概念应用到web上来获取解耦、可伸缩性和反应性的好处已经不是什么难事了。

客户端

事件驱动架构可应用在GUI开发的客户端。与创建一个传统的web页面不同,你可以把一个单独的web页面当作容器使用。每个组件(页面的每个组成部分)都可以是独立的,你可以在web上放一个Java Swing GUI,就像包含了小工具(gadget)的Google页面那样(参见参考资料中的链接)。

你需要一个事件总线(event bus),例如,你需要开发一个JavaScript事件总线,其允许每个页面组件从信道订阅或是在信道中发布。事件也可以是异步的,在两个或是多个事件到达后才触发行为。事件总线可以用于页面中的局部事件,但你也可以通过使用CometD或是Socket.IO来以插件的方式支持远端事件。

服务器端

在服务器端,你需要设置一个反向Ajax框架来支持事件驱动的框架。在本系列前几部分的考察中,发现只有CometD有着事件驱动的方法。对于其他框架来说,你需要增加自定义的支持,这不是什么大问题。你还可以加入第三方的消息系统,比如说JMS(例如Apache ActiveMQ)或是像Esper那样的CEP。一个更简单的解决方案是Redis,其支持基本的发布/订阅。

这一文章系列谈论的是事件驱动的web和反向Ajax,因此我们重点关注客户端,不会去设置一个复杂的消息系统。

事件驱动web的例子


本文将要创建的例子是一个聊天室web应用,该应用使用一个用户面板来列出连接的用户。你的用户名是加粗显示的,活动用户(20秒钟后还处活跃状态的那些)是绿色显示的,20秒钟后处于非活动状态的那些是橙色显示的。如果有用户连接或是断开连接,列表就会刷新。

出于安全目的,web.xml文件中配置了两分钟的会话超时,非活动状态两分钟后,就会弹出一个窗口,你会被重定向到登录页面。

只要你不再处于会话中或是还未连接,就会被重定向到登录页面。登录页面要求输入用户名并会查看是否可让你登录到聊天室中。

一旦登录成功,你就可以在聊天室中给所有用户发送消息。一个控制台也会显示出来,记录所有收到的事件。

该web应用是基于事件的,有了上述的信息,你可以很容易地定义几个事件:

1. 用户连接

2. 用户断开连接

3. 会话过期

4. 接收到聊天消息

5. 如果没有登录的话,安全过滤器阻拦请求

6. 用户变成非活动的

7. 用户变成活动的

8. 所有其他与UI协调相关的事件

某些事件只局部于web应用,由局部总线来识别,如清单1所示:

清单1. 总线设置

bus = {
 
    local: new EventBus({
        name: 'EventBus Local'
    }),
 
    remote: EventBus.cometd({
        name: 'EventBus Remote',
        logLevel: 'warn',
        url: document.location.href.substring(0,
            document.location.href.length -
            document.location.pathname.length) + '/async',
        onConnect: function() {
            bus.local.topic('/event/bus/remote/connected').publish();
        },
        onDisconnect: function() {
            bus.local.topic('/event/bus/remote/disconnected').publish();
        }
    })
 
};

其他事件是远端的,这意味着它们需要一个反向Ajax系统,比如说CometD来在所有客户端中发布它们。图1展示了该示例应用。

图1. 示例应用


你可以下载这一示例应用,许多类都是安全通道类,或是会话和用户管理通道类。本文给出了代码最重要的部分,不过建议你下载并运行该应用例子来更加深入地了解它的运作方式。

该web应用有不同的组件构成:聊天室、用户列表和控制台。每个都很独立,可以拿掉而不会影响到其他部分。

为了以局部的和远端的方式来设置这一事件驱动的系统,该例子使用了Ovea的EvenBus系统。其提供了一个局部的事件总线,一个活动远端事件的ComeD桥接,以及一种协调事件的方式(在几个事件完成之后触发行为)。当然,你可以选择使用另一个不同的系统。该例子使用了JavaScript来进行设置,如清单1所示。

一旦总线就位了之后,应用和组件就是基于事件的了。在本例子中,设置的是IDLE检测系统,如清单2所示。                

清单2. IDLE检测系统

bus.local.topic('/event/dom/loaded').subscribe(function() {
    $.idleTimer(20000);
    $(document).bind('idle.idleTimer', function() {
        bus.local.topic('/event/idle').publish('inactive');
    });
    $(document).bind('active.idleTimer', function() {
        bus.local.topic('/event/idle').publish('active');
    });
})

有了清单2中的代码,IDLE系统就会在检测到活动时发送事件。这一代码可用在任何需要IDLE系统的应用中。在该例子中,你需要在用户活动的远端事件中转化一下该代码。其也可用JavaScript来实现,如清单3所示。

清单3. 用户活动管理

bus.local.topic('/event/idle').subscribe(function(status) {
    bus.remote.topic('/event/user/status/changed').publish({
        status: status == 'active' ? 'online' : 'away'
    });
});
 
bus.remote.topic('/event/user/status/changed').subscribe(function(evt) {
    if(evt.user != me.name) {
        $('#users li').filter(function() {
            return evt.user == $(this).data('user').name;
        }).removeClass('online')
          .removeClass('away')
          .addClass(evt.status);
    }
});

首个订阅接收来自IDLE系统的事件,然后把用户状况发送给服务器端。其他的订阅接收来自服务器端的用户状况事件。因此,只要用户的状况发生改变,用户列表中的用户的颜色就会变成绿色或是橙色。

当用户连接或是断开连接时,就会发送一个事件,如清单4所示:

清单4. 用户列表管理

bus.remote.topic('/event/user/connected').subscribe(function(user) {
    $('#users ul').append(row(user));
});
 
bus.remote.topic('/event/user/disconnected').subscribe(function(evt) {
    $('#users li').filter(function() {
        return evt.user == $(this).data('user').name;
    }).remove();
});

应用的代码简单、解耦且是独立的,通过重用Ovea的许多技术,你可以快速地创建事件驱动的web应用。不过,因为可以使用其他的系统来代替它,因此这并不是必需的。该例子只花了一天的开发时间,其中一半的代码都是管道代码,包括:

1. Maven:构建工程

2. 安全功能(登录、退出、会话超时)

3. 使用了Jersey的REST服务

结束语


  

该文章系列展示了如何构建响应式的以及是可伸缩的应用,这些应用能够提供很好的用户体验。事件驱动的web应用还是一个相当新的概念,某些WEB框架在内部用到了这些概念。不过本文展示的是不需要web框架来构建的这样的应用,对于要分离Java开发者和web设计者之间的职责来说,使用好的、专业的库就已是绰绰有余的了。关于如何把事件驱动的开发变成你的日常工作的一部分,我希望你已经有了一个较为深入的理解。它很容易让人沉迷于其中,一旦经过尝试,你就不想再回退到传统的框架上了。

下载


描述        名称           大小   下载方法

文章的源代码    reverse_ajaxpt5_source.zip   925KB          HTTP

关于下载方法的说明

参考资料


学习资料

1. 阅读这一系列之前的部分:

  1.1. 第1部分:Comet介绍

  1.2. 第2部分:Websocket

  1.3. web服务器和Socket.IO

  1.4. Atmosphere和CometD

2. iGoogle:web上的一个Java Swing GUI例子。

3. CometD:了解关于这一事件驱动的反向Ajax解决方案的各方面的内容。

4. 在维基百科上了解这些内容:

  4.1. AJAX

  4.2. 反向Ajax

  4.3. 事件驱动的架构编程

5. Event Processing in Action,由Dr. Opher Etzion和Peter Nibblet合著,展示如何使用、设计和构建事件处理应用。

6. “Google AppEngine uses Jetty!”介绍了Google的Jetty定制。

7. 了解更多关于Jetty Continuation的功能。

8. “Exploring Reverse AJAX”:提供了一些关于反向Ajax技术的介绍说明。

9. “Cross-domain communications with JSONP, Part 1: Combine JSONP and   jQuery to quickly build powerful mashups”(developerWorks,  February 2009):了解如何把不起眼的跨域调用技术(JSONP)和一个灵活的JavaScript库(JQuery)结合在一起,以令人惊讶的速度构建出一些功能强大的聚合应用。

10. “Build Ajax applications with Ext JS”(developerWorks, July 2008):对Ext Js背后的面向对象JavaScript 设计概念做了一个概述,并说明了如何把Ext JS框架用作富互联网应用的UI元素。

11. “Compare JavaScript frameworks”(developerWorks, February 2010):对极大地增强了JavaScript开发的那些框架有一个整体的了解。

12. “Mastering Ajax, Part 2: Make asynchronous requests with JavaScript and Ajax”(developerWorks, January 2006):学习如何使用Ajax和XMLHttpRequest对象来创建一种永不会让用户等待服务器响应的请求/响应模型。

13. “Create Ajax applications for the mobile Web”(developerWorks,  March 2010):了解如何使用Ajax构建跨浏览器的智能手机Web应用。

14. “Where and when to use Ajax in your applications”(developerWorks, February 2008):了解如何使用Ajax来改进网站,同时避免糟糕的用户体验。

15. “Improve the performance of Web 2.0 applications“(developerWorks, December 2009):探讨不同的浏览器端缓存机制。

16. “Introducing JSON”(JSON.org):获得对JSON语法的一个入门介绍。

17. developerWorks Web development zone:获得各种谈论基于Web的解决方案的文章。

18. developerWorks podcasts:收听各种与软件开发者进行的有趣的访谈和讨论。

19. developerWorks technical events and webcasts:随时关注developerWorks的技术事件和webcast的进展。

获取产品和技术

1. 从Ovea的GitHub上获取Ovea捐献给CometD项目的代码。

2. JavaScript Event-Bus(从Ovea的GitHub上获得):获得以JavaScript实现的局部方式的和远端方式的事件驱动解决方案。

3. JavaScript event synchronization(从Ovea的GitHub上获得):同步异步事件。

4. 获取Esper:复杂事件处理软件。

5. 从Apache获得ActiveMQ消息系统

6. Redis:获取有着异步功能的内存缓存系统。

7. 事件驱动的web例子:获得本文中的例子源代码。

8. CometD源代码:获取该项目,这是一个web消息的可伸缩comet(服务器推)实现。

9. CometD扩展:提供了Jetty 8 WebSocket支持和来自Ovea的Google Guice支持。

10. Jetty:获取Jetty,一个web服务器和javax.servlet容器,外带对WebSocket的支持。

11. Apache Tomcat Advanced I/O文档:获得有用的链接、用户指南、参考手册和Apache Tomcat开发说明。

12. Grizzly NIO Framework:帮助你使用Java NIO API。

13. Grizzly Comet例子:在从头开始构建一个新的应用之前,了解需要对现有应用做哪些修改。

14. Glassfish Application Server:获得主要的Glassfish Server的开源版本。

15. Socket.IO Java:获得原始的项目和最后更新的Ovea的派生版本。

16. Apache Maven:获取Maven,一个软件项目管理和包容工具。

17. Java Development Kit, Version 6:获得Java平台标准版(Java Platform, Standard Edition,Java SE),该平台允许你在台式机和服务器上,以及在当今要求苛刻的嵌入式环境上开发和部署Java应用。

18. 免费试用IBM软件,下载使用版,登录在线试用,在沙箱环境中使用产品,或是通过云来访问,有超过100种IBM产品试用版选择。

讨论

1. 参加论坛的讨论

2. 现在就创建你的developerWorks个人资料,并设置一个关于Reverse Ajax的观看列表。与developerWorks社区建立联系并保持联系。

3. 找到其他在web开发方面感兴趣的developerWorks成员

4. 分享你的知识:加入一个关注web专题的developerWorks组

5. Roland Barcia在他的博客中谈论Web 2.0和中间件

6. 关注developerWork成员的shared bookmarks on web topics

7. 快速获得答案:访问Web 2.0 Apps论坛

8. 快速获得答案:访问Ajax论坛

关于作者


Mathieu Carbou是Ovea的一位提供服务和开发解决方案的Java web架构师和顾问。他是几个开源项目的提交者和领导者,也是Montreal的Java User Group的一位演讲者和领导者。Mathieu有着很强的代码设计和最佳实践背景,他是一个从客户端到后端的事件驱动的web开发方面的专家。他的工作重点是在高度可伸缩的web应用中提供事件驱动的和消息式的解决方案。你可以看一看他的博客

  (译者注:标题配图来自http://www.ibm.com/developerworks/网站

相关 [ajax 事件驱动 web] 推荐:

反向Ajax,第5部分:事件驱动的Web开发

- Taozi - 译言-电脑/网络/数码科技
到目前为止你已经了解了创建通过事件来通信的组件,在本系列的最后一部分内容中,我们把事件驱动开发的原则应用到实践中,构建一个示例性的事件驱动web应用. 你可以下载本文中使用的源代码. 理想情况下,要充分体会本文的话,你应该对JavaScrpit和Java有一定的了解,并且要有一些web开发经验. 若要运行本文中的例子,你还需要最新版本的Maven和JDK(参见参考资料).

用GWT实现基于Ajax的Web开发

- - 博客 - 伯乐在线
来源: IBM Developerworks. 简介: Google Web Toolkit (GWT) 是一个可以使用 Java 代码开发具有 Ajax 功能的应用框架. 使用 GWT,开发人员可以更加高效地开发和部署主流 Web 应用程序. GWT Designer 是 Google 新推出的可视化开发插件,大大简化了界面开发工作.

Dorado 7.2.5 发布,Ajax的Web开发平台

- - 开源中国社区最新新闻
Dorado 7.2.5 发布,Dorado7的全称是Dorado展现中间件,是一款开源软件,致力于为Web应用提供更加丰富、友好的用户界面,同时大幅提高展现层的开发效率. Dorado7由三个部分组成:浏览器端的控件(Javascript+CSS)、服务器端的驱动引擎(Java)和集成开发环境(Eclipse Plugin).

[转]反向Ajax,第3部分:Web服务器和Socket.IO

- - 高味儿在这写写有点漏味的技术文章
这一文章系列探讨了如何使用反向Ajax(Reverse Ajax)技术来开发事件驱动的web应用. 第1部分介绍了实现反向Ajax通信的不同方法:轮询(polling)、捎带(piggyback)以及使用了长轮询(long polling)和流(streaming)的Comet;第2部分说明了如何使用WebSocket来实现反向Ajax,并讨论了使用Comet和WebSocket的web服务器的局限性.

基于 Ajax 的 web 应用程序中各种客户端-服务器通信机制

- Anew - IBM developerWorks 中国 : 文档库
任何基于 Asynchronous JavaScript and XML (Ajax) 的 web. 应用程序都有一个关键组成部分,就是客户端与服务器之间的通信层. 要实现该层,需要了解浏览器提供的各种机制,以及各机制的优缺利弊. 在本文中,将学习在特定的应用程序通信需求与合适的机制之间做出搭配. 其中的详细案例将会演示如何创建一个满足不同客户端-服务器通信需求的通信层.

原生AJAX

- - Web前端 - ITeye博客
对象是ajax的基础,几乎所有的浏览器都支持他,只是创建方式不同,如IE5,IE6. 2、AJAX - 向服务器发送请求请求. 与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用. 然而,在以下情况中,请使用 POST 请求:. 无法使用缓存文件(更新服务器上的文件或数据库). 向服务器发送大量数据(POST 没有数据量限制).

初识Ajax

- - CSDN博客推荐文章
Ajax(Asynchronous JavaScript and XMLS异步JavaScript和XML)(“阿贾克斯”)技术. 完成页面的局部刷新,从而提升操作性能. AJAX 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的 Web 应用程序的技术. 依赖的核心对象:XMLHttpRequest.

Redis事件驱动库结构

- zffl - NoSQLFan
本文翻译自Redis官方对事件驱动库的结构描述,英文原文点这里,由Day Day Up博客原创,文章写的时间已经比较长了,今天才被NoSQLFan挖出来,实属难得. 文章地址:blog.ddup.us. 这是一篇翻译文章,原文见这里. Redis实现了它自己的事件库. 要弄明白Redis事件库是如何工作的最好的方法就是弄明白Redis是如何使用它的.

再谈EDA事件驱动架构

- - 人月神话的BLOG
EDA事件驱动架构首先不是对于传统的面向业务流程,数据等各种架构模式的完全否定,而是解决传统架构下无法很好解决的一些问题. 传统模式里面更加关注业务流程和业务对象,而EDA模式下将更加关注在整个业务流程中的关键状态点,已经由关键状态点触发的有明确业务含义的业务事件. EDA架构的核心仍然是基于消息的发布订阅模式,消息的特定就是准实时,异步和彻底解耦.

jquery ajax 跨域请求

- - 博客园_首页
使用 jquery 中的ajax  进行跨域请求. 说明:dataType 为  "jsonp"  ;type 只能为 GET.                    //处理错误. 后台处理代码 ValidAccountsExists.aspx.