| | | |
Sun ONE Application Server 7 Enterprise Java Beans 技术开发者指南 |
使用消息驱动型 Bean本节介绍消息驱动型 Bean,并解释在 Sun ONE Application Server 7 环境中创建消息驱动型 Bean 的要求。
注意 如果不熟悉消息驱动型 Bean 或 EJB 技术,请参阅 Java 软件教程:
关于消息驱动型 Bean 的详细信息,请参阅 Enterprise JavaBeans Specification 2.0 第 15 和 16 章中。
关于 Sun ONE 应用服务器的概述资料,请参阅“Sun ONE 应用服务器 Enterprise JavaBeans 技术介绍”和 Sun ONE 应用服务器产品介绍。
本节包含以下主题:
关于消息驱动型 Bean
消息驱动型 Bean 是一种使 J2EE 应用程序可以异步处理消息的 Enterprise Bean。消息驱动型 Bean 作为消息侦听器 - 除了消息驱动型 Bean 接收消息而不是事件之外,这与事件侦听器相似。可以通过任何 J2EE 组件(应用程序客户端、另一个 Enterprise Bean 或 Web 组件)或通过不使用 J2EE 的应用程序或系统发送消息。
本节介绍以下主题:
消息驱动型 Bean 的区别
会话 Bean 和实体 Bean 使您可以发送 JMS 消息并同步接收 JMS 消息,而不是异步方式。为避免占用服务器资源,您可能希望在服务器端组件中使用异步接收。要异步接收消息,就使用消息驱动型 Bean。
消息驱动型 Bean 与会话和实体 Bean 之间最明显的区别就是,客户端不通过接口访问消息驱动型 Bean。与会话 Bean 或实体 Bean 不同的是,消息驱动型 Bean 只有一个 Bean 类。
在多个方面,消息驱动型 Bean 与无状态会话 Bean 相似:
- 消息驱动型 Bean 的实例不对某个客户端保留任何数据或会话状态。
- 消息驱动型 Bean 的所有实例都相等,因而使容器可以Pooling这些消息驱动型 Bean 实例。这样,就可以并行处理消息流。
- 单个消息驱动型 Bean 可以处理来自多个客户端处理消息。
消息驱动型 Bean 实例的实例变量可以在客户端消息处理之间包含某种状态,例如,JMS 连接、开放的数据库连接或 EJB 对象的对象引用。
消息驱动型 Bean 的特征
消息驱动型 Bean 实例是一个消息驱动型 Bean 类的实例。它没有本地接口,也没有远程接口;消息驱动型 Bean 为匿名。消息驱动型 Bean 没有客户端可以看到的标识。
客户端通过将消息发送到消息目的地(其消息驱动型 Bean 类为 MessageListener),并通过 JMS,访问消息驱动型 Bean。部署期间,使用 Sun ONE 应用服务器资源,分配消息驱动型 Bean 的 Queue(队列)和 Topic(主题)。
消息驱动型 Bean 具有以下特征:
事务管理
支持 Enterprise JavaBeans Specification 2.0 中定义的容器管理事务和 Bean 管理事务。
通过容器管理事务,可以把消息提交给事务上下文内的消息驱动型 Bean,这样,onMessage 方法内的所有操作就是单个事务的组成部分。如果回滚消息处理,就重新提交消息。
有关事务的其他信息,请参阅“使用 Enterprise Bean 处理事务”。
并行消息处理
容器允许消息驱动型 Bean 类的多个实例同时运行,因而可以并行处理消息流。无法保证向消息驱动型 Bean 类提交消息的精确顺序,尽管在此不影响消息处理的并行性时,容器尝试以时间顺序提交消息。
因此,应准备消息驱动型 Bean,以便处理不在顺序内的消息。例如,在一个进行保留的消息到达之前,有可能先提交取消保留的消息。
并行消息驱动型 Bean
消息驱动型 Bean 模型的目标是使开发为处理传入消息而异步调用的 Enterprise Bean 像用任何其他 JMS 侦听器开发同一功能一样简单。进一步的目标是通过对消息驱动型 Bean 实例进行提供容器的Pooling,实现同时处理消息流。
下面几节提供创建消息驱动型 Bean 的准则:
创建 Bean 类定义
与会话和实体 Bean 不同的是,消息驱动型 Bean 没有定义客户端访问的远程接口或本地接口。客户端组件不查找消息驱动型 Bean,而且不直接在其上面调用方法。
尽管消息驱动型 Bean 没有业务方法,但可能包含 onMessage 方法内部调用的帮助器方法。
对于消息驱动型 Bean,类要求为:
- 类必须直接或间接地实施 javax.ejb.MessageDrivenBean 接口。
- 类必须直接或间接地实施 javax.ejb.MessageListener 接口。
- 必须把类定义为 public(公用),而不得定义为 abstract(抽象)或 final(最终)。
- 类必须具有一个公用构造函数,该构造函数不需要任何参数(容器用以创建消息驱动型 Bean 类的实例)。
- 类不得定义 finalize 方法。
- 类必须实施 onMessage 方法。
- 类必须实施一个 ejbCreate 方法,不包含任何参数。
- 类必须实施一个不包含任何参数的 ejbRemove 方法。
以下各节介绍消息驱动型 Bean 的类定义中的各种方法。
使用 ejbCreate
消息驱动型 Bean 类定义一个 ejbCreate 方法,其签名必须遵循以下规则:
- 方法名称必须为 ejbCreate。
- 必须把方法声明为 public,而不得声明为 final 或 static。
- 返回类型必须为 void。
- 方法不能包含任何参数。
- throws 子句不得定义任何应用程序例外。
使用 setMessageDrivenContext
容器提供包含 MesssageDrivenContext 的消息驱动型 Bean 实例。这使消息驱动型 Bean 实例可以访问该实例的由容器维护的上下文。
使用 onMessage
onMessage 方法只有一个参数:传入消息。
当某个消息到达要服务的 Bean 时,Bean 的容器会调用 onMessage 方法。此方法包含进行消息处理的业务逻辑。分析消息和执行必需业务逻辑是消息驱动型 Bean 的责任。
消息驱动型 Bean 类定义一个 onMessage 方法,其签名必需遵循以下规则:
- 必须把方法声明为 public,而不得声明为 final 或 static。
- 返回类型必须为 void。
- 方法必须具有一个类型为 javax.jms.Message 的参数。
- throws 子句不得定义任何应用程序异常。有关从 onMessage 产生异常的语义,请参阅“onMessage 运行时异常”。
onMessage 方法在事务范围内调用,该范围由部署描述符内定义的事务属性确定。
注意 如果把 Bean 指定为使用容器管理事务分界,就必须在部署描述符中指定 Required 或 NotSupport 事务属性。
使用 ejbRemove
消息驱动型 Bean 类定义一个 ejbRemove 方法,用以释放不再需要的 Bean。签名必须遵循以下规则:
- 方法名必须为 ejbRemove。
- 必须把方法声明为 public,而不得声明为 final 或 static。
- 返回类型必须为 void。
- 方法不能包含任何参数。
- throws 子句不得定义任何应用程序异常。
注意 不能假设容器会始终在消息驱动型 Bean 实例上调用 ejbRemove 方法。
如果 EJB 容器发生崩溃,或者由实例的 onMessage 方法向容器产生异常,就不调用 ejbRemove 方法。消息驱动型实例在 ejbCreate 方法和/或方法中分配资源,并在 ejbRemove 方法中释放资源。这些资源不会自动被释放。您的应用程序应提供一种定期清除未释放资源的机制。
配置
本节介绍以下配置主题:
连接工厂和目标
消息驱动型 Bean 是一个 JMS 客户端。因此,消息驱动型 Bean 容器使用集成到 Sun ONE 应用服务器之中的 JMS 服务。JMS 客户端使用 JMS 连接工厂和目标的管理对象。JMS 连接工厂管理对象是一种资源管理器连接工厂对象,该对象用于创建与 JMS 提供商的连接。
sun-ejb-jar.xml 文件中用于消息驱动型 Bean 的 mdb-connection-factory 元素可用于指定连接工厂,容器使用连接工厂创建容器与 JMS 提供商的连接。此元素可用于与第三方 JMS 提供商工作。
如果不指定 mdb-connection-factory 元素,就使用服务器启动时创建的默认元素。这样,就可以使用 Sun ONE 消息队列的默认用户名称/密码(资源委托者),连接到内置 Sun ONE 消息队列中介程序,该中介程序位于 server.xml 文件中 jms-service 元素(如果启用的话)中指定的端口上。有关详细信息,请参阅 Sun ONE 消息队列开发者指南。
sun-ejb-jar.xml 文件中 ejb 元素的 jndi-name 元素指定与消息驱动型 Bean 关联的 JMS 队列或主题目标的被管理对象的 JNDI 名称。
消息驱动型 Bean 池
容器管理一个消息驱动型 Bean 的池,这些消息驱动型 Bean 用于同时处理一个消息流。Sun ONE 应用服务器特有的 Bean 部署描述符包含定义池(即 bean-pool 元素)的元素。
有关这些元素的信息,请参阅“Pooling和Caching元素”。
服务器实例范围内的属性
管理员可以控制以下服务器实例范围内的消息驱动型 Bean 属性,它们用于 server.xml 文件中的 mdb-container 元素。
- steady-pool-size
- pool-resize-quantity
- max-pool-size
- idle-timeout-in-seconds
- log-level
- monitoring-enabled
有关这些属性的进一步说明,请参阅“Pooling和caching元素”和 Sun ONE 应用服务器管理员配置文件参考。
有关监控消息驱动型 Bean 的信息,请参阅 Sun ONE 应用服务器管理界面联机帮助和管理员指南。
注意 不需要时运行监视功能可能会影响性能,因此,不使用监视功能时,可使用 asadmin 命令或管理界面管理监视功能。
自动重新连接到 JMS 提供者
为每个部署的消息驱动型 Bean 启动 Sun ONE 应用服务器时,其容器保持与 JMS 提供者的连接。连接中断时,容器就不能够从 JMS 接收消息,因此,无法将消息提交到其消息驱动型 Bean 实例。启用自动重新连接功能时,如果连接中断,容器会自动尝试重新连接到 JMS 提供者。
server.xml 文件中的 mdb-container 元素包含自动重新连接属性。默认情况下,将 reconnect-enabled 设置为 True(真),而将 reconnect-delay-in-seconds 设置为 60 秒。也就是说,每次尝试重新连接之前,而且把 reconnect-max-retries 设置为 60 秒时,有 60 秒钟延迟。
容器记录每次重新连接尝试的消息。
注意 根据所处消息处理阶段,如果连接中断,onMessage 方法可能无法成功完成,或者由于 JMS 异常而回滚事务。当容器重新建立与 JMS 提供者的连接时,JMS 消息重新提交请求。
有关 server.xml 文件中 mdb-container 元素的自动重新连接属性的信息,请参阅 Sun ONE 应用服务器管理员配置文件参考。
限制和优化
本节讨论以下限制和性能优化,开发消息驱动型 Bean 时应该能够感觉到这些限制和优化:
JMS 的局限性
Sun ONE 应用服务器通过 Sun ONE Message Queue 3.0.1 平台版提供的一个内置 JMS 服务支持 JMS 消息提交。作为一个独立的产品,Sun ONE Message Queue 3.0.1 支持 JMS 1.1 规范。但是,Sun ONE Application Server 7 支持 J2EE 1.3 规范,而后者仅包含更为有限的 JMS 1.02b 规范。因此,Sun ONE Application Server 7 上运行的应用不可以使用 JMS 1.1 中的新增特性。
因此,JMS 消息提交应用的开发者应该把 Sun ONE 应用服务器环境中运行的 JMS 客户端组件限制在 JMS 1.02b。有关详细信息,请参阅 Sun ONE 消息队列开发者指南或发行说明。
池调整和监视
消息驱动型 Bean 池也是一个线程池,池中的每个消息驱动型 Bean 实例都与一个服务器会话关联,而每个服务器会话又与一个线程关联。因此,池大小越大,线程数量越多,从而会影响性能和服务器资源。
配置消息驱动型 Bean 池属性时,必须考虑以下诸多因素:消息到达率和模式、onMessage 方法处理时间、总体服务器资源(线程、内存等)以及消息驱动型 Bean 可能访问的其他资源的任何并发性要求和局限性。
进行性能和资源利用调整时,还应考虑连接工厂的潜在 JMS 提供者属性,该连接工厂由容器(部署描述符中的 mdb-connection-factory 元素)。例如,当消息传入速率远远超过 max-pool-size 所能处理的速率时,就应调整连接工厂的 Sun ONE 消息队列流控制相关属性。
有关如何获取消息驱动型 Bean 池统计数据的信息,请参阅 Sun ONE 应用服务器管理员指南。
onMessage 运行时异常
就像其他行为良好的 JMS MessageListeners 一样,一般情况下,消息驱动型 Bean 不应产生运行时异常。如果一个消息驱动型 Bean 的 onMessage 方法遇到系统级异常或阻碍方法成功完成的错误,Enterprise JavaBeans Specification 2.0 提供以下准则:
- 如果 Bean 方法遇到运行时异常或错误,该 Bean 方法应直接将错误从 Bean 方法传送到容器。
- 如果 Bean 方法执行一项导致 Bean 方法无法恢复的受阻异常的操作,Bean 方法应产生包装原始异常的 javax.ejb.EJBException。
- 应使用(javax.ejb.EJBException 是 java.lang.RuntimeException 的一个子类)报告任何其他意外错误情况。
在容器管理事务分界情况下,从消息驱动型 Bean 的 onMessage 方法接收到运行时异常时,容器将回滚启动容器的事务,并且 JMS 消息会被重新提交。这是因为消息提交本身就是启动容器的事务的组成部分。默认情况下,从一个消息驱动型 Bean 实例的 onMessage 方法中接收到第一个运行时异常时,Sun ONE 应用服务器容器结束容器与 JMS 提供者的连接。这就避免了潜在消息重新提交循环,并且在消息驱动型 Bean 的 onMessage 方法的行为继续不正常时,可保护服务器资源。可以使用 server.xml 文件中的 mdb-container 元素的 cmt-max-runtime-exceptions 属性,更改默认容器行为。
cmt-max-runtime-exceptions 属性指定容器结束容器与 JMS 提供者的连接之前,允许消息驱动型 Bean 的 onMessage 方法发生的运行时异常的最大数目。默认情况下,此值为 1;-1 会禁用此容器保护。
消息驱动型 Bean 的 onMessage 方法可以使用 javax.jms.Message getJMSRedelivered 方法检查接收到的消息是否是重新提交的消息。
注意 将来可能会降低 cmt-max-runtime-exceptions 属性的价值。
示例消息驱动型 Bean XML 文件
本节包含介绍以下示例文件:
有关与消息驱动型 Bean 关联的元素的信息,请参阅“sun-ejb-jar.xml 文件中的元素”以及 Sun ONE 应用服务器开发者指南。
示例 ejb-jar.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
<enterprise-beans>
<message-driven>
<ejb-name>MessageBean</ejb-name>
<ejb-class>samples.mdb.ejb.MessageBean</ejb-class>
<transaction-type>Container</transaction-type>
<message-driven-destination>
<destination-type>javax.jms.Queue</destination-type>
</message-driven-destination>
<resource-ref>
<res-ref-name>jms/QueueConnectionFactory</res-ref-name>
<res-type>javax.jms.QueueConnectionFactory</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</message-driven>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>MessageBean</ejb-name>
<method-intf>Bean</method-intf>
<method-name>onMessage</method-name>
<method-params>
<method-param>javax.jms.Message</method-param>
</method-params>
</method>
<trans-attribute>NotSupported</trans-attribute>
</container-transaction>
</assembly-descriptor
</ejb-jar>示例 sun-ejb-jar.xml 文件
有关这些元素的信息,请参阅“sun-ejb-jar.xml 文件中的元素”
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Sun ONE Application Server 7.0 EJB 2.0//EN' 'http://www.sun.com/software/sunone/appserver/dtds/sun-ejb-jar_2_0-0.dtd'>
<sun-ejb-jar>
<enterprise-beans>
<ejb>
<ejb-name>MessageBean</ejb-name>
<jndi-name>jms/sample/Queue</jndi-name>
<resource-ref>
<res-ref-name>jms/QueueConnectionFactory</res-ref-name>
<jndi-name>jms/sample/QueueConnectionFactory</jndi-name>
<default-resource-principal>
<name>guest</name>
<password>guest</password>
</default-resource-principal>
</resource-ref>
<mdb-connection-factory>
<jndi-name>jms/sample/QueueConnectionFactory</jndi-name>
<default-resource-principal>
<name>guest</name>
<password>guest</password>
</default-resource-principal>
</mdb-connection-factory>
</ejb>
</enterprise-beans>
</sun-ejb-jar>
| | |