|
英文原文: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. 总线设置
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();
}
})
};
it知识库:反向Ajax,第5部分:事件驱动的Web开发,转载需保留来源!
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。