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

英文原文:Reverse Ajax, Part 5: Event-driven web development

  前言

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

[javascript] view plaincopy

  1. bus = {
  2. local: new EventBus({
  3. name: ‘EventBus Local‘
  4. }),
  5. remote: EventBus.cometd({
  6. name: ‘EventBus Remote‘,
  7. logLevel: ‘warn‘,
  8. url: document.location.href.substring(0,
  9. document.location.href.length -
  10. document.location.pathname.length) + ‘/async‘,
  11. onConnect: function() {
  12. bus.local.topic(‘/event/bus/remote/connected‘).publish();
  13. },
  14. onDisconnect: function() {
  15. bus.local.topic(‘/event/bus/remote/disconnected‘).publish();
  16. }
  17. })
  18. };

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

  图1. 示例应用

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

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

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

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

  清单2. IDLE检测系统

[javascript] view plaincopy

  1. bus.local.topic(‘/event/dom/loaded‘).subscribe(function() {
  2. $.idleTimer(20000);
  3. $(document).bind(‘idle.idleTimer‘, function() {
  4. bus.local.topic(‘/event/idle‘).publish(‘inactive‘);
  5. });
  6. $(document).bind(‘active.idleTimer‘, function() {
  7. bus.local.topic(‘/event/idle‘).publish(‘active‘);
  8. });
  9. })

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

  清单3. 用户活动管理

[javascript] view plaincopy

  1. bus.local.topic(‘/event/idle‘).subscribe(function(status) {
  2. bus.remote.topic(‘/event/user/status/changed‘).publish({
  3. status: status == ‘active‘ ? ‘online‘ : ‘away‘
  4. });
  5. });
  6. bus.remote.topic(‘/event/user/status/changed‘).subscribe(function(evt) {
  7. if(evt.user != me.name) {
  8. $(‘#users li‘).filter(function() {
  9. return evt.user == $(this).data(‘user‘).name;
  10. }).removeClass(‘online‘)
  11. .removeClass(‘away‘)
  12. .addClass(evt.status);
  13. }
  14. });

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

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

  清单4. 用户列表管理

[javascript] view plaincopy

  1. bus.remote.topic(‘/event/user/connected‘).subscribe(function(user) {
  2. $(‘#users ul‘).append(row(user));
  3. });
  4. bus.remote.topic(‘/event/user/disconnected‘).subscribe(function(evt) {
  5. $(‘#users li‘).filter(function() {
  6. return evt.user == $(this).data(‘user‘).name;
  7. }).remove();
  8. });

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

  1. Maven:构建工程

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

  3. 使用了Jersey的REST服务

  结束语 

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

  代码下载

  reverse_ajaxpt5_source.zip

时间: 2024-10-12 23:39:46

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

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

英文原文:Reverse Ajax, Part 3: Web servers and Socket.IO 前言 时至今日,用户期待的是可通过web访问快速.动态的应用.这一文章系列展示了如何使用反向Ajax(Reverse Ajax)技术来开发事件驱动的web应用.系列的第1部分介绍了反向Ajax.轮询(polling).流(streaming).Comet和长轮询(long polling).你已经了解了Comet是如何使用HTTP长轮询的,这是可靠地实现反向Ajax的最好方式,因为现有的所有

反向Ajax,第4部分:Atmosphere和CometD

英文原文:Reverse Ajax, Part 4: Atmosphere and CometD 前言 这一系列文章展示了如何使用反向Ajax技术开发事件驱动的web应用,第1部分内容介绍了反向Ajax(Reverse Ajax).polling(轮询).streaming(流).Comet和长轮询(long polling):第2部分内容介绍了如何使用WebSocket来实现反向Ajax,并讨论了使用Comet和WebSocket的web服务器的局限性:第3部分内容说明的是,如果需要支持多种服

反向Ajax,第2部分:WebSocket

英文原文:Reverse Ajax, Part 2: WebSockets 前言 时至今日,用户期待的是可通过web访问快速.动态的应用.这一文章系列展示了如何使用反向Ajax(Reverse Ajax)技术来开发事件驱动的web应用.该系列的第1部分介绍了反向Ajax.轮询(polling).流(streaming).Comet和长轮询(long polling).你已经了解了Comet是如何使用HTTP长轮询的,这是可靠地实现反向Ajax的最好方式,因为现有的所有浏览器都提供支持. 在本文中

反向Ajax,第1部分:Comet介绍

英文原文:Reverse Ajax, Part 1: Introduction to Comet 在过去的几年中,web开发已经发生了很大的变化.现如今,我们期望的是能够通过web快速.动态地访问应用.在这一新的文章系列中,我们学习如何使用反向Ajax(Reverse Ajax)技术来开发事件驱动的web应用,以此来实现更好的用户体验.客户端的例子使用的是JQuery JavaScript库,在这首篇文章中,我们探索不同的反向Ajax技术,使用可下载的例子来学习使用了流(streaming)方法

http协议与分块传输,持久连接及反向ajax

web QQ都用过吧,这涉及到哪些技术,有一个就是反向ajax. 反向ajax又叫comet,server push,服务器推技术. 应用范围: 网页聊天服务器,, 新浪微博在线聊天,google mail 网页聊天,都有用到. 原理: 一般而言, HTTP协议的特点, 连接<->断开. 具体什么时间断开? 服务器响应content-length,收到到指定length长度的内容时,也就断开了. 在http1.1协议中, 允许你不写content-length,比如要发送的内容长度确实不知道时

关于Web开发里并发、同步、异步以及事件驱动编程的相关技术

一.开篇语 我的上篇文章<关于如何提供Web服务端并发效率的异步编程技术>又成为了博客园里“编辑推荐”的文章,这是对我写博客很大的鼓励,也许是被推荐的原因很多童鞋在这篇文章里发表了评论,有童鞋说我这篇文章理论化很严重,没有实际代码和具体项目做支撑,这个评论让我有种理论和实践脱节的味道,所以我想在这里谈谈我为什么要写这篇文章的原因,这篇文章是把我前不久学习多线程编程的一个总结. 当我从我书堆里找到所有与多线程开发相关的书籍简单阅读后,我发现了一个问题,在java里开发多线程最强有力的实践就是做服

反向Ajax之Socket.io

1.什么是反向ajax? 传统的ajax的困惑? 新需求--当服务器端数据发生变化时,客户端(浏览器端)如何即时得到通知呢? 找一些实际的案例:客服系统.在线聊天 这类应用,有一个显著的特点: 数据并不是单向的,原来的数据,都是从浏览器端向服务器端发起请求,然后获取数据. 现在的需求发生变化了,有时候,数据是从服务器端 推送 到浏览器端. 前面所有的http请求/响应模型都是基于单向的,包括ajax. 从服务端向浏览器端推送数据的这种ajax应用,称之为反向ajax. 2.常见解决方案   有以

反向Ajax:WebSocket

郭晨 软件151 1531610114 WebSocket 在HTML5中出现的WebSocket是一种比Comet还要新的反向Ajax技术,WebSocket启用了双向的全双工通信信道,许多浏览器(Firefox.Google Chrome和Safari)都已对此做了支持.连接是通过一个被称为WebSocket握手的HTTP请求打开的,其用到了一些特殊的报头.连接会保持在活动状态,你可以使用JavaScript来写入和接收数据,就像是在使用一个原始的TCP套接口一样. WebSocket UR

Ajax、反向Ajax和WebSocket 概念

Ajax 异步的JavaScript和XML(Asynchronous JavaScript and XML,Ajax),一种可通过JavaScript来访问的浏览器功能特性,其允许脚本向幕后的网站发送一个HTTP请求而又无需重新加载页面.Ajax的出现已经超过了十年,尽管其名字中包含了XML,但你几乎可以在Ajax请求中传送任何的东西,最常用的数据是JSON,其与JavaScript语法很接近,且消耗更少带宽.清单1给出了这样的一个例子,Ajax请求通过某个地方的邮政编码来检索该地的名称. 清