级别: 初级 Vernon Green (dkoo@ca.ibm.com), IBM WebSphere Development, 赫斯利(Hursley),英国
2003 年 6 月 01 日
© Copyright International Business Machines Corporation 2003. All rights reserved.
引言
IBM WebSphere®Application Server 支持调用 Java™ 消息服务(Java™Message Service,JMS)的应用程序,它使得应用程序能够在事务范围内使用消息传递的点到点模式和发布/预定模式来发送和接收消息。此外,Enterprise JavaBeans(EJB)2.0 引入了消息驱动 bean(MDB)。应用程序服务器侦听指定的目的地,并且在有 JMS 消息被传递时,就会将它们传到 MDB 中进行处理。然后 MDB 就委托应用程序 bean 来处理消息。
WebSphere 扩展的消息传递增强了应用程序服务器的这种基本消息传递能力。它提供了一个用于处理传入消息并(在适当的情况下)发送针对这些消息的应答的简单编程模型。此外,它还能处理传出的消息,并能够在响应被发往服务器时提供响应的相关性。通过使用公共使用模式,使得业务组件无法察觉到正在使用的是异步消息传递。把这些模式与异步消息传递一起使用意味着应用程序不必再等待每个单独的请求,而是可以并行地处理这些请求。通过把消息传递逻辑与业务逻辑清楚地分开并使用服务器工具来管理消息传递交互可以实现这一点。扩展的消息传递是 WebSphere Application Server Enterprise,V5 的一个功能部件。
基本消息传递模式
应用程序可以使用多种不同的消息传递方案来满足它们的业务需求,但通常情况下它们都倾向于使用公共模式:
| 单向通信: | 在这种方案中,应用程序发送一条消息但不要求响应。这种消息模式通常被称为
数据报(datagram)。
| | 双向通信(请求/应答): | 在这种方案中,应用程序 A 将一个请求发送给应用程序 B,并希望应用程序 B 应答这个请求。应用程序 A 把来自于应用程序 B 的应答称为
响应。我们在样本案例中所使用的就是这种请求/应答模式。
|
我们通常将这些基本消息传递模式组合起来以支持多种消息传递方案。例如:
| 单向传递并转发: | 应用程序 A 将消息发送给应用程序 B,然后应用程序 B 又将这个消息发送给应用程序 C。 |
WebSphere Application Server 中的应用程序可以:
- 根据这些模式发送消息,
- 作为此类消息的接收方,
- 或两者皆可。
图 1 展示了两种模式,它们阐明了本文使用的术语:
- 在第一种模式(单向或数据报)中,
消息被从
源发送到
目的地。
- 在第二种模式(双向或请求/应答)中,“源”将一个
请求发送给“目的地”。然后“目的地”发回一个
应答,而“源”将这个应答作为
响应来检索。
图 1. 基本消息传递术语
消息 bean
WebSphere 扩展的消息传递用两个消息 bean 来支持上面所讲的消息传递模式。这些消息 bean 是根据 EJB 构建的,分别被称为
发送方 bean和
接收方 bean。您可以使用 WebSphere Studio Application Developer,集成版(以下简称为 Application Developer)来构造这些 bean。Application Developer 鼓励 bean 开发人员将消息逻辑和业务逻辑分开,并通过生成用于处理消息的解析和格式化的代码来帮助他们。这些生成的消息 bean 中包含了用于定义消息内容的代码,并调用 WebSphere Application Server 附带的扩展的消息传递运行时。正如我们将在后面的内容中看到的一样,我们用名为
输入端口和
输出端口的资源来定义发送和接收消息所需的 JMS 资源。
调用发送方 bean 中的方法时会向它们传送多个对象,然后这些对象会被映射为消息。同样,接收方 bean 也会把从消息中抽取出的对象传递给
应用程序 bean。这些对象可以是任何类型(Java 基本类型或用户定义的类型)。用户定义的对象可以代表任何业务对象,但它们必须是可序列化的。例如,一个用户定义的对象可以代表一个银行账户的细节,或代表一个 XML 文档。
发送方 bean
发送方 bean 是根据无状态会话 bean 构建的。发送方 bean 封装了消息的发送和对所发送消息的响应的处理。它们还能处理由参数到 JMS 消息的转换以及由响应消息到对象的转换。
发送方 bean 支持以下三种交互模式:
-
发送一条消息并随后接收到一个同步的响应。
在这种模式中,由发送方 bean(在其运行时的支持下)组装消息并完成发送。然后,发送方 bean 在将响应返回给应用程序 bean 之前等待远程终端的应答。尽管应用程序看见的是一个同步模式,但由于 JMS 提供者(provider)的缘故,底层消息传递却是以异步方式完成的。扩展的消息传递支持提供了同步行为。您可以指定一个超时值以避免系统无限期地等待响应。因为系统无法在同一事务中检索一个已发送的消息的响应,所以它往往将忽略所有的事务边界,强制立即发送消息。
-
延迟响应的接收。
在这种模式中,发送方 bean 组装并发送消息,然后将一个
相关因子返回给调用者,相关因子是一个随后被用于检索与所发送消息相对应的响应的对象。应用程序负责在晚些时候检索响应。这种模式的另一个优点是可以并行地发送消息,这意味着可以在尝试检索响应前发送许多条消息。如果在应用程序尝试检索消息时响应还不可用,那么应用程序可以在晚些时候再次尝试。当应用程序请求响应时,它也可以设置一个完成响应的时限。如果超过时限还未检索到消息,该应用程序可以选择再次尝试。而对应用程序尝试检索响应的次数是没有限制的。在这种模式中,应用程序还可以检索针对一条消息的多个响应。
-
不接收响应。
这是一个数据报。发送方 bean 使用运行时来组装并发送消息,但不用任何消息检索支持。
在前面两种模式中,如果没有接收到期望的响应(在规定的时限内),应用程序可以请求扩展的消息传递将响应路由到一个用户提供的接收方 bean。欲了解更多的相关详情,请参阅
延迟响应处理。
接收方 bean
接收方 bean 不是根据消息驱动 bean 构建的,就是根据无状态会话 bean 构建的。接收方 bean 封装了消息的接收和对所接收消息的任何应答的发送。它们还可以处理从 JMS 消息到用来调用业务组件的参数的转换,以及从任何应答对象到 JMS 消息的转换。
接收方 bean 有两种类型,它们有着不同的调用语义和应答语义:
-
根据消息驱动 bean 构建的
这些接收方 bean 会在消息抵达目的地后被调用,这样会激活一个 JMS 消息侦听器。它们调用业务方法,这些方法传递从消息中派生出的参数。
-
根据无状态会话 bean 构建的
这些接收方 bean(也称为应用程序可调用接收方 bean)是由调用接收方 bean 中的方法的应用程序 bean 来调用的。它们将一个被称为逻辑消息格式(Logical Message Format,LMF)的对象返回给应用程序。应用程序 bean 使用这个对象(它包含经过数据编出的消息)的 getter 方法检索数据内容。
接收方 bean 支持以下三种交互模式:
-
发送一个同步应答。
在这种模式中,先对所接收到的消息进行数据编出,然后用它来调用一个业务组件以传递从经过数据编出的消息中抽出的参数。此外,接收方 bean 可以将下游业务方法的结果转换为一条 JMS 消息并把它作为应答来发送。在这种情况下,应答的目的地是由原始消息在 JMS 消息头的
JMSReplyTo 域中定义的。这种模式适用于根据消息驱动 bean 构建的接收方 bean。
-
发送一个异步的应答。
在这种模式中,消息经过接收、数据编出后被发往用户应用程序。该应用程序可以通过调用一个已生成的 replySender 方法来发送一个应答,这个方法的功能是将自身的参数映射为一条 JMS 消息。消息随后被发往在请求消息或输入端口中定义的应答目的地。使用这种技术,应用程序可以在应答一个消息请求时发送多条消息。这种模式适用于根据无状态会话 bean 构建的接收方 bean。
-
不发送应答。
这种模式利用了数据编出和方法调用函数,但不使用任何应答处理能力。这种模式对上述两种类型的接收方 bean 都适用。
样本案例
为了演示应用程序和消息 bean 之间的关系,让我们看一个样本案例,尽管我们简化了这个案例,但它仍将有效地展示如何使各部分相互关联。
在这个案例中,用户需要了解其账户的余额。开发人员将为此构建两个部件:
- 客户端应用程序(通常是一个从客户端调用的 EJB)
- 后端访问应用程序。
这个案例的流程如下所示:
- 客户端应用程序接收到一个请求并执行一些本地处理。
- 客户端应用程序决定要将请求发送给后端应用程序,并发送之。所发送的请求通常可能包含一些账户细节,比如账号、账户类型、客户姓名等等。
- 后端系统收到来自客户端的请求,然后用它来执行账户查询并检索余额。
- 来自后端应用程序的响应被返回到客户端应用程序。
- 响应被返回给用户。
开发人员编写客户端应用程序和后端访问应用程序来实现这个案例。在开发人员决定用消息传递来连接两端后,他使用 Application Developer 工具来构建发送方 bean 和接收方 bean。
图 2 演示了样本案例的流程。客户机调用一个发送方 bean 来构建一个 JMS 消息并将它写入目的地队列 Q1。当一个消息侦听器检索到这个消息时,它就调用接收方 bean 来传送这个 JMS 消息。然后,接收方 bean 将解析这条 JMS 消息,并随后调用查询 EJB 中的 getBalance() 方法来获取余额。当控制权返回到接收方 bean 时,所返回的对象就被映射为一个 JMS 消息,然后应答被写入目的地队列 Q2。然后由客户端应用程序调用的发送方 bean 就会从目的地 Q2 中检索响应并将它返回给请求发送方。
图 2. 样本案例
前面已经提到过,在发送消息和检索响应时要注意两种交互模式:延迟响应模式和同步响应模式。下面将讲述如何将这两种模式分别应用到上述案例中:
延迟响应模式的流程:
- 应用程序 A 调用发送方 bean 中的一个方法(例如,getBalance),在这次调用中传递多个参数。
- 发送方 bean 将数据编组一条 JMS 消息,并在消息头的
replyTo 域中指定目的地 Q2(指出响应的目的地)。它随后将消息放入 Q1(目的地),然后返回到应用程序 A,并传回一个相关因子对象。
- 在接收端,JMS 提供者检测到有消息从目的地 Q1 发来,就调用接收方 bean 的一个实例,由它将 JMS 消息传送给 onMessage() 方法。
- 接收方 bean 对消息进行数据编出,然后调用应用程序 B 中的方法(例如,getBalance),并将消息中的对象传给它。
- 当应用程序 B 完成后,它返回到接收方 bean,同时传回一个对象。
- 接收方 bean 将返回的对象映射为 JMS 消息,然后将应答消息发送至目的地 Q2。
- 由于应用程序 A 使用的是延迟响应,使得它可以一直执行其他工作。为了获取响应,应用程序 A 调用发送方 bean 中的 retrieve() 方法,并将在第 2 步中返回的相关因子传给该方法。如果响应消息不可用,应用程序尽可以重复调用检索方法。
- 发送方 bean 对消息进行数据编出并将响应返回给应用程序 A。
同步模式的流程和延迟接收响应模式的不同,其流程如下:
- 应用程序 A 调用发送方 bean 中的方法(例如,getBalance),在这次调用中传递多个参数。然后应用程序就等待响应。
- 发送方 bean 将数据编成一条 JMS 消息,并在消息头的
replyTo 域中指定目的地 Q2(指出响应的目的地)。然后它将消息放入 Q1(目的地)。发送方 bean 既不返回到应用程序 A 也不传回相关因子对象,而是等待有响应被传来。
- 余下的第 3 步至第 8 步同上,但第 7 步不会发生。
在后面几部分中,我们会回头参考这个样本案例。
消息内容
作为其消息构建工作的一部分,扩展的消息传递会为您处理消息格式。在调用发送方 bean 或接收方 bean 的各方法时会传送多个对象。这些对象可以是任何类型,但必须是可序列化的。否则,将无法构造消息流。当您尝试以非可序列化对象为基础构造消息时会有一个错误异常被抛出。
JMS 定义的消息由一个头和一个主体组成,并且 JMS 提供五种形式的消息主体。扩展的消息传递为消息主体使用流消息(StreamMessage)形式,并通过依次添加所传送的对象来构造消息主体。生成的代码(在消息 bean 中)负责将构成消息的参数传递给消息格式化程序。您首先要序列化不属于 Java 基本类型的对象,然后在把这些对象添加到数据流之前将它们处理为字节数组。但这一点是不明显的,因为系统会处理消息格式化。在接收端,对消息进行数据编出并从中抽取所有的对象是相当简单的。然而,为了能够在两端间进行数据映射,扩展的消息传递要能够把两端的消息类型关联在一起。发送端和接收端的方法用一个消息格式标识符把消息及它所定义的数据内容关联在一起。这两端需要对消息内容有完全一致的理解。因为每个发送方 bean 和接收方 bean 都可能要处理多种消息格式,所以用
JMSType 头域来保存消息标识符。
尽管扩展的消息传递工具是被设计用来生成能够处理服务的两端的消息 bean,但用户可以在任意一端使用非 WebSphere 应用程序服务器。在这种情况下,应用程序开发人员可以把参数的缺省数据编组和数据编出覆盖到 JMS 消息中。
延迟响应处理
有时,发送方 bean 可能在规定的时间周期内无法收到响应。这些延迟的消息被称为
延迟响应。有多种原因可能导致发生延迟响应;可能是产生应答消息的应用程序太忙无法在规定的时间内作出应答,也可能是消息传递系统的基础结构中出现了故障等等。在这些情况下,需要响应的应用程序必须决定采用哪种操作来纠正错误。
WebSphere 扩展的消息传递这个工具可以帮助处理任何延迟响应。当响应消息终于被发送时,它可能会被路由到一个用户创建的消息驱动接收方 bean。
发送方 bean 的响应方式会影响到系统注册延迟响应请求的方式:
- 同步方式:如果在部署 bean 的同时启用了处理延迟消息的功能,系统就会自动地注册一个请求以将延迟响应路由到接收方 bean 中。
- 延迟方式:应用程序 bean 负责向发送方 bean 注册请求。在这种方式中,扩展的消息传递系统无法自动注册请求。这是因为应用程序自身可能会多次重试检索,而扩展的消息传递检测不出应用程序何时放弃了重试。应用程序 bean 通过调用发送方 bean 中的 registerLateResponse(Correlator) 方法来注册请求,该方法传递在发送调用中返回的相关因子。另一件需要考虑的事情是如果应用程序希望接收到多个响应又会发生什么情况。在这种情况下,应用程序需要为每一个它想要用扩展的消息传递来处理的尚未处理的响应注册一个请求。例如,如果应用程序希望检索到三个响应而实际上却只接收到一个,那么它会再调用注册方法两次。当然,这个前提是应用程序希望将这两个延迟的响应都路由到接收方 bean 中。
一旦注册了处理延迟响应的请求,扩展的消息传递系统就会侦听所要传递的消息,然后将这些消息传送给由用户开发的接收方 bean。扩展的消息传递如何得知要将延迟响应发送到哪个接收方 bean 呢?这样的链接是在部署发送方 bean 并指定侦听器端口时完成的。然后将侦听器端口关联到接收方 bean 或消息驱动 bean(如果部署了消息驱动 bean)。
对于延迟响应,我们要考虑的一个方面是,发出注册请求后又会发生什么?这个请求被用于通知侦听器函数将从指定的目的地检索哪条消息。起初是通过记录下注册请求来持久保存该请求以防万一系统重新启动或发生故障,然后侦听器开始侦听,等待响应消息出现。与此同时,注册过请求的应用程序可以继续执行一些其他工作。因此,很可能在某些情况下会导致接收方 bean 在最初的应用程序 bean 完成它的处理前已经先处理了消息。
事务
要求在全局事务中接收初始消息的消息驱动 bean 必须使用容器管理的事务(Container Managed Transaction,CMT)。类似地,由扩展的消息传递工具构造的消息 bean 也要使用 CMT。对消息 bean 使用 CMT 使得发送方 bean 和两种接收方 bean 都拥有相同的事务控制权。通过用这种方式设置 bean 方法的事务属性,应用程序部署人员就能够全权控制事务边界。
在使用事务时,很重要的一点是要牢记除非事务被提交,否则在事务范围内发送的任何消息都没有被实际发送。如果事务被回滚(而非提交),那么任何消息都会被废弃并不会被发送至目的地。在使用同步方式交互时,发送消息和检索响应发生在同一个发送方 bean 方法中。而如果该方法是事务的一部分就会出问题,因为在事务被提交前发送将无法完成。这意味着永远无法在这个事务中成功地接收到响应;这样就会导致响应超时,或者如果没有指定超时的话,情况还会更糟糕,将导致死锁发生。为了避免这种交互方式中可能出现的死锁,扩展的消息传递总是将消息发送强制为立即发送(sendImmediate);也就是说,发送不再是任何事务的组成部分并且能立即发生。检索响应是与方法相关联的事务的组成部分。这意味着一旦发生了发送,即使事务被回滚,发送也无法撤销;即无论如何消息都将被发送。
当在延迟响应模式下进行发送时,应该由应用程序部署人员负责通过确保发送方法和接收方法不是相同事务的组成部分来避免任何可能的死锁。例如,在部署完应用程序之后,部署人员可以通过 RequiresNew 的事务属性来指定接收方法。
样本代码
我们需要编写多少代码才能使应用程序能够使用扩展的消息传递呢?在一些情况下,只要少量代码就可以了。请考虑上面的样本应用程序,发送方 bean 和接收方 bean 是由 Application Developer 工具生成的。您需要添加一些代码来调用消息 bean 中的方法(在客户端 EJB 中)以处理我们的方法调用所返回的错误。(我们将在后面的
错误处理中讨论这个问题。)因为查询 EJB 没必要知道自己是被从接收方 bean 调用的,所以不需要添加特殊的代码。
让我们再来看看样本案例,先用同步消息模式,然后用延迟响应模式:
同步响应方式
在使用同步消息模式时,需要创建发往发送方 bean 的对象并设置它们的值。为此,我们将调用发送方 bean 中的 getBalance 方法,并传给它三个参数(客户名称、账户类型和账号),然后接收账户余额。这个代码段如清单 1 所示。
清单 1. 使用同步响应的样本案例
int accountBalance; // int that holds the result.
String custName = "Smith"; // String - parameter 1.
int accountType = 20; // int - parameter 2.
int accountNumber = 123456; // int - parameter 3.
// Create the initial context for looking up the Sender Bean.
Context initialContext = new javax.naming.InitialContext();
// Lookup the SenderBeanHome and cast it to the correct reference type.
SenderBeanHome home = (SenderBeanHome)javax.rmi.PortableRemoteObject.narrow(initialContext.lookup
("java:comp/env/ejb/ems/SenderBean"), SenderBeanHome.class);
// Create the SenderBean.
SenderBean sb = home.create();
// Lookup the result by calling the getBalance method on the SenderBean.
accountBalance = sb.getBalance(custName, accountType, accountNumber);
|
清单 1 中的代码将返回所请求账户的账户余额。这里不需要任何特殊的代码来适应扩展的消息传递。您可以用类似的代码进行任何 EJB 方法调用。
延迟响应方式
在使用延迟响应模式时,实际上是调用了两个方法:一个用于发送请求,而另一个用于检索响应。首先调用 requestBalance 方法,并将前面提到的三个参数传给它,然后在晚些时候,当我们想看看响应是否已传来时再调用 getBalance 方法。
清单 2. 使用延迟响应的样本案例
int accountBalance; // int that holds the result.
String custName = 揝mith_ // String - parameter 1.
int accountType = 20; // int - parameter 2.
int accountNumber = 123456; // int - parameter 3.
// For simplicity the code to look up the sender bean home has been omitted.
// Send the request by calling the senderBean requestBalance method.
CMMCorrelator corr = sb.requestBalance(custName, accountType, accountNumber);
// do some other work, until we are ready to retrieve the result
// Get the result, using the correlator returned from the request
accountBalance = sb.getBalance(corr);
|
在清单 2 中,我们为了解账户余额而发送了一个请求(requestBalance 方法调用),紧接着我们又发送了一个请求以获取结果(getBalance 方法调用)。requestBalance 方法的调用返回一个相关因子对象,随后我们会再次将它用于 getBalance 方法调用。这种模式提供的好处是可以在发送请求和检索响应的间隙执行其他不相关的处理。
如同先前所讨论的一样,在发出请求后如果没有响应,我们可以通过将消息路由到一个接收方 bean 中以请求在晚些时候处理响应。如果要用延迟消息传递模式来实现这一点,请使用下列代码将相关因子对象传给 registerLateResponse 方法:
清单 3. 将消息路由到接收方 bean 中
// Register request for the system to handle the late response
sb.registerLateResponse(corr) ;
|
错误处理
在使用扩展的消息传递时需要考虑两种错误情况:
- 处理返回给应用程序的异常
- 当事务被回滚时处理恢复。
如果扩展的消息传递运行时在处理消息时遇到问题(比如说配置错误或 JMS 通信错误,又或者是数据格式化问题等等),它将抛出一个 CMMException。在我们的样本应用程序中调用发送方 bean 的地方,我们通常应该在一个 try-catch 条件块中调用它。清单 4 展示了如何处理一个 CMMException。为了简单起见,我们省略了对其他异常的处理。
清单 4. 处理 CMMException
try
{
....
accountBalance = sb.getBalance(custName, accountType, accountNumber);
}
catch (....)
{
...
}
catch (CMMException cmme)
{
// Create message if an error occurs during Extended Messaging transit.
result = "Error occurred in getBalance\r\n";
result += "Exception message is: " + cmme + "\r\n";
Exception e = cmme.getLinkedException();
if (e != null)
result += "Extended Messaging Linked Exception is: " + e + "\r\n";
if (e instanceof JMSException)
{
JMSException jmse = (JMSException)e;
msg += "JMS Linked Exception:" + jmse.getLinkedException() + "\r\n";
}
}
|
应用程序随后将记录下包含错误信息的结果字符串,并用它把一个适当的错误报告给客户端应用程序。
在多数情况下可以应用标准方法来纠正错误,因为用户应用程序通常是处于我们的控制之下。例如,回滚现有的事务并重试原来的操作也可能会成功。如果一次重试失败,应用程序可以决定重试多少次之后再报告一个永久性错误并停止。
由于消息驱动 bean 的调用是由系统而非应用程序控制的,对于根据消息驱动 bean 构建的接收方 bean 还要考虑另一种情况。此时,如果事务被回滚,消息被返回给队列,则系统会通过调用该消息驱动 bean 的另一个实例来重试原来的操作。为了排除潜在的无限循环,我们可以为侦听器端口指定一个重试次数,一旦计数超过了这个指定的次数,就会停止传递消息直至该侦听器端口被重新启动。一旦到达一个重试阈值,一些 JMS 提供者能够将消息重定向到一个死信队列。
开发方案
在研究构建发送方 bean 或接收方 bean 的步骤前,我们需要考虑有关应用程序设计的一些方面。一旦构建了应用程序,我们还需要看一下 WebSphere Application Server 中所需的运行时定义和资源。
尽管您可以以任何顺序来开发这些 bean,但一般来说,首先创建应用程序 bean,然后创建发送方 bean,最后创建接收方 bean 的顺序可以使开发变得更容易。如果接收方 bean 是根据消息驱动 bean 构建的,就需要在构建了目标应用程序 bean 之后构建它,因为它要使用应用程序 bean 的方法说明来定义数据映射。
应用程序 bean
在设计应用程序 bean 时,我们需要考虑它们如何与自身使用的消息 bean 进行交互。所使用的应答/响应模式的类型,以及是选择使用数据映射还是使用非数据映射都会对应用程序 bean 和消息 bean 的设计产生重大影响。
我们将考虑三种交互方式:
-
由基于消息驱动 bean 的接收方 bean 驱动的应用程序
在这种方式中,应用程序 bean 一般是与消息传递层隔开的并且不知道消息传递正在驱动应用程序。简单地说,您可以用与定义任意两个交互的 EJB 相同的方式来定义接收方 bean 和应用程序之间的接口。
-
通过发送方 bean 发送请求的应用程序
按照前面的样本,我们可以用交互的同步方式或延迟方式来发送请求。同步方式一般比较简单一些,因为发送请求和检索响应它只需要一次调用。然而,如果能在需要响应前处理完其他工作,那么先发送请求然后再查找响应这种方式就会更实用一些。如果请求需要多个响应,就可以使用这种方式。
-
请求来自于根据无状态会话 bean 构建的接收方 bean 的输入的应用程序
此时,应用程序需要完全了解其检索的内容。我们在前面已经看到过,消息的内容被映射为要返回给应用程序的逻辑消息格式(LMF)。然后应用程序就可以使用被返回的对象的 getter 方法来抽取相关的属性。如果应用程序只对一个属性感兴趣,那它可以直接检索该属性的值而不必使用 LMF 对象。
发送方 Bean
在设计一个发送方 bean 时第一要考虑的是应用程序所用的交互的形式。第二,还要考虑需要哪种形式的数据映射?应用程序可以在发送消息后检索响应前继续处理吗?或者应用程序需要等待响应到达吗?另外,应用程序也可以不期望任何响应。当应用程序希望有响应时,我们需要决定如果响应不可用的话要调用什么行为。这是一个应用程序将要处理的错误吗?或者是否需要扩展的消息传递支持来处理延迟响应?最后,在设计发送方 bean 的接口时,请牢记要传送的任何对象都必须经过序列化,否则在映射消息时将发生错误。
接收方 bean
所使用的接收方 bean 的类型是由应用程序需求规定的:
- 由消息调用的 bean 是否被传递(使用一个消息驱动 bean 构造),或者
- 应用程序是否请求消息(使用一个无状态会话 bean)?
消息驱动 bean 通常是用起来最有效的类型,因为一旦消息可用,系统就将调用一个实例。
对于接收方 bean 来说,我们还需要考虑是否需要发送应答。根据所使用的接收方 bean 类型的不同,交互也是不同的。对于根据消息驱动 bean 构建的接收方 bean 来说,应答是从由用户应用程序返回的对象映射为 JMS 消息。对于根据无状态会话 bean 构造的接收方 bean 来说,应答是异步的,并且是从传送的对象映射到接收方 bean 中构造的应答(reply)/发送程序(sender)方法。在这两种情况下,映射都是由接收方 bean 中的方法处理的。
消息 bean
消息 bean 是用 Application Developer 工具创建的。扩展的消息传递提供了许多向导来简化 bean 的创建。您可以用这些向导来创建新的 bean、将现有的 EJB 转换为消息 bean 或更新一个现有的消息 bean。一个要被转换为发送方 bean 或接收方 bean 的 EJB 必须是一个无状态会话 bean 或消息驱动 bean。可以单独创建发送方 bean 或接收方 bean,或者也可以用一个组合向导一步到位地同时创建发送方 bean 和接收方 bean。
运行向导将:
- 创建发送方 bean 或接收方 bean 的代码。
- 创建对任何输入端口或输出端口的资源引用。
- 为消息驱动 bean 创建 ejb 引用。
- 创建帮助简化应用程序安装的 WebSphere 绑定(可选)。
应用程序部署描述符的 IBM 扩展
除了标准的 EJB 部署描述符之外,扩展的消息传递还有一些其他的扩展。如果消息 bean 是用应用程序部署描述符(Application Deployment Descriptor)编辑器开发的,您就可以添加这些扩展,或者您也可以用应用程序装配工具(Application Assembly Tool,AAT)来添加它们。
对于发送方 bean 来说,这些属性包括:
-
响应超时- 设置这个值可以覆盖最初构造发送方 bean 时指定的值。
-
处理延迟响应- 启用这个选项是指定扩展的消息传递应该处理任何已注册的延迟响应。如果要生成发送方 bean 来注册延迟响应,这些发送方 bean 一般都希望这个值得到设置。如果启用了延迟响应选项,就可以指定相关的侦听器端口。
对于应用程序可调用接收方 bean(那些基于无状态会话 bean 的 bean)来说,只有一个扩展的属性:
-
消息选择器 - 它使得我们能够通过指定选择接收方 bean 要检索的消息的标准来过滤消息。该选择是基于 JMS 的消息头和属性域。例如,
JMSType = 'car'
AND color = 'red' 。该选择器使用和消息驱动 bean 相同的格式。
对于基于消息驱动的接收方 bean 来说,除了那些通常用于消息驱动 bean 的属性以外,没有其他的属性。
已部署的消息 bean 使用的资源
扩展的消息传递要用哪些资源?除了基本的 JMS 资源(比如说 JMS 目的地和连接工厂),还有两个主要的资源:发送方 bean 的输出端口,以及应用程序可调用的接收方 bean 的输入端口。此外,任何打算使用延迟响应处理的应用程序都必须有与之相关联的专用侦听器端口。
您可以用 WebSphere Application Server 系统管理控制台或批处理 WSAdmin 命令来定义这些资源。在管理控制台中,输入端口和输出端口是在位于 Resources 下的扩展的消息传递提供者下定义的。
输入端口
输入端口是由根据无状态会话 bean 构造的接收方 bean 所使用的。输入端口的目的是用来定义 JMS 资源及其他属性。根据消息驱动 bean 构建的接收方 bean 不需要输入端口,因为在部署它们的同时已经将它们和一个侦听器端口关联在一起了。
输入端口所需的属性包括连接工厂的 JNDI 名称以及用来接收消息的目的地。除此之外还需要定义其他属性(比如说目的地类型或预订持久性)。如果将一个应答发送给消息,就要用到消息的
JMSReplyTo 信息,但您可以通过在输入端口中指定连接工厂和应答的目的地来覆盖这个信息。
在样本案例中,因为接收方 bean 是作为使用侦听器端口的消息驱动 bean 来部署的,所以不需要输入端口。
输出端口
就像接收方 bean 和输入端口相关联一样,发送方 bean 要和输出端口相关联。输出端口也用于定义 JMS 资源以及其他属性。它需要连接工厂的 JNDI 名称和用于发送消息的目的地。它还可以定义目的地类型和用于处理 JMS 消息的其他属性。我们还可以在输出端口中指定多个目的地。如果指定了多个端口,消息就会被发往每个端口。
如果期望得到响应,那么在配置输出端口时就要多考虑些问题。如果应用程序使用的是延迟响应模式,或者如果响应能够被作为延迟响应处理,那就需要用于检索响应的连接工厂和目的地名称。对于同步响应模式来说,工厂和目的地是可选的;如果没有指定它们,就使用一个临时队列。
侦听器端口
侦听器端口通常被用于将 JMS 资源和已部署的消息驱动 bean 与将处理传入消息的侦听器服务关联在一起。为了支持延迟响应处理,扩展的消息传递用一个专用的侦听器端口来检索消息。当启用这个端口来处理延迟响应时,还可以指定额外的属性来控制等待响应时的检查周期和超时时间。
您可以通过系统管理控制台来指定这些属性,方法是选择
Manage Application Server => Application Server => Extended Messaging
Service => Listener Port Extensions,然后为侦听器端口添加一个新的条目。
安装应用程序
在我们相继完成了消息 bean 的设计与构建、应用程序的组装以及所需资源的定义之后,最后一步就是安装应用程序。为此,您可以遵循标准的应用程序服务器安装步骤。
结束语
在本文中,我们研究了如何用 WebSphere Application Server 中的扩展的消息传递支持来简化应用程序。我们讨论了消息传递模式和可用的运行时工具,并通过研究一个带有编码示例的样本案例展示了使用扩展的消息传递支持给应用程序带来的好处。
关于作者  | |  |
Vernon Green是一位从事 WebSphere Application Server 开发的 IBM 高级软件工程师,他所在的公司位于英国的赫斯利(Hursley)。Vernon 是负责开发应用程序服务器的功能的架构师和开发人员,而且他在为分布式环境开发中间件软件方面有着丰富的经验。您可以通过
vernon_green@uk.ibm.com和 Vern 联系。
|
对本文的评价
|