274 lines
18 KiB
XML
274 lines
18 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!-- ============================================================================= -->
|
||
<!-- Copyright © 2009 Red Hat, Inc. and others. -->
|
||
<!-- -->
|
||
<!-- The text of and illustrations in this document are licensed by Red Hat under -->
|
||
<!-- a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). -->
|
||
<!-- -->
|
||
<!-- An explanation of CC-BY-SA is available at -->
|
||
<!-- -->
|
||
<!-- http://creativecommons.org/licenses/by-sa/3.0/. -->
|
||
<!-- -->
|
||
<!-- In accordance with CC-BY-SA, if you distribute this document or an adaptation -->
|
||
<!-- of it, you must provide the URL for the original version. -->
|
||
<!-- -->
|
||
<!-- Red Hat, as the licensor of this document, waives the right to enforce, -->
|
||
<!-- and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent -->
|
||
<!-- permitted by applicable law. -->
|
||
<!-- ============================================================================= -->
|
||
<chapter id="using-jms">
|
||
<title>使用JMS</title>
|
||
<para>很多用户喜欢使JMS,因此HornetQ提供了JMS服务。</para>
|
||
<para>JMS是一个普遍使用API标准,绝大多数的消息系统都提供JMS接口。如果你对JMS还不熟悉,建议你先参考一下
|
||
Sun的<ulink url="http://java.sun.com/products/jms/tutorial/1_3_1-fcs/doc/jms_tutorialTOC.html">
|
||
JMS 教程</ulink>。</para>
|
||
<para>HornetQ还提供了许多的JMS的示例程序(examples)。比如简单的JMS Queue和Topic的示例,就很适合初学者做为了
|
||
解HornetQ JMS的起点。<xref linkend="examples"/>对这些示例作了详细的说明。</para>
|
||
<para>下面我们将带领读者一步步地配置HornetQ的JMS服务,并创建一个简单的JMS程序。我们还将展示如何在没有JNDI的情况下
|
||
来使用HornetQ中的JMS。</para>
|
||
<section>
|
||
<title>一个简单的订购系统</title>
|
||
<para>本章我们将用一个简单的订购系统做为一个例子。尽管它十分简单,但是它能够很好地向大家展示JMS的设置和使用。</para>
|
||
<para>本例中有一个名为 <literal>OrderQueue</literal>JMS队列,还将有一个 <literal>MessageProducer</literal>
|
||
用来向队列发送订购消息。发送到队列的消息由一个 <literal>MessageConsumer</literal> 来接收。</para>
|
||
<para>我们所用的队列是<literal>持久(durable)</literal>的队列,也就是说这个队列不受服务器故障的影响。当服务器
|
||
发生故障重新启动后,这个队列仍然存在。我们需要把这个队列事先部署好。办法就是将队列写到JMS的配置文件中。当服务启动
|
||
时将配置文件中的队列自动部署好。</para>
|
||
</section>
|
||
<section id="using-jms.server.configuration">
|
||
<title>JMS服务的配置</title>
|
||
<para><literal>hornetq-jms.xml</literal>文件包含了需要创建与部署的JMS Queue,Topic和ConnectionFactory
|
||
的实例。该文件必须要指定在classpath中。从这个文件中部署好的对象都可以用JNDI来找到。</para>
|
||
<para>JMS客户端可以利用JMS ConnectionFactory对象来创建与服务器的连接。ConnectionFactory中有关于服务器地址的
|
||
信息以及各种参数。通常使用这些参数的默认值即可。</para>
|
||
<para>这里我们将要在服务器端部署一个JMS队列和一个JMS ConnectionFactory (连接工厂)。当然完全可以部署多个JMS对象。
|
||
下面给出了具体的配置内容:</para>
|
||
<programlisting>
|
||
<configuration xmlns="urn:hornetq"
|
||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
xsi:schemaLocation="urn:hornetq ../schemas/hornetq-jms.xsd ">
|
||
|
||
<connection-factory name="ConnectionFactory">
|
||
<connectors>
|
||
<connector-ref connector-name="netty"/>
|
||
</connectors>
|
||
<entries>
|
||
<entry name="ConnectionFactory"/>
|
||
</entries>
|
||
</connection-factory>
|
||
|
||
<queue name="OrderQueue">
|
||
<entry name="queues/OrderQueue"/>
|
||
</queue>
|
||
|
||
</configuration>
|
||
</programlisting>
|
||
<para>在本文件中我们部署了一个名为 <literal>ConnectionFactory</literal> 的一个连接工厂,并且将其绑定到
|
||
JNDI中。如果需要可以将一个连接工厂绑定为多个名称。只需要将绑定的名字加入到 <literal>entry</literal>
|
||
中即可。</para>
|
||
<note>
|
||
<para>在JMS ConnectionFactory的配置中引用了一个名为 <literal>netty</literal>的<literal>connector</literal>。
|
||
它实际上指向的是HornetQ核心中部署的一个连接器(connector)。它的配置在HornetQ的核心配置文件
|
||
<literal>hornetq-configuration.xml</literal> 中。它定义了采用何种传输与服务器连接。</para>
|
||
</note>
|
||
</section>
|
||
<section id="using-jms.configure.factory.types">
|
||
<title>连接工厂的类型</title>
|
||
<para>在JMS API文档中有几种不同类型的连接工厂供用户使用。HornetQ为用户提供了配置连接工厂类型的参数。用户可以通过
|
||
配置连接工厂的”signature"属性和"xa"参数来得到想要的类型。“singature"属性是字符串类型,它有三个可选值:
|
||
<emphasis>generic</emphasis>、<emphasis>queue</emphasis>和<emphasis>topic</emphasis>;
|
||
<literal>xa</literal>是一个布尔型参数。下表给出了不同类型连接工厂对应的配置值:</para>
|
||
<table frame="topbot" id="using-jms.table.configure.factory.types">
|
||
<title>连接工厂类型的配置</title>
|
||
<tgroup cols="3">
|
||
<colspec colname="signature" colnum="1"/>
|
||
<colspec colname="xa" colnum="2"/>
|
||
<colspec colname="cftype" colnum="3"/>
|
||
<thead>
|
||
<row>
|
||
<entry>signature</entry>
|
||
<entry>xa</entry>
|
||
<entry>连接工厂的类型</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>generic (默认)</entry>
|
||
<entry>false (默认)</entry>
|
||
<entry>javax.jms.ConnectionFactory</entry>
|
||
</row>
|
||
<row>
|
||
<entry>generic</entry>
|
||
<entry>true</entry>
|
||
<entry>javax.jms.XAConnectionFactory</entry>
|
||
</row>
|
||
<row>
|
||
<entry>queue</entry>
|
||
<entry>false</entry>
|
||
<entry>javax.jms.QueueConnectionFactory</entry>
|
||
</row>
|
||
<row>
|
||
<entry>queue</entry>
|
||
<entry>true</entry>
|
||
<entry>javax.jms.XAQueueConnectionFactory</entry>
|
||
</row>
|
||
<row>
|
||
<entry>topic</entry>
|
||
<entry>false</entry>
|
||
<entry>javax.jms.TopicConnectionFactory</entry>
|
||
</row>
|
||
<row>
|
||
<entry>topic</entry>
|
||
<entry>true</entry>
|
||
<entry>javax.jms.XATopicConnectionFactory</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
<para>下面的例子配置了一个XAQueueConnectionFactory:</para>
|
||
<programlisting>
|
||
<configuration xmlns="urn:hornetq"
|
||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
xsi:schemaLocation="urn:hornetq ../schemas/hornetq-jms.xsd ">
|
||
|
||
<connection-factory name="ConnectionFactory" signature="queue">
|
||
<xa>true</xa>
|
||
<connectors>
|
||
<connector-ref connector-name="netty"/>
|
||
</connectors>
|
||
<entries>
|
||
<entry name="ConnectionFactory"/>
|
||
</entries>
|
||
</connection-factory>
|
||
</configuration>
|
||
</programlisting>
|
||
</section>
|
||
<section>
|
||
<title>JNDI的配置</title>
|
||
<para>当客户端使用JNDI时需要定义一些JNDI的参数。这些参数主要用来确定JNDI服务的地址。这些参数通常保存在
|
||
一个名为 <literal>jndi.properties</literal> 的文件中。这个文件需要在客户端的classpath中。或者你
|
||
可以在创建JNDI的InitialContext时将这些参数传进去。想了解全面的JNDI知识,可以参见 <ulink
|
||
url="http://java.sun.com/products/jndi/tutorial/TOC.html">Sun JNDI 教程</ulink>
|
||
。</para>
|
||
<para>要与JBoss的JNDI Server进行通迅,需要指定以下的JNDI参数:</para>
|
||
<programlisting>
|
||
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
|
||
java.naming.provider.url=jnp://myhost:1099
|
||
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
|
||
</programlisting>
|
||
<para>其中的 <literal>myhost</literal> 是 JNDI server的主机名或IP地址。 1099是端口号,根据不同的配置,
|
||
端口号也可能不同。</para>
|
||
<para>在默认的单独方式(standalone)配置中,JNDI服务端口等参数定义在<literal>hornetq-beans.xml</literal>
|
||
文件中的 <literal>JNDIServer</literal> bean下,如:</para>
|
||
<programlisting>
|
||
<bean name="JNDIServer" class="org.jnp.server.Main">
|
||
<property name="namingInfo">
|
||
<inject bean="Naming"/>
|
||
</property>
|
||
<property name="port">1099</property>
|
||
<property name="bindAddress">localhost</property>
|
||
<property name="rmiPort">1098</property>
|
||
<property name="rmiBindAddress">localhost</property>
|
||
</bean>
|
||
</programlisting>
|
||
<note>
|
||
<para>如果你的JNDI服务器与客户端不在同一台机器上,一定不要忘记将bindAddress改成相应的地址,
|
||
千万不能用<literal>localhost</literal>!</para>
|
||
</note>
|
||
<note>
|
||
<para><emphasis>只有当HornetQ作为独立服务器运行时</emphasis>
|
||
才可以配置JNDIServer bean。当HornetQ运行于JBoss应用服务器中时,由于JBOSS服务器已经提供了
|
||
JNDI服务,所以就不需要再进行配置了。</para>
|
||
</note>
|
||
</section>
|
||
<section>
|
||
<title>程序代码</title>
|
||
<para>下面给出的例子中的代码:</para>
|
||
<para>首先我们创建一个JNDI的Initial Context:</para>
|
||
<programlisting>InitialContext ic = new InitialContext();</programlisting>
|
||
<para>下面我们查找 connection factory:</para>
|
||
<programlisting>ConnectionFactory cf = (ConnectionFactory)ic.lookup("/ConnectionFactory");</programlisting>
|
||
<para>然后查找 Queue:</para>
|
||
<programlisting>Queue orderQueue = (Queue)ic.lookup("/queues/OrderQueue");</programlisting>
|
||
<para>接下来用拿到的ConnectionFactory建立JMS连接:</para>
|
||
<programlisting>Connection connection = cf.createConnection();</programlisting>
|
||
<para>再创建一个非事务的、AUTO_ACKNOWLEDGE方式的JMS Session:</para>
|
||
<programlisting>Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);</programlisting>
|
||
<para>创建一个 MessageProducer 以向队列发送订单消息:</para>
|
||
<programlisting>MessageProducer producer = session.createProducer(orderQueue);</programlisting>
|
||
<para>创建一个 MessageConsumer 以从队列中接收订单消息:</para>
|
||
<programlisting>MessageConsumer consumer = session.createConsumer(orderQueue);</programlisting>
|
||
<para>要启动连接,以使消息能传递给接收者:</para>
|
||
<programlisting>connection.start();</programlisting>
|
||
<para>发送一个简单的TextMessage:</para>
|
||
<programlisting>TextMessage message = session.createTextMessage("This is an order");
|
||
producer.send(message);</programlisting>
|
||
<para>之后接收这个消息:</para>
|
||
<programlisting>TextMessage receivedMessage = (TextMessage)consumer.receive();
|
||
System.out.println("Got order: " + receivedMessage.getText());
|
||
</programlisting>
|
||
<para>看起来就是这么简单。 在HornetQ有发布包中有很多各种各样的JMS例子供用户参考。</para>
|
||
<warning>
|
||
<para>请注意,JMS的连接(connection)、会话(session)、生产者(producer)和消费者(consumer)
|
||
对象是可以<emphasis>重用</emphasis>的。</para>
|
||
<para>如果每发送或接收一个消息都要重新创建这些JMS对象,是不符合设计模式的要求的。这样做会造成应用程序
|
||
的性能很差。这方面的内容在<xref linkend="perf-tuning"/>中将会进一步的讨论。</para>
|
||
</warning>
|
||
</section>
|
||
<section>
|
||
<title>不使用JNDI而直接创建JMS的对象</title>
|
||
<para>尽管采用JNDI对 JMS 的各种<emphasis>管理对象(Administered
|
||
Objects)</emphasis> (即JMS Queue, Topic and ConnectionFactory)是很常用的方法,但在有些
|
||
情况时JNDI不可用,或者你不需要用JNDI时,如何还能正常使用JMS呢?</para>
|
||
<para>HornetQ允许你不通过JNDI也能使用JMS。HornetQ支持直接创建JMS的各种对象而无需JNDI的存在。</para>
|
||
<para>在<xref linkend="examples"/>中包括有这样的例子供读者参考。</para>
|
||
<para>下面我们就将上述那个简单的例子重写,以抛开对JNDI的依赖:</para>
|
||
<para>我们通过HornetQJMSClient类来方便地创建JMS的ConnectionFactory。注意这里要提供各种连接参数和定义
|
||
所用的传输方式。有关连接器(connector)的信息参见<xref linkend="configuring-transports"
|
||
/>。</para>
|
||
<programlisting>
|
||
TransportConfiguration transportConfiguration =
|
||
new TransportConfiguration(NettyConnectorFactory.class.getName());
|
||
ConnectionFactory cf = HornetQJMSClient.createConnectionFactory(transportConfiguration);
|
||
</programlisting>
|
||
<para>同样利用HornetQJMSClient类创建JMS Queue对象:</para>
|
||
<programlisting>Queue orderQueue = HornetQJMSClient.createQueue("OrderQueue");</programlisting>
|
||
<para>然后用连接工厂创建 JMS 连接:</para>
|
||
<programlisting>Connection connection = cf.createConnection();</programlisting>
|
||
<para>还有非事务的\AUTO_ACKNOWLEDGE方式的 JMS 会话(session):</para>
|
||
<programlisting>Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);</programlisting>
|
||
<para>以及用于发送消息的MessageProducer:</para>
|
||
<programlisting>MessageProducer producer = session.createProducer(orderQueue);</programlisting>
|
||
<para>和接收消息的 MessageConsumer:</para>
|
||
<programlisting>MessageConsumer consumer = session.createConsumer(orderQueue);</programlisting>
|
||
<para>启动连接:</para>
|
||
<programlisting>connection.start();</programlisting>
|
||
<para>创建一个简单的 TextMessage 并将其发送到队列:</para>
|
||
<programlisting>TextMessage message = session.createTextMessage("This is an order");
|
||
producer.send(message);</programlisting>
|
||
<para>接收消息:</para>
|
||
<programlisting>TextMessage receivedMessage = (TextMessage)consumer.receive();
|
||
System.out.println("Got order: " + receivedMessage.getText());
|
||
</programlisting>
|
||
</section>
|
||
<section id="using-jms.clientid">
|
||
<title>Client ID的设置</title>
|
||
<para>在建立持久的订阅(subscription)时,JMS客户需要有一个客户ID (client id)。我们可以通过配置
|
||
connection factory来定义它。(其中的 <literal>client-id</literal>项)。这样所有通过这个
|
||
connection factory来创建的连接都具有这个客户ID。</para>
|
||
</section>
|
||
<section id="using-jms.dupsokbatchsize">
|
||
<title>设置DUPS_OK的Batch Size </title>
|
||
<para>如果JMS的通知模式为<literal>DUPS_OK</literal>,我们可以配置接收者(consumer)以使得它以批为单位
|
||
发送通知,而不是一个一个地发通知。这样做可以节省很多带宽,效率高。配置的方法是设置connection factory下
|
||
的<literal>dups-ok-batch-size</literal>项。单位是字节(byte)。默认值是1024 * 1024 bytes = 1 MiB。</para>
|
||
</section>
|
||
<section id="using-jms.txbatchsize">
|
||
<title>设置事务(Transaction)的Batch Size</title>
|
||
<para>当在一个事务内接收消息时,可能通过配置使接收者采用批量的方式发送通知,而不是一个一个的发送。这样也可以节省带宽。
|
||
配置方法是设置connection factory下的<literal
|
||
>transaction-batch-size</literal>项。 单位是字节(byte)。默认值是1024 *
|
||
1024。</para>
|
||
</section>
|
||
</chapter>
|