Java 消息服务常见问题

一般问题

技术问题

一般问题

问. 什么是 Java 消息服务?
答: Java 消息服务(Java Message Service,JMS) API 是一个用于访问企业消息传递系统的 API。是 Java 2 Platform, Enterprise(J2EE)的一部分。

Java 消息服务使得编写那些需要异步发送及接收重要业务数据和事件的业务应用变得容易。

Java 消息服务定义了一个公共的企业消息传递 API,该 API 设计来被大范围的企业消息传递产品容易而有效地支持。

Java 消息服务不仅支持消息队列,还支持消息传递的发布/订阅模式。

问. Java 消息服务是另一种邮件 API 吗?
答: 不是。术语“消息传递”在计算机领域的定义很广。它被用来描述不同的操作系统概念,用于描述电子邮件和传真系统;如果使用了 JMS API,它被用来描述企业应用之间的异步通信。

JMS 消息是指由企业应用(而不是人)消费的异步请求、报告或事件。它们包含了协同这些系统所需的重要信息,包含了描述特定业务活动的精确格式化的数据。通过这些消息的交换,每个应用可以跟踪企业的进展。

问. Java 消息服务是一种产品吗?
答: 不是,Java 消息服务是一个用于企业消息传递的公共 API 的规范,使用它需要一个由企业消息传递供应商提供的 JMS 提供者。

问. 可从哪里获得 Java 消息服务规范?
答: Java 消息服务 1.0.2 规范可从 http://java.sun.com/products/jms/docs.html 处获得。在线的 JMS API 文档也可在这个链接地址中找到。

问. 谁与 SUN 公司一起制定了 Java 消息服务 1.0.2 规范?
答: 起初有众多重要的行业参与者与 SUN 公司一起共同制定了 Java 消息服务规范的初稿。另外,在为期 3 个月的公众评审期间,收到了来自其他公司、政府和教育组织以及其他机构的许多建议。

问. Java 消息服务有何引人注目的?
答: Java 消息服务引人注目的原因是:

  • 它是第一个得到广泛行业支持的企业消息传递 API。
  • 通过提供适应广泛企业消息传递系统的标准消息传递概念和约定,它简化了企业应用的开发。
  • 它利用现有的,经过企业验证的消息传递系统。

问. 开发人员为何应该使用 Java 消息服务?
答: 因为 Java 消息服务能使下面的工作变得简单:

  • 编写可移植的,基于消息的业务应用。
  • 通过加入新的能够与现有非 JMS 客户端完全协作的 JMS 客户端,以扩展现有的基于消息的应用。

问. 为了学习使用 Java 消息服务,程序员需要具备什么?
答: 基于消息的应用和基于远程过程调用的应用是根本不同的。一旦开发人员理解了如何最好地使用这两个技术, JMS API 就使得编写基于消息的应用跟学习几个附加接口一样容易。

问. Java 消息服务和企业 JavaBeans 组件有何关系?
答: 自从 J2EE 1.2 版本发布以来,企业 JavaBeans 组件已经能够使用 JMS API 来同步地发送和接收企业消息。J2EE 1.3 版本现在已可获得,它提供了一种新的允许企业应用异步地接收消息的企业 Bean,即消息驱动 Bean。

问. Java 消息服务 API 与 Java 命名和目录接口(JNDI) API 之间有何关系?
答: 跟其他 Java 企业 API 一样,JMS API 使用 JNDI API 来进行管理。JMS API 将 ConnectionFactories 和 Destinations 定义为被管理对象,它们被配置并放入到 JNDI 命名的上下文中。然后 JMS 客户端查找和使用这些预先配置好的对象。这样保证了 JMS 应用易于部署和管理。

问. Java 消息服务 API 与 Java 数据库连接(JDBC)API 之间有何关系?
答: JMS 客户端或许也使用 JDBC API。在同一个事务中它们可能既使用 JMS API 也使用 JDBC API。大多数情况下,通过把这些客户端实现为企业 Bean,JMS 客户端可自动完成。JMS 客户端可能也使用 Java 事务 API。

问. Java 消息服务与 Java 事务 API 及 Java 事务服务之间有何关系?
答: Java 事务 API (Java Transaction API,JTA) 提供了一个用于划分分布式事务的客户端 API,以及一个用于访问资源以能够参与分布式事务的 API。JMS 客户端可能使用 JTA 来划分分布式事务。JMS 提供者通过 JTA 可以自如地支持分布式事务。

Java 事务服务 (Java Transaction Service,JTS) 能和JMS API一道用来形成分布式的事务,该事务结合了消息收发与数据库更新以及其他 JTS 能识别的服务。当 JMS 客户端在应用服务器(比如 J2EE 服务器)中运行时,这些服务应该被自动处理;可是, 也有可能需要 JMS 客户端去明确编写它们。

问. 在哪里可以找到 JMS API 讨论组?
http://java.sun.com/j2ee/community/forums/index.html 处可找到一个 JMS API 论坛。要加入这个论坛,您必须是 Java Developer Connection 的成员。

问. 为什么 JMS API 发展成为一种在企业内部组件间提供通信的技术,而不是用于在企业间通过 Internet 进行企业-企业(B2B)的通信?
答: 提供基于 Internet 的 JMS API 消息传递的主要问题是,JMS API 供应商们必须使用一种共同的通信格式,并且 JMS 规范必须指定一个路由器-路由器 API。

B2B 消息传递是一个重要的领域,有它自己独特的要求。该领域正被一些不断涌现的标准所占据,比如电子商务 XML计划,即 ebXML (参见 http://www.ebxml.org/),SUN 公司在这一方面起到了积极的作用。现在,ebXML 计划定义了一个消息传递服务 (参见 ebXML Specifications 页面), 它支持通过Internet 的 XML 消息传递。当前的 ebXML 规范着重于,通过使用多种传输协议,比如 HTTP、mail 和 FTP,通过 Internet 提供终端间的点到点异步通信。ebXML 还计划提供发布/订阅式的 XML 消息传递。Sun 公司和它的合作伙伴正在定义一个新的 Java API for Messaging,即 JAXM (参见 JSR #000067, Java APIs for XML Messaging 1.0), 它支持 ebXML 消息服务规范。早先版本的 JAXM 可以从 Java Developer Connection 的 The M Project 1.0 Early Access 处得到。

总之, JMS API 是特意设计为在企业内部使用的,而独立企业之间通过 Internet 进行的 XML 消息传递将得到 ebXML 计划和新的支持不同 ebXML 规范的 Java API(包括 JAXM)的支持。

技术问题

问. JMS API 需要哪一种 JDK 版本?
答: JMS API 需要 JDK/JRE 1.1.x 或者更高的版本。

问. JMS API 是否提供了一个分布式 Java 事件模型的版本?
答: 一般来讲,JMS API 可以被用作通知服务;但是,它没有定义 Java 事件的分布式版本。一个实现分布式 Java 事件的可选方案是作为 JavaBeans 组件,该组件通过 JMS API 透明地向事件产生 Bean 和事件监听 Bean 分布事件。

问. 为什么有两个各自分开的 JMS API 域,即点到点和发布/订阅,而不合为一个域呢?
答: 即使有许多类似之处,提供独立的域仍然很重要。这意味着厂商不必被强迫支持不属于他们的域内的设备,客户端代码也更加轻便灵活,因为产品更充分地支持一个域(相对于支持一个合并域中较少定义的子集而言)。

问. 为什么 JMS API 不指定一套 JavaBeans 组件?
答: JMS API 是一个低级的 API, 就像其他低级的 Java API 一样, 它无助于直接表现为 JavaBeans 组件。

问. JMS API 如何与 CORBA 通知服务相结合?
答: 通知服务为 CORBA 事件服务增加了过滤功能、传输保证语义、持久性连接和事件网络的集结。它从 CORBA 消息服务(该服务定义了异步 CORBA 方法调用)获得传输保证语义。

Java 技术与 CORBA 很好的结合在一起。它提供了 Java IDL 和 COS Naming 技术。另外,OMG 最近定义了 RMI over IIOP。

预计大多数 Java 对 IIOP 的使用将通过 RMI。预计大多数 Java 对 COS Naming 的使用将通过 JNDI API(Java 命名和目录服务)。JMS API 是 Java 特定的 API,它设计位于大范围现有和未来的面向消息中间件(Message Oriented Middleware,MOM)系统的上层(正如 JNDI API位于现有命名和目录服务的上层一样)。

问. 为什么 JMS API 不提供端到端的同步消息传输和传输通知?
答: 作为一个实现可靠应用的机制,一些消息传递系统提供到目的地的同步传输。一些系统为客户端提供了不同形式的传输通知,这样客户端就能检测出被删除或忽略的消息。这不是由 JMS API 定义的模型。

JMS API 消息传递通过一次并且只有一次的 PERSISTENT 消息传输语义来提供有保障的传输。另外,消息消费者可以通过使用 CLIENT_ACKNOWLEDGE 模式或事务会话来确保可靠的消息处理。这样使用最小的同步实现了可靠的传输,这是大多数厂商和开发者都喜欢的企业消息传递模型。

JMS API 没有定义系统消息(如传输通知)的模式。如果应用需要消息收到的确认回复,可以定义一个应用级的确认消息。

这些问题放到发布/订阅应用的上下文中考察,将能更清楚地理解。在这个上下文中,同步传输和/或系统接收确认对于实现可靠的应用并不是一个有效的机制(因为,按照定义,生产者不会,也不想负责端到端的消息传输)。

问. 为什么 JMS API 不提供群体发送(send-to-list)机制?
答: 当前,JMS API 提供大量的消息传送选择;但是,消息在同一时刻只能发送到一个目的地。

群体发送的好处是,程序员只需做很少的工作,并且还有 JMS 提供者对发送同样的消息到多个目的地的情况进行优化的潜力。

群体发送机制不利的方面是,目的列表在效果上是由客户端实现并维护的集合。这将增加 JMS 客户端管理的复杂度。

不使用提供群体发送机制的 JMS API,而推荐提供者支持配置代表一个群体的目的。这允许客户端通过一次发送便可以到达所有的消费者,同时保证群体被正确地管理。

问. 为什么 JMS API 不提供订阅通知?如果发布者能够察觉到一个主题的订阅者已经存在,它将能抑制非订阅主题的发布。
答: 尽管给发布者提供抑制发布非订阅主题的机制可能会带来一些好处,但是这将增加 JMS API 的复杂性,并需要附加的提供者开销,而这些并不与它带来的好处成正比。相反,JMS 提供者应该保证最小化用于处理发布给非订阅主题的消息的开销。

问. 如果消息消费者已经关闭了,该消息还能被确认吗?
答: 可以。既然消息确认是在会话级处理的,那么在消费者关闭后,消息确认仍然是相关的。所有由会话消费的消息按以下两个例子情况被确认:

// CLIENT_ACKNOWLEDGE session
Message msg1 = topicSubscriber1.receive();
Message msg2 = topicSubscriber2.receive();
topicSubscriber1.close();
msg2.acknowledge();

// transacted session
Message msg1 = queueReceiver.receive();
queueReceiver.close();
session.commit();

在会话被关闭后,对 Message.acknowledge 或 Session.commit 方法的调用将抛出IllegalStateException 异常。对于事务型会话,如果在进行事务提交之前调用 close 方法,那么该事务将被回滚。

注意,为了防止从持久订阅或队列重复投递消息,一个仍能被会话确认的消息不能被投递到别的消息消费者。只有当初始收到消息的会话再也不能确认该消息时,此消息才能被投递到别的消息消费者。

问. 当同一队列在相同时间有两个或以上 QueueReceiver 时,JMS API 指定的是什么分发策略?
答: 当有两个或以上的 QueueReceiver 同时注册到某个目的地时,JMS API 并没有指定消息分发策略。JMS API 语义上在任何时刻只说明一个 QueueReceiver。JMS API 并不禁止一个队列有多个 QueueReceiver;它只是没有为这种情况定义行为。

问. 在一个会话当中的同一个主题有两个或以上 TopicSubscriber 时,CLIENT_ACKNOWLEDGE 模式是如何工作的?
答: 从 JMS 1.0.2 规范第 6.11 节得知:

一个 TopicSession 允许为每个目的地创建多个 TopicSubscriber,它将把针对某个目的地的消息投递到每一个能接收它的 TopicSubscriber。每一个消息的拷贝将被认为是完全独立的消息。对其中的一个所做的处理将不会影响到其他的拷贝;确认其中一个并不等于确认其他;一条消息可以马上被投递,即使在它之前有其他的拷贝正在等待消费者去处理。

Message.acknowledge 方法是编写来确认由会话消费的所有消息的接收。因此,如果msgA 被投递到同一个会话中的主题订阅者 TS1 和 TS2,TS1 同步接收 msgA 的拷贝,那么 TS2 也能同步接收 msgA 的拷贝。然而,如果 TS1 接收 msgA 的拷贝,然后 TS2 接收 msgA 的拷贝,TS2 对接收该消息的确认将确认 TS1 和 TS2 都收到了 msgA 的拷贝,事实上会话接收 TS1 的 msgA 拷贝是在会话接收 TS2 的 msgA 拷贝之前发生的。

问. 对于已关闭的消费者来说,消息的可恢复性指的是什么?
答: 对于从队列或持久订阅消费的消息,回滚机制能确保该消息不被确认,并将它传送给这些持久实体的下一个消费者。JMS 规范声明,发送给非持久订阅者的消息会被丢失。该声明意味着 JMS API 并没有定义非持久订阅者消费的消息的可恢复性。

问. 为什么不能使用 J2EE 1.2.x SDK 运行 JMS API 示例程序?
答: J2EE 1.2.x 参考实现并没有包含 JMS API 的实现,只包含了它的接口。JMS 参考实现可以作为 J2EE 1.3 的一部分得到,并可在 J2EE 1.3 服务器上运行它的示例程序。参见 JMS Tutorial 以获得关于在 J2EE 1.3 服务器上运行简单 JMS 客户端程序的更多信息。

请点击 Java Message Service API Licensees 查看提供 JMS API 实现的供应商列表。许多供应商对各自的 JMS API 实现都有示例,能用来运行从网站上下载的 JMS API 例子程序。

问. 根据规范,“试图获得某个未设定的名称的属性值将被处理为,就好像该属性已存在,值为 null。”那么对于 getStringProperty,这意味着返回一个 null 值,还是一个 null 字符串?
答: 两者都是允许的。这是因为一些 MOM 系统压根就没有 null 字符串的概念,因此它们将所有值为 null 的字符串在发送时都转换为空字符串。

问. 用 Connection.setExceptionListener(ExceptionListener) 方法去尝试处理连接问题,但是当停止服务器的时候, 好像没有 JMSException 被触发;而且onException 方法也没被调用。这是一个 bug 吗?
答: JMS 规范并没有指定传送给 ExceptionListener 的究竟是何异常,以及它们是何时被传送的,因此提供者们在如何处理连接问题上是多种多样的。这方面如果有问题的话,请与您的 JMS 提供者协商。

问. 依照 JMS 规范的第 3.4 节,一个客户端只能有效地设置消息表头字段中的三个。所有的其余字段要么由 JMS 提供者设置,要么由 sendpublish 方法设置。为什么所有字段都有一个 setter 方法,当消息被发送或者被 JMS 提供者传递时,任何客户端的设置又是在何时覆盖原值的呢?难道对大多数字段只允许 getter 方法没有意义了?
答: 规范需要 setter 方法主要是为了保持一致性,因此每个消息字段都有一个getter 和 setter方法。另一个原因是, 在一个消息被接收后但是在它被传送到应用程序的其他部分去处理之前,允许接收的客户端更改消息的某个字段。虽然大多数的 setter 方法很少被用到,但是它们给了程序员很大的灵活性。

问. 两个不同的 JMS 服务能彼此通话吗?举例来说, 如果 A 和 B 是两个不同的 JMS 提供者, 那么提供者 A 能把消息直接发送给提供者 B 吗?如果不能, 那么提供者 A 的订阅者能担任提供者 B 的发布者吗?
答: 第一个问题的答案是不能,而第二个问题的答案是能。JMS 规范并不要求一个 JMS 提供者能够直接发送消息给另一个提供者。然而,规范确实要求一个 JMS 客户端必须能够接收另一个不同的 JMS 提供者发送的消息,因此,提供者 A 的一个订阅者收到的消息能被再次发布给提供者 B。需注意的是,提供者 B 的发布者并不需要处理一个叫 JMSReplyTo 的表头,该表头引用一个特定于提供者 A 的目的地。

 

常见问答

下载中心

产品简介

 

 

Solaris论坛