本教程提供了对 Java Message Service (JMS)的概述,并提供了用它开发程序的一些基础知识。JMS 是由 Sun Microsystems 开发的,它为 Java 程序员提供一种访问企业消息系统的方法,即 面向消息的中间件 (MOM)。MOM 通过中间媒介以间接方式在应用程序之间异步传送数据,用这种方法提供一种以松散耦合的灵活方式集成应用程序的机制。
在开始学习本教程之前,读者应该熟悉 Java 编程和面向对象编程的概念。
编写在本教程中描述的程序需要一个编辑环境,它可以是像操作系统编辑器这样的基本编译环境。在进行开发时,许多人使用集成开发环境(IDE),因为它有专门为编写和测试代码而设计的调试程序和其他特性。
编写这些程序需要 Java 编译器(javac.exe),还需要 javax.jms
包中的 JMS 类和 javax.naming
包中的 Java Naming and Directory Interface (JNDI)类。可以从 Sun 下载它们:JMS 和 JNDI。
要执行并测试这些程序,需要访问 JMS 的供应商实现。大多数 Java 2 Enterprise Edition (J2EE)供应商都会提供 JMS 的一种实现。要想获得设置 JMS 运行时程序和执行程序的有关信息,请参阅供应商文档。
有些编程决定将取决于所使用的 JMS 版本(请参阅 JMS 1.1 中的新内容:统一域)。 JMS 版本 1.1 是为了向后兼容性才而支持 点对点接口 和 Pub/sub 接口,如果使用基于 JMS 版本 1.1 的供应商进行绝对全新的程序开发,则完全可以跳过本教程中有关这些主题的章节。
企业消息系统 | 第 1 页(共8 页) |
Java Message Service 是由 Sun Microsystems 开发的,它为 Java 程序提供一种访问 企业消息系统 的方法。在讨论 JMS 之前,我们分来析一下企业消息系统。
企业消息系统,即 面向消息的中间件(MOM),提供了以松散耦合的灵活方式集成应用程序的一种机制。它们提供了基于 存储和转发 的应用程序之间的异步数据发送,即应用程序彼此不直接通信,而是与作为中介的 MOM 通信。
MOM 提供了有保证的消息发送(至少是在尽可能地做到这一点),应用程序开发人员无需了解远程过程调用(PRC)和网络/通信协议的细节。
消息灵活性 | 第 2 页(共8 页) |
如下图所示,应用程序 A 与应用程序 B 通过使用 MOM 的应用程序编程接口(API)发送消息进行通信。
MOM 将消息路由给应用程序 B,这样,消息就可以存在于完全不同的计算机上,MOM 负责处理网络通信。如果网络连接不可用, MOM 会存储消息,直到连接变得可用时,再将消息转发给应用程序 B。
灵活性的另一方面体现在,当应用程序 A 发送其消息时,应用程序 B 甚至可以不处于执行状态。MOM 将保留这个消息,直到应用程序 B 开始执行并试着检索消息为止。这还防止了应用程序 A 因为等待应用程序 B 检索消息而出现阻塞。
这种异步通信要求应用程序的设计与现在大多数应用程序不同,不过,对于时间无关或并行处理,它可能是一个极其有用的方法。
松散耦合 | 第 3 页(共8 页) |
企业消息系统的真正威力在于应用程序的 松散耦合。在前一页的图表中,由应用程序 A 发送消息指定一个特定目标,如“订单处理”。而现在,是由应用程序 B 提供订单处理功能。
但是在将来,我们可以用不同的订单处理程序替换应用程序 B,应用程序 A 将不再是明智之选。替换应用程序将继续发送消息完成“订单处理”,而消息也仍将得到处理。
同样,我们也可以替换应用程序 A,只要替换应用程序继续发送消息进行“订单处理”,订单处理程序就无需知道是否有一个新的应用程序在发送订单。
发布和订阅 | 第 4 页(共8 页) |
最初,开发企业消息系统是为了实现 点对点模型 (PTP),由一个应用程序生成的每个消息都由另一个应用程序接收。近年来,出现了一种新的模型,叫做 发布与订阅 (或者 pub/sub)。
Pub/sub 用称为 主题(topic) 的内容分层结构代替了 PTP 模型中的惟一目的地,发送应用程序 发布 自己的消息,指出消息描述的是有关分层结构中的一个主题的信息。
希望接收这些消息的应用程序 订阅了 这个主题。订阅包含子主题的分层结构中的主题的订阅者可以接收该主题和其子主题发表的所有消息。
下图展示了发布和订阅模型。
多个应用程序可以就一个主题发布和订阅消息,而应用程序对其他人仍然是匿名的。MOM 起着 代理(broker) 的作用,将一个主题已发表的消息路由给该主题的所有订阅者。
JMS 是什么? | 第 5 页(共8 页) |
Java Message Service 规范 1.1 声称:
JMS 是一组接口和相关语义,它定义了 JMS 客户如何访问企业消息产品的功能。
在 JMS 之前,每一家 MOM 厂商都用专有 API 为应用程序提供对其产品的访问,通常可用于许多种语言,其中包括 Java 语言。JMS 通过 MOM 产品为 Java 程序提供了一个发送和接收消息的标准的、便利的方法。用 JMS 编写的程序可以在任何实现 JMS 标准的 MOM 上运行。
JMS 可移植性的关键在于:JMS API 是由 Sun 作为一组接口而提供的。提供了 JMS 功能的产品是通过提供一个实现这些接口的提供者来做到这一点的。
开发人员可以通过定义一组消息和一组交换这些消息的客户机应用程序建立 JMS 应用程序。
JMS 的目标 | 第 6 页(共8 页) |
更好地理解 JMS 有助于了解 JMS 规范的制定者设置的目标。
现在,市场上有许多企业消息产品,生产这些产品的几家公司也参与了 JMS 的开发。
现有的这些系统的能力和功能各不相同。这些制定者知道如果 JMS 结合所有现有系统的所有功能,那么它会变得过于复杂。同样,他们相信,他们也不能让 JMS 只局限于所有系统共有的那些特性。
制定者相信,让 JMS 包括实现“高级的企业应用程序”所需要的所有功能是很重要的。
JMS 规范中声明, JMS 的目标是:
- 定义一组消息公用概念和实用工具。
- 最少化程序员使用消息时必须学习的概念。
- 最大化消息应用程序的可移植性。
- 最小化实现一个提供者所需的工作量。
- 为点对点和 pub/sub 域 提供客户机接口。“域”是用于在前面讨论的消息模型的 JMS 术语。(注意:提供者不需要实现两个域。)
JMS 1.1 中的新内容:统一域 | 第 7 页(共8 页) |
在 JMS 1.1 以前的版本中,每一个域都有自己的特定于该域的一组客户机接口。JMS 版本 1.1 提供了单一的一组接口,它允许客户机可以在两个域中发送和接收消息。这些“域无关的接口”保留了每一个域的语义和行为,是实现 JMS 客户机的最好选择。支持特定于域的接口是为了向后兼容性,新的开发不该使用它。
统一域的好处是:
- 它展示了用于客户机编程的更简单的编程模型。
- 队列和主题的操作可以是同一事务的一部分。
- 它为 JMS 提供者提供了优化其实现的机会。
哪些是 JMS 没有提供的 | 第 8 页(共8 页) |
以下特性在MOM 产品中常见,但在 JMS 规范中没有出现。尽管 JMS 制定者承认它们对于开发功能强大的消息应用程序很重要,但是这些功能是特定于 JMS 提供者的。
JMS 提供者可以以任何他们愿意的方式自由实现这些功能:
- 负载平衡和容错
- 错误和咨询系统消息和通知
- 管理
- 安全性
- Wire 协议
- 消息类型仓库
JMS概述和体系结构
应用程序 | 第 1 页(共15 页) |
JMS 应用程序由以下元素组成:
- JMS 客户机。 用 JMS API 发送和接收消息的 Java 程序。
- 非 JMS 客户机。 认识到传统程序通常整个 JMS 应用程序的一部分是非常重要的,在规划时必须优先考虑它们的存在。
- 消息。就JMS 应用程序设计而言,通过JMS 和非 JMS 客户机所交换的消息的格式和内容是完整的。
- JMS 提供者。 如前所述,JMS 定义了一组接口,供应者必须提供特定于其 MOM 产品的具体实现。
- 管理对象。 消息系统提供者的管理员创建的、独立于提供者的专有技术的对象。
管理对象 | 第 2 页(共15 页) |
MOM 产品的提供者在实现消息时使用的机制和技术有很大不同。为了保持 JMS 客户机的可移植性,实现了 JMS 接口的对象必须与提供者的专有技术隔离。
完成这项任务的机制是 管理对象。这些实现 JMS 接口的对象由提供者的消息系统的管理员创建,并被放置在 JNDI 名字空间中。
然后由 JMS 程序检索这些对象,通过它们实现的 JMS 接口访问这些对象。JMS 提供者必须提供允许创建受管理对象及它们在 JNDI 名字空间中的存放地的工具。
有两种受管理对象:
ConnectionFactory
:用于创建到提供者的底层消息系统的连接。Destination
:用 JMS 客户机来指定正发送消息的目的地或正接收消息的来源。
尽管受管理对象本身就是特定于提供者实现的类的例子,但可以使用可移植机制(JNDI)检索它们,并且可以通过可移植接口(JMS)访问它们。JMS 程序只需要知道管理对象的 JNDI 名称和 JMS 接口类型即可,无需了解特定于提供者的知识。
接口 | 第 3 页(共15 页) |
JMS 定义了一组封装各种消息概念的高级接口。而这些接口又因为两个消息域——PTP 和 pub/sub——进行了进一步地定义和定制。
高级接口包括:
ConnectionFactory
:一个创建Connection
的受管理对象。Connection
:连接到提供者的活动连接。Destination
:一个封装消息目的地的身份的受管理对象,如消息的来源地和发送地。Session
:发送和接收消息的单线程环境。为了简化,并且因为Session
控制事务的缘故,通过多个线程进行并发访问受到了限制。可以将多个Session
用于多线程应用程序。MessageProducer
:用于发送消息。MessageConsumer
:用于接收消息。
接口 (续) | 第 4 页(共15 页) |
下表列出了从每一个高级接口继承的特定于域的接口。
高级接口 | PTP 域 | Pub/sub 域 |
---|---|---|
ConnectionFactory | QueueConnectionFactory | TopicConnectionFactory |
Connection | QueueConnection | TopicConnection |
Destination | Queue | Topic |
Session | QueueSession | TopicSession |
MessageProducer | QueueSender | TopicPublisher |
MessageConsumer | QueueReceiver ,QueueBrowser | TopicSubscriber |
接口:JMS 1.1 中的变化 | 第 5 页(共15 页) |
在 JMS 以前的版本中,高级接口是特定于域的接口的父接口,并且只包含在两个域中共有的那些功能。JMS 提供者没有提供高级接口的实现。在 JMS 1.1 中,一些高级接口现在则被认为是“公用接口”,并且它们包含两个域的所有功能; JMS 提供者必须提供这些接口的实现。尽管公用接口仍然是特定于域的接口的父接口,但它们现在是 JMS 客户机编程的首选方法,并且它们提供特定于域的接口只为了后向兼容。
下面重新列出了前一小节中的表,展示了一些公用接口。
JMS 公用接口 | PTP 域 | Pub/sub 域 |
---|---|---|
ConnectionFactory | QueueConnectionFactory | TopicConnectionFactory |
Connection | QueueConnection | TopicConnection |
Destination | Queue | Topic |
Session | QueueSession | TopicSession |
MessageProducer | QueueSender | TopicPublisher |
MessageConsumer | QueueReceiver ,QueueBrowser | TopicSubscriber |
统一有公用接口的域会导致继承这些方法的一些特定于域的类不再适合它的域。如果在客户机代码中出现这种情况,JMS 提供者需要使用 IllegalStateException。
开发 JMS 程序 | 第 6 页(共15 页) |
一个典型的 JMS 程序要经过以下步骤才能开始产生和使用消息:
- 通过 JNDI 查询
ConnectionFactory
。 - 通过 JNDI 查询一个或者多个
Destination
。 - 用
ConnectionFactory
创建一个Connection
。 - 用
Connection
创建一个或者多个Session
。 - 用
Session
和Destination
创建所需要的MessageProducer
和MessageConsumer
。 - 启动
Connection
。
这时,消息就可以开始流动,应用程序可以根据需要接收、处理和发送消息。 在后面几节中,我们将开发 JMS 程序,您将会看到这些步骤的细节。
消息 | 第 7 页(共15 页) |
header 字段 | 第 8 页(共15 页) |
标准属性 | 第 9 页(共15 页) |
下面列出了 Message
的每一个标题字段的名称、它对应的 Java 类型和字段的描述:
JMSMessageID
——类型为string
惟一标识提供者发送的每一条消息。这个字段是在发送过程中由提供者设置的,客户机只能在消息发送后才能确定消息的
JMSMessageID
。JMSDestination
——类型为Destination
消息发送的
Destination
,在发送过程中由提供者设置。JMSDeliveryMode
——类型为int
包含值
DeliveryMode.PERSISTENT
或者DeliveryMode.NON_PERSISTENT
。持久性消息被传输并且只被传输一次,非持久性消息最多被传输一次。要知道“最多一次”包括根本不传输。非持久性消息在应用程序或者系统出故障时被提供者弄丢。因此要格外小心,确保持久性消息不受故障的影响。这比开销通常被认为是发送持久性消息方面的开销,在决定消息的发送模式时,必须仔细考虑,在可靠性和性能之间进行权衡。JMSTimestamp
——类型为long
提供者发送消息的时间,由提供者在发送过程中设置。
JMSExpiration
——类型为long
消息失效的时间。这个值是在发送过程中计算的,是发送方法的生存时间(time-to-live)值和当前时间值的和。提供者不应发送过期的消息。值 0 表明消息不会过期。
JMSPriority
——类型为int
消息的优先级,由提供者在发送过程中设置。优先级 0 的优先级最低,优先级 9 的优先级最高。
JMSCorrelationID
——类型为string
通常用来链接响应消息与请求消息,由发送消息的 JMS 程序设置。响应来自另一个 JMS 程序的消息的 JMS 程序将正响应消息的
JMSMessageID
拷贝到这个字段中,这样,正作出响应的程序就可以与它所发出的特定请求的响应相 关联。JMSReplyTo
——类型为Destination
请求程序用它来指出回复消息应发送的地方,由发送消息的 JMS 程序设置。
JMSType
——类型为string
JMS 程序用它来指出消息的类型。一些提供者维护着一个消息类型仓库,并用该字段引用仓库中的定义类型,在这里,JMS 程序不应该使用这个字段。
JMSRedelivered
——类型为boolean
指出消息被过早地发送给了 JMS 程序,程序不知道消息的接收者是谁;由提供者在接收过程中设置。
下面列表给出了 Message
的每一个标准属性的名称、它对应的 Java 类型和该属性的说明。提供者对标准属性的支持是可选的。JMS 为这些属性和将来 JMS 定义的属性保留了 “JMSX” 属性名。
JMSXUserID
——类型为string
发送消息的用户的身份。
JMSXApplID
——类型为string
发送消息的应用程序的身份。
JMSXDeliveryCount
——类型为int
已经尝试发送消息的次数。
JMSXGroupID
——类型为string
该消息所属的消息组的身份。
JMSXGroupSeq
——类型为int
该消息在消息组中的序号。
JMSXProducerTXID
——类型为string
生成该消息的事务的身份。
JMSXConsumerTXID
——类型为string
使用该消息的事务的身份。
JMSXRcvTimestamp
——类型为long
JMS 将消息发送给客户的时间。
JMSXState
——类型为int
提供者用它来维护消息的消息仓库,通常,它与 JMS 生产者和客户关系不大。
JMSX_<vendor_name>
为特定于提供者的属性而保留。
消息系统的核心当然是消息。JMS 为不同类型的内容提供了几种消息类型,但所有消息都是从 Message
接口派生出来的。
Message
分为三个组成部分:
- header 是一组标准字段,客户机和提供者都用它们来标识和路由消息。
- Properties 提供了一个给消息添加可选标题字段的实用工具。如果应用程序需要用标准标题字段没有提供的方法对消息进行归类或分类,那么可以为消息添加一个属性来实现这种归类和分类;提供了
set<Type>Property(...)
和get<Type>Property(...)
方法来设置和获得各种 Java 类型的属性,其中包括Object
。JMS 定义了提供者可以选择性提供的一组标准属性。 - 消息的 body 包含将 发送到接收应用程序的内容。每一个消息接口都专用于它所支持的内容类型。