activemq-artemis/docs/user-manual/zh/using-jms.xml

274 lines
18 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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 AttributionShare 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因此ActiveMQ提供了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>ActiveMQ还提供了许多的JMS的示例程序examples。比如简单的JMS Queue和Topic的示例就很适合初学者做为了
解ActiveMQ JMS的起点。<xref linkend="examples"/>对这些示例作了详细的说明。</para>
<para>下面我们将带领读者一步步地配置ActiveMQ的JMS服务并创建一个简单的JMS程序。我们还将展示如何在没有JNDI的情况下
来使用ActiveMQ中的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>activemq-jms.xml</literal>文件包含了需要创建与部署的JMS QueueTopic和ConnectionFactory
的实例。该文件必须要指定在classpath中。从这个文件中部署好的对象都可以用JNDI来找到。</para>
<para>JMS客户端可以利用JMS ConnectionFactory对象来创建与服务器的连接。ConnectionFactory中有关于服务器地址的
信息以及各种参数。通常使用这些参数的默认值即可。</para>
<para>这里我们将要在服务器端部署一个JMS队列和一个JMS ConnectionFactory 连接工厂。当然完全可以部署多个JMS对象。
下面给出了具体的配置内容:</para>
<programlisting>
&lt;configuration xmlns="urn:activemq"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:activemq ../schemas/activemq-jms.xsd "&gt;
&lt;connection-factory name="ConnectionFactory"&gt;
&lt;connectors>
&lt;connector-ref connector-name="netty"/&gt;
&lt;/connectors>
&lt;entries&gt;
&lt;entry name="ConnectionFactory"/&gt;
&lt;/entries&gt;
&lt;/connection-factory&gt;
&lt;queue name="OrderQueue"&gt;
&lt;entry name="queues/OrderQueue"/&gt;
&lt;/queue&gt;
&lt;/configuration&gt;
</programlisting>
<para>在本文件中我们部署了一个名为 <literal>ConnectionFactory</literal> 的一个连接工厂,并且将其绑定到
JNDI中。如果需要可以将一个连接工厂绑定为多个名称。只需要将绑定的名字加入到 <literal>entry</literal>
中即可。</para>
<note>
<para>在JMS ConnectionFactory的配置中引用了一个名为 <literal>netty</literal><literal>connector</literal>
它实际上指向的是ActiveMQ核心中部署的一个连接器connector。它的配置在ActiveMQ的核心配置文件
<literal>activemq-configuration.xml</literal> 中。它定义了采用何种传输与服务器连接。</para>
</note>
</section>
<section id="using-jms.configure.factory.types">
<title>连接工厂的类型</title>
<para>在JMS API文档中有几种不同类型的连接工厂供用户使用。ActiveMQ为用户提供了配置连接工厂类型的参数。用户可以通过
配置连接工厂的”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>
&lt;configuration xmlns="urn:activemq"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:activemq ../schemas/activemq-jms.xsd "&gt;
&lt;connection-factory name="ConnectionFactory" signature="queue"&gt;
&lt;xa>true&lt;/xa>
&lt;connectors>
&lt;connector-ref connector-name="netty"/&gt;
&lt;/connectors>
&lt;entries&gt;
&lt;entry name="ConnectionFactory"/&gt;
&lt;/entries&gt;
&lt;/connection-factory&gt;
&lt;/configuration&gt;
</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>activemq-beans.xml</literal>
文件中的 <literal>JNDIServer</literal> bean下</para>
<programlisting>
&lt;bean name="JNDIServer" class="org.jnp.server.Main"&gt;
&lt;property name="namingInfo"&gt;
&lt;inject bean="Naming"/&gt;
&lt;/property&gt;
&lt;property name="port"&gt;1099&lt;/property&gt;
&lt;property name="bindAddress"&gt;localhost&lt;/property&gt;
&lt;property name="rmiPort"&gt;1098&lt;/property&gt;
&lt;property name="rmiBindAddress"&gt;localhost&lt;/property&gt;
&lt;/bean&gt;
</programlisting>
<note>
<para>如果你的JNDI服务器与客户端不在同一台机器上一定不要忘记将bindAddress改成相应的地址
千万不能用<literal>localhost</literal></para>
</note>
<note>
<para><emphasis>只有当ActiveMQ作为独立服务器运行时</emphasis>
才可以配置JNDIServer bean。当ActiveMQ运行于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>看起来就是这么简单。 在ActiveMQ有发布包中有很多各种各样的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>ActiveMQ允许你不通过JNDI也能使用JMS。ActiveMQ支持直接创建JMS的各种对象而无需JNDI的存在。</para>
<para><xref linkend="examples"/>中包括有这样的例子供读者参考。</para>
<para>下面我们就将上述那个简单的例子重写以抛开对JNDI的依赖</para>
<para>我们通过ActiveMQJMSClient类来方便地创建JMS的ConnectionFactory。注意这里要提供各种连接参数和定义
所用的传输方式。有关连接器connector的信息参见<xref linkend="configuring-transports"
/></para>
<programlisting>
TransportConfiguration transportConfiguration =
new TransportConfiguration(NettyConnectorFactory.class.getName());
ConnectionFactory cf = ActiveMQJMSClient.createConnectionFactory(transportConfiguration);
</programlisting>
<para>同样利用ActiveMQJMSClient类创建JMS Queue对象</para>
<programlisting>Queue orderQueue = ActiveMQJMSClient.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>在建立持久的订阅subscriptionJMS客户需要有一个客户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>