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。