Message Queue 4.0 包括以下新功能:
这些功能将在以下各部分中进行描述。
在版本 4.0 中进行了一些比较细微但可能会引起问题的更改,其中之一就是不再使用命令行选项来指定密码。此后,必须将所有密码都存储在文件中,如过时的密码选项中所述。
Message Queue 4.0 版中增加了两个新属性,可在已放到停用消息队列中的所有消息上设置这些属性。
JMS_SUN_DMQ_PRODUCING_BROKER 指示生成消息时所在的代理。
JMS_SUN_DMQ_DEAD_BROKER 指示将消息标记为停用的代理。
Message Queue 4.0 版中增加了两个新属性,可在已放到停用消息队列中的所有消息上设置这些属性。
JMS_SUN_DMQ_PRODUCING_BROKER 指示生成消息时所在的代理。
JMS_SUN_DMQ_DEAD_BROKER 指示将消息标记为停用的代理。
已将 query 子命令添加到 imqdbmgr 命令。使用此子命令可显示有关持久性存储库的信息,包括存储库版本、数据库用户以及是否已创建数据库表。
以下是此命令所显示的信息的示例。
imqdbmgr query |
[04/Oct/2005:15:30:20 PDT] Using plugged-in persistent store: version=400 brokerid=Mozart1756 database connection url=jdbc:oracle:thin:@Xhome:1521:mqdb database user=scott Running in standalone mode. Database tables have already been created. |
为了提高性能,Message Queue 3.7 UR1 对持久性存储库格式进行了两处更改。一处是对文件存储库的更改,另一处是对 JDBC 存储库的更改。
文件存储库中保留的事务数据的格式
已经对事务状态信息(存储在 Message Queue 的基于文件的持久性存储库中)的格式进行了更改,以减少磁盘 I/O 和提高 JMS 事务性能。
Oracle JDBC 存储库
在 Message Queue 的以前版本中,适用于 Oracle 的存储库结构使用 LONG RAW 数据类型来存储消息数据。在 Oracle 8 中,Oracle 引入了 BLOB 数据类型,而不再使用 LONG RAW 类型。Message Queue 3.7 UR1 已转为使用 BLOB 数据类型,以提高性能和提供更多支持。
由于这些更改会影响存储库兼容性,因此已将 Message Queue 3.7 UR1 中文件存储库和 JDBC 存储库的版本从 350 更改为 370。
Message Queue 4.0 对 JDBC 存储库进行了一些更改,以便实现优化和支持未来的增强功能。因此,JDBC 存储库版本已提高到 400。请注意,在 4.0 版中,基于文件的持久性存储库版本仍保持为 370,因为未对此版本进行任何更改。
Message Queue 4.0 支持将持久性存储库自动转换为基于文件的持久性存储库和 JDBC 持久性存储库的最新版本。imqbrokerd 首次启动时,如果实用程序检测到早期版本的存储库,则会将存储库迁移到新格式,并留下早期的存储库。
基于文件的存储库版本 200 和 350 将迁移到 370 版本格式。
JDBC 存储库的 350 和 370 版本将被迁移到 400 版本格式。(如果需要升级 200 版本的存储库,需要先逐步升级到 3.5 或 3. 6 中间版本。)
如果需要回滚此升级,可以先卸载 Message Queue 4.0,然后重新安装以前运行的版本。由于未对存储库的早期副本进行任何更改,因此代理可以运行存储库的早期副本。
命令实用程序 (imqcmd) 添加了一个子命令和几个选项,管理员可通过它们停止代理、在指定时间间隔后关闭代理、 销毁连接或设置 java 系统属性 (例如,与连接相关的属性)
停止代理可使其进入静默状态,以便在关闭或重新启动代理之前处理完所有消息。无法对处于停止状态的代理创建新连接。要停止代理,请输入如下命令。
imqcmd quiesce bkr -b Wolfgang:1756
要在指定的时间间隔后关闭代理,请输入如下命令。(时间间隔指定了在代理关闭之前等待的秒数。)
imqcmd shutdown bkr -b Hastings:1066 -time 90
如果指定了时间间隔,代理将记录一条消息,指出何时关闭代理。例如,
Shutting down the broker in 29 seconds (29996 milliseconds)
当代理等待关闭时,可通过以下方式影响其行为。
继续接受管理 jms 连接。
不接受任何新 jms 连接。
现有 jms 连接将继续工作。
代理无法接管高可用性群集中的任何其他代理。
imqcmd 实用程序不会阻止,它将向代理发送关闭请求并立即返回。
要销毁连接,请输入如下命令。
imqcmd destroy cxn -n 2691475382197166336
使用 imqcmd list cxn 或 imqcmd query cxn 命令可获取连接 ID。
要使用 imqcmd 设置系统属性,请使用新的 –D 选项。它适用于设置或覆盖 JMS 连接工厂属性或与连接相关的 java 系统属性。例如:
imqcmd list svc -secure -DimqSSLIsHostTrusted=true imqcmd list svc -secure -Djavax.net.ssl.trustStore=/tmp/mytruststore -Djavax.net.ssl.trustStorePassword=mytrustword
有关 imqcmd 命令语法的完整信息,请参见《Sun Java System Message Queue 4.1 Administration Guide》中的第 13 章 “Command Line Reference”。
目前可以将 Apache Derby 版本 10.1.1 作为符合 JDBC 的持久性存储库提供程序。
从 4.0 发行版开始,客户端连接工厂属性 imqSSLIsHostTrusted 的默认值为 false。如果应用程序依赖于以前的默认值 true,则需要对此属性进行重新配置,并将其明确设置为 true。
将代理配置为使用自签名证书时,您可以选择信任主机。在这种情况下,除了指定连接应使用基于 SSL 的连接服务(通过 imqConnectionType 属性)外,还应该将 imqSSLIsHostTrusted 属性设置为 true。
例如,要在代理使用自签名证书时安全地运行客户端应用程序,请使用如下命令。
java -DimqConnectionType=TLS -DimqSSLIsHostTrusted=true <ClientAppName>
要在代理使用自签名证书时安全地运行管理工具 imqcmd,请使用如下命令。
imqcmd list svc -secure -DimqSSLIsHostTrusted=true
已添加符合 Java Management Extension (JMX) 规范的新 API,用于配置和监视 Message Queue 代理。使用此 API,可以在 Message Queue 客户端应用程序中以编程方式来配置和监视代理函数。在 Message Queue 的早期版本中,只能从命令行或管理控制台访问这些函数。
此 API 由一组 JMX 受管理 Bean (MBean) 组成,用于管理以下与 Message Queue– 相关的资源:
消息代理
连接服务
连接
目的地
消息生成方
消息使用方
事务
代理群集
日志记录
Java 虚拟机 (Java Virtual Machine, JVM)
这些 MBean 提供了一些属性和操作,用于同步轮询和处理基础资源的状态,此外还提供了通知,以使客户端应用程序能够在状态发生更改时异步侦听和响应这些更改。使用 JMX API,客户端应用程序可以执行如下配置和监视任务:
设置代理的端口号
设置代理的最大消息大小
暂停连接服务
设置连接服务的最大线程数
获取服务上的当前连接数
销毁连接
创建目的地
销毁目的地
启用或禁用自动创建目的地
清除来自目的地的所有消息
获取自代理启动以来目的地收到的累积消息数
获取队列的当前状态(正在运行或已暂停)
获取某主题当前消息生成方的数量
清除来自长期订阅者的所有消息
获取当前 JVM 堆大小
有关 JMX API 的简介以及完整的参考信息,请参见《Sun Java System Message Queue 4.1 Developer’s Guide for JMX Clients》。
为了支持 JMX API,添加了几个新的代理属性(请参见表 1–3)。无法使用 Message Queue 命令实用程序 (imqcmd) 从命令行设置这些属性。可以使用代理实用程序 (imqbrokerd) 的 -D 选项设置这些属性,也可以在代理的实例配置文件 (config.properties) 中手动编辑这些属性。此外,某些属性(imq.jmx.rmiregistry.start、imq.jmx.rmiregistry.use 和 imq.jmx.rmiregistry.port)还可以使用表 1–4 中所述的新代理实用程序选项进行设置。此表列出了每个选项,指定了选项类型,并描述了选项用途。
表 1–3 用于支持 JMX 的新代理属性
imq.jmx.connector.list 属性用于定义一组要在代理启动时创建的命名 JMX 连接器;imq.jmx.connector.activelist 用于指定要激活其中哪些连接器。每个命名的连接器都有其自身的一组属性:
imq.jmx.connector.connectorName .urlpath |
imq.jmx.connector.connectorName .useSSL |
imq.jmx.connector.connectorName .brokerHostTrusted |
默认情况下,将创建两个 JMX 连接器,名称分别为 jmxrmi 和 ssljmxrmi;前者被配置为不使用 SSL 加密 (imq.jmx.connector.jmxrmi.useSSL = false),后者则使用 SSL 加密 (imq.jmx.connector.ssljmxrmi.useSSL = true)。默认情况下,代理启动时只激活 jmxrmi 连接器;有关如何激活 ssljmxrmi 连接器以进行安全通信的信息,请参见针对 JMX 客户端的 SSL 支持。
为了方便起见,还向命令行代理实用程序 (imqbrokerd) 中添加了新的选项(表 1–4),用于控制 RMI 注册表的使用、启动和端口。这些选项的用途和影响与表 1–3 中所述的等效代理属性相同。此表列出了每个选项,指定了选项的等效代理属性,并描述了选项用途。
表 1–4 用于支持 JMX 的新代理实用程序选项
选项 |
等效代理属性 |
描述 |
---|---|---|
-startRmiRegistry |
imq.jmx.rmiregistry.start |
指定在代理启动时是否启动 RMI 注册表。 |
-useRmiRegistry |
imq.jmx.rmiregistry.use |
指定是否使用外部 RMI 注册表。 |
-rmiRegistryPort |
imq.jmx.rmiregistry.port |
RMI 注册表的端口号 |
命令行命令实用程序 (imqcmd) 中添加了一个新的子命令(表 1–5),用于列出代理启动时创建并启动的 JMX 连接器的 JMX 服务 URL。不使用 Message Queue 简便类 AdminConnectionFactory 的 JMX 客户端需要使用此信息来获取 JMX 连接器;另外,通过通用的 JMX 浏览器(例如 Java 监视和管理控制台 jconsole),还可以使用此信息来管理或监视 Message Queue。
表 1–5 新的命令实用程序子命令
子命令 |
描述 |
---|---|
list jmx |
列出 JMX 连接器的 JMX 服务 URL |
如上所述,默认情况下将使用预配置的 JMX 连接器 jmxrmi 来配置 Message Queue 消息代理,从而进行不安全的通信。需要使用安全套接字层 (Secure Socket Layer, SSL) 进行安全通信的应用程序,必须激活备用的安全 JMX 连接器 ssljmxrmi。这需要执行以下步骤:
获取和安装签名证书,方法与为 ssljms、ssladmin 或 cluster 连接服务获取和安装签名证书(如 Message Queue 管理指南 中所述)相同。
在信任存储库中安装根证书颁发机构颁发的证书(如有必要)。
将 ssljmxrmi 连接器添加到代理启动时要激活的 JMX 连接器列表:
imq.jmx.connector.activelist=jmxrmi,ssljmxrmi
使用 Message Queue 代理实用程序 ( imqbrokerd) 启动代理,可以将密码文件中的密钥库密码传递给此实用程序,也可以在提示时从命令行键入密码。
默认情况下,ssljmxrmi 连接器(或基于 SSL 的任何其他连接器)被配置为验证提供给它的所有代理 SSL 证书。要避免进行此验证(例如,在使用自签名证书进行软件测试时),请将代理属性 imq.jmx.connector.ssljmxrmi.brokerHostTrusted 设置为 true。
在客户端,必须使用将 ssljmxrmi 指定为首选连接器的 URL 配置管理员连接工厂 (AdminConnectionFactory ) :
AdminConnectionFactory acf = new AdminConnectionFactory(); acf.setProperty(AdminConnectionConfiguration.imqAddress, "mq://myhost:7676/ssljmxrmi");
如果需要,可使用系统属性 javax.net.ssl.trustStore 和 javax.net.ssl.trustStorePassword 将 JMX 客户端指向信任存储库。
本部分介绍了 Message Queue 4.0 对客户端运行时环境日志记录的支持,记录内容包括与连接和会话有关的事件。
JDK 1.4(及更高版本)包含 java.util.logging 库。此库实现了一个标准记录程序接口,可用于特定于应用程序的日志记录。
Message Queue 客户端运行时环境使用 Java 日志记录 API 来实现日志记录功能。可以使用所有 J2SE 1.4 日志记录工具来配置日志记录活动。 例如,应用程序可以使用以下 Java 日志记录工具来配置 Message Queue 客户端运行时环境输出日志记录信息的方式:
日志记录处理程序
日志记录过滤器
日志记录格式化程序
日志记录级别
有关 Java 日志记录 API 的详细信息,请参见位于 http://java.sun.com/j2se/1.4.2/docs/guide/util/logging/overview.html 的 Java 日志记录概述
Message Queue 提供程序定义了一组与日志记录级别和日志记录活动相关联的日志记录名称空间,以使 Message Queue 客户端能够在正确设置日志记录配置后记录连接和会话事件。
Message Queue 客户端运行时环境的根日志记录名称空间被定义为 javax.jms。Message Queue 客户端运行时环境中的所有记录程序均将此名称用作父名称空间。
用于 Message Queue 客户端运行时环境的日志记录级别与 java.util.logging.Level 类中定义的级别相同。此类定义了七个标准日志级别,另外还有两个用于打开和关闭日志记录的设置。
关闭日志记录。
最高优先级,最大值。由应用程序定义。
由应用程序定义。
由应用程序定义。
由应用程序定义。
由应用程序定义。
由应用程序定义。
最低优先级,最小值。由应用程序定义。
启用所有消息的日志记录。
通常,Message Queue 客户端运行时环境中发生的异常和错误由具有 javax.jms 名称空间的记录程序进行记录。
从 JVM 抛出并由客户端运行时环境捕获的异常(例如 IOException)由具有 javax.jms 日志记录名称空间的记录程序记录为 WARNING 级别。
从客户端运行时环境抛出的 JMS 异常(例如 IllegalStateException)由具有 javax.jms 日志记录名称空间的记录程序记录为 FINER 级别。
从 JVM 抛出并由客户端运行时环境捕获的错误(例如 OutOfMemoryError)由具有 javax.jms 日志记录名称空间的记录程序记录为 SEVERE 级别。
下表列出了可记录的事件和日志级别,要记录 JMS 连接事件和会话事件,必须设置此级别。
下表描述了连接的日志级别和事件。
表 1–6 javax.jms.connection 名称空间的日志级别和事件
日志级别 |
事件 |
---|---|
FINE |
连接已创建 |
FINE |
连接已启动 |
FINE |
连接已关闭 |
FINE |
连接已断开 |
FINE |
已重新连接 |
FINER |
其他连接活动,例如 setClientID |
FINEST |
消息、确认、Message Queue 操作和控制消息(例如提交事务) |
对于会话,将在日志记录中记录以下信息。
传送给使用方的消息的每个日志记录包括 ConnectionID、SessionID 和 ConsumerID。
由生成方发送的消息的每个日志记录包括 ConnectionID、SessionID、ProducerID 和目的地名称。
下表描述了会话的日志级别和事件。
表 1–7 javax.jms.session 名称空间的日志级别和事件
日志级别 |
事件 |
---|---|
FINE |
会话已创建 |
FINE |
会话已关闭 |
FINE |
生成方已创建 |
FINE |
使用方已创建 |
FINE |
目的地已创建 |
FINER |
其他会话活动,例如提交会话。 |
FINEST |
已生成并使用消息。(未在日志记录中记录消息属性和主体。) |
默认情况下,输出日志级别是从运行应用程序所在的 JRE 继承而来的。可查看 JRE_DIRECTORY/lib/logging.properties 文件以确定该级别。
可通过编程方式或使用配置文件来配置日志记录,还可以控制进行日志记录的范围。以下部分介绍了这些可行的操作。
以下示例说明了如何在 JRE_DIRECTORY/lib/logging.properties 文件(用于设置 Java 运行环境的日志级别)中设置日志记录的名称空间和级别。使用此 JRE 的所有应用程序都将具有相同的日志记录配置。下面的样例配置将 javax.jms.connection 名称空间的日志记录级别设置为 INFO,并指定将输出写入 java.util.logging.ConsoleHandler。
#logging.properties file. # "handlers" specifies a comma separated list of log Handler # classes. These handlers will be installed during VM startup. # Note that these classes must be on the system classpath. # By default we only configure a ConsoleHandler, which will only # show messages at the INFO and above levels. handlers= java.util.logging.ConsoleHandler # Default global logging level. # This specifies which kinds of events are logged across # all loggers. For any given facility this global level # can be overriden by a facility-specific level. # Note that the ConsoleHandler also has a separate level # setting to limit messages printed to the console. .level= INFO # Limit the messages that are printed on the console to INFO and above. java.util.logging.ConsoleHandler.level = INFO java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter # The logger with javax.jms.connection name space will write # Level.INFO messages to its output handler(s). In this configuration # the ouput handler is set to java.util.logging.ConsoleHandler. javax.jms.connection.level = INFO
还可以从用于运行应用程序的 Java 命令行定义日志记录配置文件。该应用程序将使用在指定的日志记录文件中定义的配置。在以下示例中,configFile 使用的格式与 JRE_DIRECTORY/lib/logging.properties 文件中定义的格式相同。
java -Djava.util.logging.config.file=configFile MQApplication
以下代码使用 java.util.logging API 来记录连接事件,方法是将 javax.jms.connection 名称空间的日志级别更改为 FINE。您可以在应用程序中包含此类代码,以便通过编程方式设置日志记录配置。
import java.util.logging.*; //construct a file handler and output to the mq.log file //in the system's temp directory. Handler fh = new FileHandler("%t/mq.log"); fh.setLevel (Level.FINE); //Get Logger for "javax.jms.connection" domain. Logger logger = Logger.getLogger("javax.jms.connection"); logger.addHandler (fh); //javax.jms.connection logger would log activities //with level FINE and above. logger.setLevel (Level.FINE);
连接事件通知允许 Message Queue 客户端侦听关闭和重新连接事件,并根据通知类型和连接状态采取适当的操作。例如,如果发生故障转移,并且客户端重新连接到了其他代理,则应用程序可能希望清除事务状态,并继续执行新事务。
如果 Message Queue 提供程序检测到某个连接存在严重问题,它将调用该连接对象的已注册的异常侦听器。它调用侦听器的 onException 方法,并向其传递一个用于描述该问题的 JMSException 参数。Message Queue 提供程序还提供一个事件通知 API,该 API 允许客户端运行时环境向应用程序通知有关连接状态更改的信息。通知 API 由以下元素定义:
com.sun.messaging.jms.notification 软件包,用于定义事件侦听器和通知事件对象。
com.sun.messaging.jms.Connection 接口,用于定义 javax.jms.Connection 接口的扩展。
以下部分介绍了可以触发通知的事件,并说明了如何创建事件侦听器。
下表列出并介绍了事件侦听器可以返回的事件。
请注意,当发生连接事件时,不会调用 JMS 异常侦听器。仅当客户端运行时环境达到最大重新连接尝试次数时,才会调用异常侦听器。客户端运行时环境在调用异常侦听器之前,始终会调用事件侦听器。
表 1–8 通知事件
事件类型 |
含义 |
---|---|
ConnectionClosingEvent |
如果 Message Queue 客户端运行时环境从代理收到通知,指明由于管理员请求关闭而即将关闭连接,则 Message Queue 客户端运行时环境将生成此事件。 |
ConnectionClosedEvent |
关闭连接时(由于代理错误或管理员请求关闭或重新启动连接),Message Queue 客户端运行时环境将生成此事件。 如果事件侦听器收到 ConnectionClosedEvent,则应用程序可以使用所收到的事件的 getEventCode() 方法,来获取用于指明关闭原因的事件代码。 |
ConnectionReconnectedEvent |
Message Queue 客户端运行时环境已重新连接到代理。此代理可以是客户端先前连接的代理,也可以是其他代理。 应用程序可以使用所收到的事件的 getBrokerAddress 方法,来获取它所重新连接到的代理的地址。 |
ConnectionReconnectFailedEvent |
Message Queue 客户端运行时环境无法重新连接到代理。每次重新连接尝试失败时,该运行时环境都会生成新的事件,并将该事件传送到事件侦听器。 发生连接事件时不会调用 JMS 异常侦听器。仅当客户端运行时环境达到最大重新连接尝试次数时,才会调用 JMS 异常侦听器。客户端运行时环境在调用异常侦听器之前,始终会调用事件侦听器。 |
以下代码示例说明了如何设置连接事件侦听器。只要发生连接事件,客户端运行时环境就会调用事件侦听器的 onEvent 方法。
//create an MQ connection factory. com.sun.messaging.ConnectionFactory factory = new com.sun.messaging.ConnectionFactory(); //create an MQ connection. com.sun.messaging.jms.Connection connection = (com.sun.messaging.jms.Connection )factory.createConnection(); //construct an MQ event listener. The listener implements //com.sun.messaging.jms.notification.EventListener interface. com.sun.messaging.jms.notification.EventListener eListener = new ApplicationEventListener(); //set event listener to the MQ connection. connection.setEventListener ( eListener );
在此示例中,应用程序选择让事件侦听器将连接事件记录到应用程序的日志记录系统中:
public class ApplicationEventListener implements com.sun.messaging.jms.notification.EventListener { public void onEvent ( com.sun.messaging.jms.notification.Event connEvent ) { log (connEvent); } private void log ( com.sun.messaging.jms.notification.Event connEvent ) { String eventCode = connEvent.getEventCode(); String eventMessage = connEvent.getEventMessage(); //write event information to the output stream. } }