Mark Richard 的《Java 消息服务》第二版

阅读数:4349 2010 年 2 月 1 日

话题:Java架构Book Review语言 & 开发

Mark Richards 的新书《Java 消息服务》第二版覆盖主题包括异步消息和 Java 消息服务(JMS)。JMS规范自 2000 年本书第一版出版以来已经取得了长足发展。

在新书中,Mark 讨论了消息的基本概念,消息产品厂商的常用架构以及 JMS 的两种编程模型:发布 - 订阅模型点对点模型。该书还涉及到了如消息过滤、有保证的消息传递及事务等高级专题。

Mark 讨论了 Java EE 对 EJB 3.0 规范的消息传递的实现(使用消息驱动 Bean)以及 Srping 框架使用 Srping JMS 模板和消息驱动对象(MDPs)实现的 JMS 消息传递。

该书以对消息服务的设计结束,包括使用内部与外部目的地,请求 / 响应处理以及一些常见消息传递反模式,例如单一目的队列,过度使用消息优先权和误用消息头。书中使用的例子代码是ActiveMQ,一个开源的消息容器。

InfoQ 与 Mark 讨论了他这本新书,包括写作的主要动机以及其他议题。访谈的议题包括在实现消息应用中和企业服务总线(ESB)架构中 Spring 消息驱动对象(MDP)的作用。

我们还从《Java 消息服务》一书中为我们的读者摘取了一段(314K 的 PDF 文档)摘要

InfoQ:写作本书第二版的主要动机是什么?

Mark Richards (MR): 当别人要求我修订本书的第一版时,我没想到这会需要很大的努力,尤其是本书的第一版写得非常好,从 Java 社区得到的好评如潮(感谢 Richard Monson-Heafel 和 David Chappell 为第一版所作的贡献 )。然而,当我开始修订原稿的手稿时,我很快发现自从第一版以来的 10 年中消息领域发生了巨大的变化。这种变化不仅仅是在于 JMS 的版本升级(从 JMS1.02 到 JMS1.1)方面,而且还在于消息的使用方式方面。新技术、工具以及科技都在冲击着市场,包括消息驱动 Bean(MDB),Spring 消息驱动对象(MDP),企业级开源产品提供者例如 ActiveMQ,RESTFul JMS 接口,面向服务的架构以及企业服务总线中间件产品。此外,随着 Java 平台日渐成熟,我们使用消息的方式也日趋成熟。从与异构平台和组件的互操作到 高速异步并发处理,消息已经在我们今天使用的大多数架构中证实了它的价值。因此,我在第二版中重写了本书 75% 的内容。

我写本书的第二版的主要动机是我对于技术的激情与对消息领域的强烈兴趣和丰富经验的联合结果。这个写作机会让我得以把我的新生命融入一本伟大的著作中,帮 助原著传播什么是 JMS API 以及如何使用它来解决一些非常有趣的问题。在第二版中有很多新内容,讲述了我们今天如何使用消息。

InfoQ:Spring 的消息驱动对象(MDP)已经成为 EJB 模型中的消息驱动 Bean(MDB)代替设计。你在书中写 到了 Spring 的 MDP 以及使用消息驱动 Bean(MDB)的 J2EE 方式来实现异步消息传递。Spring 作为一个完善的 DI 框架,它的 MDP 架构相 对于 MDB 有什么优势呢?

MR: 绝对有优势。EJB 与 Spring 框架都支持容器管理的异步监听器。然而,MDB 架需要 EJB 的支持,其支持是非常简单而直接的,仅仅提供一种类型的异步消息监听器。一个 MDB 必须实现javax.jms.MessageListener接口,需要你实现 onMessage() 方法,该方法接收一个javax.jms.Message类型的消息对象作为参数。

在另一方面,Spring 支持三种类型的异步消息监听器(MDP)。第一种类型的 MDP 与对应的 MDB 最接近,实现了 javax.jms.MessageListener 接口和 onMessage() 方法,需要你处理一个 javax.jms.Message 类型的消息 。第二种类型与第一种类似,但是实现了 Spring 框架的接口 SessionAwareMessageListener,允许 javax.jms.Session 对象与消息对象一起传入 onMessage() 方法,这样就易于在收到消息后发送或者响应消息。第三种类型 MDP(我最喜爱的类型)是一个真正的 POJO,使用一个 Spring 的 MessageListenerContainer。通过第三种 MDP,Spring 会自动为你处理所有的消息变换,允许你构造一个异步的消息监听器 (MDP),该监听器有一个诸如 processTrade() 的方法,该方法接收一个从消息负载中得到的包含一些 XML 的字符串参数,而不是像非描述性的 onMessage() 方法那样接受一个消息对象。

基本上,Spring 的 MDP 删除了与消息相关的繁文缛节,允许你专注于业务逻辑。这真的很酷,值得一看。

InfoQ:Servlet 3.0 规范为 web 层带来了异步通信。你认为 Servlet 3.0 规范会对企业软件开发和有异步处理需求的案例带来什么样的冲击?这对于传统的、也许不像 servlet 模型那么轻量级的使用 JMS 技术的异步消息编程意味着什么?

MR:首先,我不认为我同意 servlet 模型比一个简单的消息框架(特别是使用 Spring 时)还轻量级。我没有发现 Servlet 3.0 规范会代替现存的或者将来的 JMS 消息系统。消息系统提供了 Servlet 3.0 规范异步通信方面之外的几个重要方面,包括高级的负载平衡,更好的监控能力,以及也许是最重要的——确保交付能力。我认为不考虑消息处理 层,Servlet 3.0 规范异步通信方面会对通常的消息(包括 JMS 和非 JMS)有益,因为它会增加人们对 Java 平台上异步通信重要性的认识,然而,我的观点是它不会成 为强大而简单的 JMS 消息框架的代替品,尤其是目前的消息产品WebSphere MQ和 Apache ActiveMQ 正在向 RESTful 消息接口方面努力。

InfoQ:企业服务总线(ESB)模式在异步消息 /JMS 架构模型中适用于什么方面?

MR:企业服务总线可以通过各种协议与消费者和生产者(包括内部的和外部的)通信,包括 HTTP 和 JMS。这些协议都可以发 送和接收 SOAP 消息和普通 XML(或者其他用于特定用途的消息负载格式)。然而,大多数企业服务总线在防火墙之后作为一个消息中间件工作,提供媒介、路 由和转换功能(和其他功能)。因此,我认为 JMS 消息机制是比 HTTP 之上的 web service 好的多的选择,在访谈中我已经提到了许多原因 —— 更高的可靠性,更低的复杂性,更优的负载平衡,更好的监控,更好的性能以及提供确保投递的能力。

毫不奇怪,在大多数企业集成模式中,ESB 是基于消息的概念的。大多数 ESB 允许你在 HTTP 和 JMS 协议之间方便的切换。除非你有来自外部互联网的客户请求,我建议你坚持使用 JMS 而不要切换到 HTTP。

InfoQ:什么是开发者和架构师在架构和构建现实的 JMS 系统时应该采用的最佳实践和“秘笈”?

MR:我最喜爱的“秘笈”是为一个消息设置过期时间。因为 JMS API 在 javax.jms.Message 对象中通过标准的 get 和 set 方法暴露了消息头属性,开发者经常试图直接调用 message.setJMSExpiration() 来设置消息过期。然而,除了 JMSReplyTo、JMSCorrelationID 和 JMSType 之外,JMS 提供者会重写每个消息头。因此,当你通过 set 方法直接设置消息头属性使消息过期,该消息实际上永远不会失效。你需要使用 MessageProducer 接口的 setTimeToLive() 方法来设置消息过期。同样,调用 message.setJMSPriority() 不会设置消息的优先级。你需要使用 MessageProducer 接口的 setPriority() 方法。

开发者应该意识到还有许多设计实践和陷阱,例如使用太少(或者太多)队列,使用太少(或者太多)并发监听器,什么时候应该使用消息过滤而不是多队列或者主 题,什么时候使用点到点消息模型,什么时候使用发布 - 订阅模型。所有这些场景(以及更多的场景)都在新版的 JMS 书中谈到了。

InfoQ: 异步消息在面向服务架构 (SOA) 中扮演着重要角色,不管是作为事件驱动架构 (EDA) 还是 ESB 设计的一部分。你认为在 SOA 领域中 JMS 扮演着什么角色?

MR:在 SOA 环境中部署的服务应该高度解耦。服务可能有在多种语言和平台上实现,包括一些遗留的主机系统。SOA 的伟大之 处之一是服务实现完全独立于服务定义。假如你不改变服务契约,那么你就可以替换、修改该服务实现,而客户根本不知道服务的实现发生了变化。这里是消息系统 大显身手之处——与通过消息来解耦组件和服务相比,还有什么更好的方式?大多数企业级商业和开源的消息产品都提供异构的通信,也就是说 Java 可以给一个 C# 应用或者 CICS 组件发送一个消息。在 SOA 中消息是解耦解决方案的至关重要的一部分。

InfoQ:对于 JMS 规范,你希望会有什么新特征,或者现有特征有什么变化?

MR: JMS1.1 规范在 7 年前的 2002 年三月发布。目前没有更新 1.1 规范的动向,这说明当前的 JMS 规范实际上是非常稳定的,经过深思熟虑的。我希望规范 变化的或者增加的特征是在发布 - 订阅模型中定义子主题,这样可以增加一致性。许多消息产品供应商允许你定义主题的一个继承结构。例如,你可能有一个叫做 “ERRORS”的根主题。该主题下也许有两个孩子主题(或者子主题),定义为“ERRORS.VALIDATION"和“ERRORS.DB”。这些将 来可以分解为验证或者数据库类型错误。你可以定义一个订阅者,使用一个通配符“ERRORS.*”来接收所有的错误。或者你订阅 “ERRORS.VALIDATION.*”来接收所有验证错误。最终,你通过直接订阅“ERRORS.VALIDATION.XMLPARSE”得到所 有 XML 解析验证错误。

因为 JMS1.1 规范没有定义如何处理子主题,所以每个消息产品都实现的不同。例如, IBM WMQ 定义分隔符为一个斜杠(“/”),通配符为一个星号(“*”),而SonicMQ是用一个店(“.”)作为主体分隔符,一个星号(“*”)作为通配符。SwiftMQ使用一个百分号(“%”)作为通配符,等等。如果这成为标准,那么所有产品都会一致,这样会很好。

InfoQ:对于本书和 JMS 的话题你还有其他评论么?

MR:我相信消息技术(特别是 JMS)正在迅速成为开发者、设计者和架构师的一项“核心能力”。对于消息技术和 Java 平台来说现在都是激动人心的时刻。新出版的《Java 消息服务》第二版会帮助开发者理解 JMS APi 和常用的消息概念,从而很快地迎头赶上。

InfoQ:谢谢你 Mark。


感谢宋玮对本文的审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家加入到InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。