Merging #26 on removing chinese docs

This commit is contained in:
Clebert Suconic 2014-12-01 12:43:02 -05:00
commit b0b19cb4f8
59 changed files with 0 additions and 10150 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,95 +0,0 @@
<?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="architecture">
<title>总体架构</title>
<para>本章对ActiveMQ的总体技术架构进行了概括描述。</para>
<section>
<title>核心架构</title>
<para>ActiveMQ的核心是由一组简单Java对象POJO构成的。同时在设计ActiveMQ时将对外部jar包的依赖降到最低限度。
实际上ActiveMQ的核心部分只有一个外部依赖就是netty.jar。ActiveMQ使用了其中的用于缓冲的一些类。
我们相信这样的理念应该受到用户的欢迎。</para>
<para>由于依赖性很小ActiveMQ可以非常容易地嵌入到其它应用中或者加入到一些依赖注入式的框架中
如JBoss MicrocontainerSpring或Google Guice。</para>
<para>每个ActiveMQ服务器都有自己的超高性能的持久日志journal用于消息和其它信息的持久化。</para>
<para>采用这种独特的高效日志要比采用普通数据库作为持久层的系统拥有更高的性能。</para>
<para>通常情况下分布在不同物理机器上的客户端同时访问ActiveMQ服务器。目前ActiveMQ提供了两套API供客户端使用</para>
<para>
<orderedlist>
<listitem>
<para>核心API。这是一组普通的Java接口用它可以访问ActiveMQ的全部功能。</para>
</listitem>
<listitem>
<para>JMS客户端API。这是标准的JMS API。</para>
</listitem>
</orderedlist>
</para>
<para>实际上JMS API是在核心API的外部加上一层简单的封装。</para>
<para>在ActiveMQ内核是没有JMS的这样设计的目的是为了支持多个协议。</para>
<para>当客户端通过JMS接口访问ActiveMQ时所有JMS的操作都被转换成相应的核心API然后将请求以ActiveMQ格式发向服务器。</para>
<para>ActiveMQ服务器只接收核心API的访问。</para>
<para>图3.1描述了这些操作。</para>
<para>
<graphic fileref="images/architecture1.jpg" align="center"/>
</para>
<para>在图3.1中示出了两个用户访问ActiveMQ服务器。用户1使用JMS API用户2使用的是核心API。</para>
<para>图中清楚的展示出了JMS是如何通过封装facade转化为核心API的。</para>
</section>
<section>
<title>将ActiveMQ嵌入到你的应用程序中</title>
<para>如果你的应用程序内部需要消息服务但同时你又不想将消息服务暴露为单独的ActiveMQ服务器你可以在应用中直接将ActiveMQ实例化。</para>
<para>有关嵌入式ActiveMQ的详细信息请参阅 <xref linkend="embedding-activemq"
/>。</para>
</section>
<section>
<title>将ActiveMQ与JEE应用服务器集成</title>
<para>ActiveMQ提供了标准的JCA适配器利用它可以将ActiveMQ轻松地集成到任何一个符合JEE规范的应用服务器或servlet容器中。</para>
<para>JEE应用服务品提供了消息BeanMDB用于处理来自外部的消息比如来自JMS系统或邮件系统的消息。</para>
<para>最常见的应用应该是用MDB来接收来自JMS系统中的消息了。在JEE规范中规定了JEE应用服务器使用JCA adaptor与JMS消息系统集成
MDB通过这个adaptor来接收消息。</para>
<para>JCA adaptor不仅可以用来接收消息还可以用来从EJB或servlet中向外部的JMS发送消息。在JEE应用服务器中应该用JCA adaptor与JMS系统进行交互。
实际上JEE规范中不允许在JEE服务器中不通过JCA而直接访问JMS系统。</para>
<para>在EJB中使用消息往往需要连接池或交易而JCA可以提供这方面的服务无需额外的开发任务。当然直接访问JMS系统是可能的
但是你将不能利用JCA所提供的这些有用的功能因此我们不建议使用直接访问的方式。</para>
<para>图3.2给出了ActiveMQ通过JCA adaptor与JEE应用服务器集成的示意图。图中可以看出所有的交互都通过JCA adaptor。</para>
<para>图中带有禁止符号的箭头表明的是从EJB会话Bean直接访问ActiveMQ的情况。由于不通过JCA这种方法往往造成每次EJB访问ActiveMQ都要新建一个连接和会话
使效率大降低。这被视为反设计模式anti-pattern</para>
<para>
<graphic fileref="images/architecture2.jpg"/>
</para>
<para><xref linkend="appserver-integration"/>对如何使用JCA给出了更加详细的描述。</para>
</section>
<section>
<title>ActiveMQ作为独立的服务standalone</title>
<para>ActiveMQ可以部署成为独立的服务器。它可运行于任何JEE应用服务器之外作为一个独立的服务运行。
作为独立服务器运行时ActiveMQ消息服务器包括一个核心服务器一个JMS服务以及一个JNDI服务。</para>
<para>JMS服务用来部署服务器端<literal>activemq-jms.xml</literal>配置文件中的JMS QueueTopic和ConnectionFactory实例。
此外它还提供一组简单的管理接口通过这些接口可以创建、消毁destroyQueueTopic和ConnectionFactory实例。
用于可以通过JMX或连接使用这些接口。JMS服务是单独的服务它不是ActiveMQ核心服务。ActiveMQ的核心不包含JMS相关的服务。
如果你不需要通过服务器端的xml配置文件部署任何JMS对象也不需要JMS的管理接口你可以选择不启动该服务。</para>
<para>启动JNDI服务的目的是因为JMS需要通过JNDI来获得QueueTopic以及ConnectionFactory。如果不需要也可以选择不启动该服务。</para>
<para>ActiveMQ允许在客户端程序中通过编程来直接创建各种JMS对象和核心对象来代替JNDI查找所以JNDI不是必需的。
ActiveMQ采用JBoss Microcontainer来引导并实例化服务并保证模块之间的依赖关系。JBoss Microcontainer是一个轻量级的POJO引导器bootstrapper</para>
<para>图3.3给出了ActiveMQ独立服务器的架构。</para>
<para>
<graphic fileref="images/architecture3.jpg"/>
</para>
<para>相关配置的相关信息可以在第<xref
linkend="server.configuration"/>找到。$ </para>
</section>
</chapter>

View File

@ -1,47 +0,0 @@
<?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="client-classpath">
<title>客户端的Classpath</title>
<para>ActiveMQ的<emphasis>客户端Classpath</emphasis>需要有几个jar文件。具体是哪几个要根据客户端
是需要内核API、JMS和JNDI中的哪些服务来确定。</para>
<warning>
<para>本章所提及的所有jar文件全部在HorneQ发布包的 <literal>lib</literal>目录下。在使用中一定
要确保所有的jar文件属于同一个发布版本。将不同版本的jar混在一起使用可能造成一些难以发现的错误。</para>
</warning>
<section>
<title>使用ActiveMQ内核的客户端</title>
<para>如果客户端只使用ActiveMQ内核非JMS客户端需要将 <literal
>activemq-core-client.jar</literal>
<literal>netty.jar</literal> 放到classpath中。</para>
</section>
<section>
<title>JMS客户端</title>
<para>如果客户端使用JMS需要在classpath上增加两个jar文件 <literal
>activemq-jms-client.jar</literal><literal>jboss-jms-api.jar</literal></para>
<note>
<para><literal>jboss-jms-api.jar</literal>中包含的只是 <literal>javax.jms.*</literal> 包中的接口类。
如果这些类已经在你的classpath中则你就不需要这个jar文件。</para>
</note>
</section>
<section>
<title>需要JNDI的JMS客户端</title>
<para>如果你的JMS客户端使用JNDI来查找ActiveMQ单独服务器上的对象你需要将 <literal>jnp-client.jar</literal> 增加
到你的classpath中。</para>
</section>
</chapter>

View File

@ -1,116 +0,0 @@
<?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="client-reconnection">
<title>客户端重新连接与会话恢复</title>
<para>通过配置ActiveMQ的客户端在与服务器的连接出现故障时可以自动地重新建立连接并恢复与服务器的通迅。</para>
<section>
<title>100%透明的会话恢复re-attachment</title>
<para>如果网络出现暂时性连接故障,并且服务器没有重启的情况下,当前的会话还会存在服务器中,其状态如同客户端
没有断开超过连接TTL<xref linkend="connection-ttl"/>时间。</para>
<para>在这种情况下当客户端重新连接上服务器后ActiveMQ自动将客户端和会话与服务器端的会话重新连接起来。整个过程
对于客户端是完全透明的,在客户端就好像什么都没有发生一样。</para>
<para>具体工作原理如下:</para>
<para>客户端再向服务器发送命令时,它将每个命令保存到内存的一块缓存中。当连接出现故障时客户端会尝试与该服务
器恢复会话。做为恢复协议的一部分服务器在会话恢复时通知客户端最后一个成功接收的命令id。</para>
<para>根据这个命令id客户端可以判断它的缓存中是否有命令还未被服务器成功接收。如果有客户端可以重新发送
这些命令。</para>
<para>缓存的大小由<literal>ConfirmationWindowSize</literal>参数决定。当服务器成功接收了
<literal>ConfirmationWindowSize</literal>字节的命令时,会向客户端发送一个命令确认,以使客户端
及时清除缓存。</para>
<para>如果使用JMS服务并且JMS的连接工厂是注册到JNDI的话相应的参数是<literal
>activemq-jms.xml</literal>文件中的<literal
>confirmation-window-size</literal>项。如果你并不将JMS连接工厂注册到JNDI则你需要在
<literal>ActiveMQConnectionFactory</literal>上使用相应的方法直接设置该参数。</para>
<para>如果使用核心服务,你可以直接在<literal>ClientSessionFactory</literal>实例上直接设置该参数。</para>
<para>参数的单位是字节。</para>
<para>如果该参数是值设为<literal>-1</literal>,则关闭缓存,即关闭了重新恢复功能,迫使进行重新连接。默认
值是<literal>-1</literal>(表示没有自动恢复)。</para>
</section>
<section>
<title>会话重新连接</title>
<para>有时服务器发生故障后进行了重启。这时服务器将丢失所有当前的会话,上面所述的会话恢复就不能做到完全透明了。</para>
<para>在这种情况下ActiveMQ自动地重新建立连接并<emphasis role="italic">重新创建</emphasis>会话
和接收者。这一过程与向备份服务器进行失效备援failover完全一样。</para>
<para>客户重新连接的功能还用在其它一些模块上,如核心桥,以使它们能够重新连接到目标服务器上。</para>
<para>要全面理解事务性会话和非事务性会话在失效备援/重连接情况下的细节,以及如何保证<emphasis role="italic">
一次并且只有一次</emphasis>的消息传递,请参见<xref linkend="ha.automatic.failover"/>的有关内容。</para>
</section>
<section>
<title>重新连接/会话恢复的配置参数</title>
<para>下面是客户端用于重新连接的参数:</para>
<itemizedlist>
<listitem>
<para><literal>retry-interval</literal>。可选参数。它决定了两次重新连接尝试间隔的时间。单位
是毫秒。默认值是<literal>2000</literal>毫秒。</para>
</listitem>
<listitem>
<para><literal>retry-interval-multiplier</literal>。可选参数。它表示下一次重试时间间隔的
系数。即下一次重试的时间间隔是本次时间间隔乘以该参数。</para>
<para>这样可以实现重试间隔的<emphasis>指数延迟exponential backoff</emphasis></para>
<para>让我们看一个例子:</para>
<para>假设<literal>retry-interval</literal><literal>1000</literal> ms并且我们
<literal>retry-interval-multiplier</literal>设为<literal>2.0</literal>,如果
第一次尝试失败,则等待<literal>1000</literal>毫秒后进行第二次重试,如果再失败,则每三次重
试要在<literal>2000</literal>毫秒后进行,第四次要等待<literal>4000</literal>毫秒,
以此类推。</para>
<para>默认值是<literal>1.0</literal>,表示每次重试间隔相同的时间。</para>
</listitem>
<listitem>
<para><literal>max-retry-interval</literal>。可选参数。它决定了重试间的最大时间间隔。
使用<literal>retry-interval-multiplier</literal>可以使重试的时间间隔以指数级增加。
有可能造成时间间隔增加到一个非常大的数值。通过设置一个最大值可对其增长进行限制。默认
值是<literal>2000</literal>毫秒。</para>
</listitem>
<listitem>
<para><literal>reconnect-attempts</literal>。可选参数。它表示要进行多少重试后才放弃
并退出。<literal>-1</literal>表示进行无限次重试。默认值是<literal>0</literal></para>
</listitem>
</itemizedlist>
<para>如果使用JMS并且将JMS的连接工厂绑定到JNDI服务中则需要在<literal>activemq-jms.xml</literal>
文件中对这些参数进行配置,如下例所示:</para>
<programlisting>
&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;entry name="XAConnectionFactory"/&gt;
&lt;/entries&gt;
&lt;retry-interval&gt;1000&lt;/retry-interval&gt;
&lt;retry-interval-multiplier&gt;1.5&lt;/retry-interval-multiplier&gt;
&lt;max-retry-interval&gt;60000&lt;/max-retry-interval&gt;
&lt;reconnect-attempts&gt;1000&lt;/reconnect-attempts&gt;
&lt;/connection-factory&gt;
</programlisting>
<para>如果使用JMS但是直接实例化JMS连接工厂你可以使用适当的方法在 <literal
>ActiveMQConnectionFactory</literal> 对象上直接设置这些参数。</para>
<para>如果使用核心接口直接创建 <literal
>ClientSessionFactory</literal>实例,则用它的适当的方法可以设置这些参数。</para>
<para>如果客户端重新连接后发现会话已经丢失(如服务器重启或超时),则无法完成恢复。如果在连接上或会话上注册了
<literal>ExceptionListener</literal><literal>FailureListener</literal>
它们将会被通知。</para>
</section>
<section id="client-reconnection.exceptionlistener">
<title>ExceptionListeners and SessionFailureListeners</title>
<para>请注意当客户端进行重新连接或恢复会话时注册的JMS <literal
>ExceptionListener</literal> 或核心接口的 <literal>SessionFailureListener</literal>
将会被调用。</para>
</section>
</chapter>

View File

@ -1,550 +0,0 @@
<?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="clusters">
<title>集群</title>
<section>
<title>集群概述</title>
<para>ActiveMQ集群是由一组ActiveMQ服务器组成的集合它们协同合作进行消息处理。集群中每个主节点就是一个
ActiveMQ服务器它管理自己的连接并处理自己的消息。要将一个ActiveMQ服务器配置为集群服务器需要将配置
文件<literal>activemq-configuration.xml</literal><literal>clustered</literal>的值设
<literal>true</literal>。默认值是<literal>false</literal></para>
<para>要组成一个集群,每个节点都要在其核心配置文件<literal>activemq-configuration.xml</literal>
中声明<emphasis>集群连接</emphasis>,用来建立与集群中其它节点的通迅。每两个节点间都是通过内部的一个
<emphasis>核心桥</emphasis>(参见<xref linkend="core-bridges" />)连接的。这些连接的建立是
透明的--你不需要为每个连接显式地声明一个桥。集群连接的作用是在集群的各个节点间进行负载平衡。</para>
<para>ActiveMQ可以采用不同的拓扑结构来组成集群。本章后面将讲述几种常用的拓扑结构。</para>
<para>我们还将讨论客户端的负载均衡--客户端如何均衡其与集群各节点的连接,以及消息的再分配--在节点间合理
的分配消息以避免消息匮乏starvation</para>
<para>本章还涉及集群的另一个重要方面--<emphasis>服务器发现</emphasis>,即服务器通过广播的方式将
自己的连接信息告诉客户端或其它服务器,以使它们能与其建立连接,不需要额外的配置。</para>
</section>
<section id="clusters.server-discovery">
<title>服务器发现</title>
<para>服务器发现是指服务器通过广播的方式将自己的连接设置发送到网络上的机制,它有两个目的:</para>
<itemizedlist>
<listitem>
<para>被消息客户端发现。客户端接到广播后可以知道集群中有哪些服务器处于工作状态以及如何与它们
建立连接。虽然客户端<emphasis>可以</emphasis>可以在初始化时接受一个集群服务器的列表,
但是这样做与广播方式相比不够灵活。比如集群中有服务器离开或新加入时,列表的方式不能及时更新这些信息。</para>
</listitem>
<listitem>
<para>被其它服务器发现。通过广播,服务器之间可以自动建立彼此间的连接,不需要事先知道集群中其它
服务器的信息。</para>
</listitem>
</itemizedlist>
<para>服务器发现使用<ulink url="http://en.wikipedia.org/wiki/User_Datagram_Protocol"
>UDP</ulink>协议来广播连接设置。如果网络中UDP被关闭则不能使用服务器发现功能。只有用显式
地指定服务器的方法来设置集群或集群的客户端。</para>
<section id="clusters.broadcast-groups">
<title>广播组</title>
<para>服务器以广播组的方式来广播它的连接器信息。连接器定义了如何与该服务器建立连接的信息。关于连接器更多的
解释,请参见<xref linkend="configuring-transports" /></para>
<para>广播组包括了一系列的连接器对。每个连接器对由主服务器的连接器和备份(可选)服务器连接器信息组成。
广播组还定义了所使用的UDP的在址和端口信息。</para>
<para>广播组的配置中服务器配置文件<literal
>activemq-configuration.xml</literal>中。一个ActiveMQ服务器可以有多个广播组。所有的广播组
必需定义在<literal>broadcast-groups</literal>内。</para>
<para>让我们来看一个<literal>activemq-configuration.xml</literal>文件中广播组的例子:</para>
<programlisting>&lt;broadcast-groups>
&lt;broadcast-group name="my-broadcast-group">
&lt;local-bind-address>172.16.9.3&lt;/local-bind-address>
&lt;local-bind-port>54321&lt;/local-bind-port>
&lt;group-address>231.7.7.7&lt;/group-address>
&lt;group-port>9876&lt;/group-port>
&lt;broadcast-period>2000&lt;/broadcast-period>
&lt;connector-ref connector-name="netty-connector"
backup-connector="backup-connector"/>
&lt;/broadcast-group>
&lt;/broadcast-groups></programlisting>
<para>有些广播组的参数是可选的,通常情况下可以使用默认值。在上面例子中我们为了说明目的给出了这些参数。
下面是这些参数的说明:</para>
<itemizedlist>
<listitem>
<para><literal>name</literal>。每个广播组需要有一个唯一的名字。</para>
</listitem>
<listitem>
<para><literal>local-bind-address</literal>。这个参数是套接字的本地绑定地址。如果在服务器
中有多个网络接口卡时,必须要指定使用的是哪个接口。如果这个参数没有指定,那么将使用系统内核
所选定的IP地址。</para>
</listitem>
<listitem>
<para><literal>local-bind-port</literal>。这个参数指定了套接字的本地绑定端口。通常情况下
可以使用其默认值<literal>-1</literal>,表示使用随机的端口。这个参数总是和
<literal>local-bind-address</literal>一起定义。</para>
</listitem>
<listitem>
<para><literal>group-address</literal>。这个参数指定的是广播地址。它是一个D类的IP地址
取值范围是<literal>224.0.0.0</literal><literal>239.255.255.255</literal>
地址<literal>224.0.0.0</literal>是保留地址,所以不能使用。这个参数是必需指定。</para>
</listitem>
<listitem>
<para><literal>group-port</literal>。这个参数设定广播的UDP端口。
是一个必需指定的参数。</para>
</listitem>
<listitem>
<para><literal>broadcast-period</literal>。指定两次广播之间的时间间隔,单位毫秒。
这是一个可选参数,它的默认值是<literal>1000</literal>毫秒。</para>
</listitem>
<listitem>
<para><literal>connector-ref</literal>。这个参数指定了要广播的连接器以及可选的备份连接器。
(参见<xref linkend="configuring-transports" />)。
<literal>connector-name</literal>属性的值是连接器的名字,
<literal>backup-connector</literal>属性是备份连接器的名字,是可选属性。</para>
</listitem>
</itemizedlist>
</section>
<section id="clusters.discovery-groups">
<title>发现组</title>
<para>广播组规定了如何广播连接器的信息,发现组则定义的如何接收连接器的信息。</para>
<para>一个发现组包括了一系列的连接器对--每个连接器对代表一个不同的服务器广播的连接器信息。每当接收一次广播,
这个连接对的列表就被更新一次。</para>
<para>如果在一定时间内没有收到某个服务器的广播,则其相应的连接器对将从列表中删除。</para>
<para>发现组在ActiveMQ中有两处应用</para>
<itemizedlist>
<listitem>
<para>在创建集群连接时用来判断集群中哪些服务器是可以连接的。</para>
</listitem>
<listitem>
<para>客户端用来发现哪些服务器可以连接。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>在服务器端定义发现组。</title>
<para>服务器端的发现组定义在<literal>activemq-configuration.xml</literal>配置文件中。所有的发现组都必须
<literal>discovery-groups</literal>内定义。发现组可以定义多个。请看下面的例子:</para>
<programlisting>&lt;discovery-groups>
&lt;discovery-group name="my-discovery-group">
&lt;local-bind-address>172.16.9.7&lt;/local-bind-address>
&lt;group-address>231.7.7.7&lt;/group-address>
&lt;group-port>9876&lt;/group-port>
&lt;refresh-timeout>10000&lt;/refresh-timeout>
&lt;/discovery-group>
&lt;/discovery-groups></programlisting>
<para>下面是对每个参数的解释:</para>
<itemizedlist>
<listitem>
<para><literal>name</literal>属性。每个发现组都必须有一个唯一的名字。</para>
</listitem>
<listitem>
<para><literal>local-bind-address</literal>。如果你的主机有多个网络接口,你可能希望发现组只监听一个指定的
网络接口。这个参数就可以用于这个目的。它是一个可选参数。</para>
</listitem>
<listitem>
<para><literal>group-address</literal>。需要监听的广播地址。它需要与广播组的
<literal>group-address</literal>一致才可以收到广播组的信息。这是一个必要参数。</para>
</listitem>
<listitem>
<para><literal>group-port</literal>。需要监听的UDP端口。它需要与广播组的
<literal>group-port</literal>值相同才可以收到广播组的信息。这是一个必要参数。</para>
</listitem>
<listitem>
<para><literal>refresh-timeout</literal>。这个参数决定了在收到某个服务器的广播后,需要等待
多长时间下一次广播必须收到,否则将该服务器的连接器对从列表中删除。通常这个参数的值应该远大于
广播组的<literal>broadcast-period</literal>,否则会使服务器信息由于小的时间差异而丢失。
这个参数是可选的,它的默认值是<literal>10000</literal>毫秒10秒</para>
</listitem>
</itemizedlist>
</section>
<section id="clusters-discovery.groups.clientside">
<title>客户端的发现组</title>
<para>现在讨论如何配置ActiveMQ客户端来发现可以连接的服务器列表。使用JMS时所用的方法与使用核心接口时所用的
方法有所不同。</para>
<section>
<title>使用JMS时客户端发现的配置方法</title>
<para>如果使用JMS并且在服务器端的JMS连接工厂是注册到JNDI的情况下你可以在服务器端的配置文件
<literal>activemq-jms.xml</literal>中指定连接工厂所用的发现组。如下面所示:</para>
<programlisting>&lt;connection-factory name="ConnectionFactory">
&lt;discovery-group-ref discovery-group-name="my-discovery-group"/>
&lt;entries>
&lt;entry name="ConnectionFactory"/>
&lt;/entries>
&lt;/connection-factory></programlisting>
<para>其中<literal>discovery-group-ref</literal>的值是定义在
<literal>activemq-configuration.xml</literal>文件中的一个发现组。</para>
<para>当连接工厂从JNDI下载到客户端时使用它创建连接就会在列表中的服务器间进行负载均衡。
客户端通过监听发现组中的广播地址可以不断更新这个服务器列表。</para>
<para>如果使用JMS但是不用JNDI而是直接实例化JMS的连接工厂的话可以用适当的方法来设置发现组的各个
参数。如下所示:<programlisting>final String groupAddress = "231.7.7.7";
final int groupPort = 9876;
ConnectionFactory jmsConnectionFactory =
ActiveMQJMSClient.createConnectionFactory(groupAddress, groupPort);
Connection jmsConnection1 = jmsConnectionFactory.createConnection();
Connection jmsConnection2 = jmsConnectionFactory.createConnection();</programlisting></para>
<para><literal>refresh-timeout</literal>参数可以直接在连接工厂上使用
<literal>setDiscoveryRefreshTimeout()</literal>方法设置。</para>
<para>连接工厂还有一个方法<literal>setDiscoveryInitialWaitTimeout()</literal>。它可以设置连接工厂的
初始等待时间。当一个连接工厂被创建后立即进行用于创建连接的话,连接工厂可能没有足够的时间来接收各
个服务器发出的广播信息,也就无法建立完整的服务器列表。有了这个参数,连接工厂会在首次创建连接时
等待一定的时间,以接收广播。默认值是<literal>10000</literal>毫秒。</para>
</section>
<section>
<title>使用核心API的客户端的配置</title>
<para>如果使用核心接口直接创建<literal>ClientSessionFactory</literal>的实例,可以使用相应的方法
直接进行参数的设置,如:
<programlisting>
final String groupAddress = "231.7.7.7";
final int groupPort = 9876;
SessionFactory factory = ActiveMQClient.createClientSessionFactory(groupAddress, groupPort);
ClientSession session1 = factory.createClientSession(...); ClientSession
session2 = factory.createClientSession(...);
</programlisting>
</para>
<para>方法<literal>setDiscoveryRefreshTimeout()</literal>可以用来直接设置参数
<literal>refresh-timeout</literal></para>
<para>会话工厂还有一个方法<literal>setDiscoveryInitialWaitTimeout()</literal>。它可以设置会话工厂的
初始等待时间。当一个会话工厂被创建后立即进行用于创建连接的话,该会话工厂可能没有足够的时间来接收各
个服务器发出的广播信息,也就无法建立完整的服务器列表。有了这个参数,会话工厂会在首次创建连接时
等待一定的时间,以接收广播。默认值是<literal>10000</literal>毫秒。</para>
</section>
</section>
</section>
<section>
<title>服务器端消息的负载均衡</title>
<para>如果集群和各节点间定义了集群连接ActiveMQ可以对到达一个节点的消息进行负载均衡。</para>
<para>举一个简单的例子。一个集群有4个节点分别称为节点A、B、C和节点D。它们组成了一个
<emphasis>对称式集群</emphasis>(有关对称式集群参见<xref linkend="symmetric-cluster" />)。
在每个节点上部署了一个名为<literal>OrderQueue</literal>的队列。</para>
<para>一个客户端Ca连接到节点A并向其发送订单消息。客户端Pa、Pb、Pc和Pd分别连接到节点A、B、C和D并接收处理
这些订单消息。如果在节点A中没有定义集群连接那么订单消息都发送到节点A中的队列<literal>OrderQueue</literal>
中。因此只有连接到节点A的客户端Pa才能接收到订单消息。</para>
<para>如果在节点A定义了集群连接的话发送到节点A的消息被轮流round-robin从节点A分配到各个节点上的
<literal>OrderQueue</literal>队列中。这种消息分配完全在服务器端完成客户端只向节点A发送消息。</para>
<para>例如到达节点A的消息可能以下列顺序进行分配B、D、C、A、B、D、C、A、B、D。具体的顺序取决于节点启动的
先后但是其算法是不变的即round-robin</para>
<para>ActiveMQ集群连接在进行消息负载均衡时可以配置成统一负载均衡模式即不管各个节点上有无合适的接收者一律在
所有节点间进行消息的分配。也可以配置成为智能负载均衡模式,即只将消息分配到有合适接收者的节点上。这两种模式我们
都将举例说明。首先我们先介绍一般的集群连接配置。</para>
<section id="clusters.cluster-connections">
<title>配置集群连接</title>
<para>集群连接将一组服务器连接成为一个集群,消息可以在集群的节点之间进行负载均衡。集群连接的配置在
<literal>activemq-configuration.xml</literal>文件中的
<literal>cluster-connection</literal>内。一个ActiveMQ服务器可以有零个或多个集群连接。
下面是一个典型的例子:</para>
<programlisting>
&lt;cluster-connections&gt;
&lt;cluster-connection name="my-cluster"&gt;
&lt;address&gt;jms&lt;/address&gt;
&lt;retry-interval&gt;500&lt;/retry-interval&gt;
&lt;use-duplicate-detection&gt;true&lt;/use-duplicate-detection&gt;
&lt;forward-when-no-consumers&gt;false&lt;/forward-when-no-consumers&gt;
&lt;max-hops&gt;1&lt;/max-hops&gt;
&lt;discovery-group-ref discovery-group-name="my-discovery-group"/&gt;
&lt;/cluster-connection&gt;
&lt;/cluster-connections&gt;
</programlisting>
<para>上面给出了集群连接的所有可配置参数。在实际应用中有些你可以使用默认值,不必全部给出。</para>
<itemizedlist>
<listitem>
<para><literal>address</literal>。每个集群连接只服务于发送到以这个参数的值为开始的
地址的消息。</para>
<para>本例中的集群连接只对发往以<literal>jms</literal>为开始的地址的消息进行负载均衡的
处理。这个集群连接实际上能够处理所有JMS队列和话题的订阅中的消息这是国为所有JMS的队列
或订阅都映射到内核中以“jms“开头的队列。</para>
<para>这个地址可以为任何值而且可以配置多个集群连接每个连接的地址值可以不同。这样ActiveMQ
可以同时对不同地址同时进行消息的负载均衡。有的地址甚至可能在其它集群的节点中。这也就意谓着
一个ActiveMQ服务器可以同时参与到多个集群中。</para>
<para>要注意别造成多个集群连接的地址互相重复。比如地址“europe“和”europe.news“就互相重复
就会造成同一个消息会被多个集群连接进行分配,这样有可能发生重复传递。</para>
<para>本参数是必须指定的。</para>
</listitem>
<listitem>
<para><literal>retry-interval</literal>。如前所述,一个集群连接实际上在内部是用桥将两
个节点连接起来。如果集群连接已经创建但是目的节点还未启动,或正在重启,这时集群连接就会不断
重试与这个节点的连接,直到节点启动完毕连接成功为止。</para>
<para>这个参数决定了两次重试之间的时间间隔,单位是毫秒。它与桥的参数<literal>retry-interval</literal>
的含义相同(参见<xref linkend="core-bridges" />)。</para>
<para>这个参数是可选的,默认值是<literal>500</literal>毫秒。</para>
</listitem>
<listitem>
<para><literal>use-duplicate-detection</literal>。集群连接使用桥来连接各节点,而桥可以
通过配置向每个转发的消息添加一个重复id的属性。如果目的节点崩溃并重启消息可以被重新发送。
重复检测的功能就是在这种情况下将重复发送的消息进行过滤并丢弃。</para>
<para>这个参数与桥的参数<literal
>use-duplicate-detection</literal>相同。关于重复检测的更多信息,请参见
<xref linkend="duplicate-detection"/></para>
<para>这参数是可选的,默认值是<literal>true</literal></para>
</listitem>
<listitem>
<para><literal>forward-when-no-consumers</literal>。这个参数决定了是否向没有合适接收者
的节点分配消息。即不管有没有合适的接收者,消息在所有的节点间轮流分配。</para>
<para>如果这个参数设为<literal>true</literal>,则消息就会轮流在每个节点间分配,不管是否
节点上有没有相应的接收者(或者有接收者但是具有不匹配的选择器)。注意,如果其它节点中没有
与本节点同名的队列ActiveMQ不会将消息转发到那些节点中去不受本参数的限制。</para>
<para>如果参数设为<literal>false</literal> ActiveMQ中将消息转发到集群中那些有着适合接收者
的节点中。如果接收者有选择器,则至少有一个选择器与所转发的消息匹配才可,否则不转发。</para>
<para>本参数是可选的,默认值是<literal>false</literal></para>
</listitem>
<listitem>
<para><literal>max-hops</literal>。当一个集群连接在确定进行消息负载均衡的节点组时,这些
节点不一定是与本节点直接相连的节点。ActiveMQ可以通过其它ActiveMQ节点作为中介向那些非直接相
连的节点转发消息。</para>
<para>这样可以使ActiveMQ组成更加复杂的拓扑结构并且仍可提供消息的负载均衡。在本章的后面我们还要作
进一步的讨论。</para>
<para>本参数是可选参数,它的默认值是 <literal>1</literal>,表示消息只向直接相连的节点进行负载均衡。</para>
</listitem>
<listitem>
<para><literal>discovery-group-ref</literal>。这个参数决定了使用哪个发现组来获得集群服务器的列表。
集群连接与列表中的服务器建立连接。</para>
</listitem>
</itemizedlist>
</section>
<section id="clusters.clusteruser">
<title>集群用户的安全信息</title>
<para>当集群中两个节点建立连接时ActiveMQ使用一个集群用户和集群密码。它们定义在
<literal>activemq-configuration.xml</literal>文件中:</para>
<programlisting>
&lt;cluster-user&gt;HORNETQ.CLUSTER.ADMIN.USER&lt;/cluster-user&gt;
&lt;cluster-password&gt;CHANGE ME!!&lt;/cluster-password&gt;
</programlisting>
<warning><para>强烈建议在实际应用中不要使用默认的值,否则任意远程客户端会使用这些默认值连接到服务器上。当使用默认值时,
ActiveMQ会检测到并在每次启动的时候给出警告。</para></warning>
</section>
</section>
<section id="clusters.client.loadbalancing">
<title>客户端负载均衡</title>
<para>ActiveMQ的客户端负载均衡使同一个会话工厂每次创建一个会话时都连接到集群不同的节点上。这样可以使所的有会话
均匀分布在集群的各个节点上,而不会‘拥挤’到某一个节点上。</para>
<para>客户端负载均衡的策略是可配置的。ActiveMQ提供两种现成的负载均衡策略。你也可以实现自己的策略。</para>
<para>两种现成的策略是:</para>
<itemizedlist>
<listitem>
<para>轮流策略Round Robin。这个策略是先随机选择一个节点作为第一个节点然后依次选择各个节点。</para>
<para>例如一个顺序可能是 B, C, D, A, B, C, D, A, B另一个也可能是 D,
A, B, C, DA, B, C, D, A 或者 C, D, A, B, C, D, A, B, C, D, A等等。</para>
</listitem>
<listitem>
<para>随机策略。每次都是随机选择一个节点来建立会话。</para>
</listitem>
</itemizedlist>
<para>你可以实现自己的策略。只需要实现接口<literal
>org.apache.activemq.api.core.client.loadbalance.ConnectionLoadBalancingPolicy</literal>即可。</para>
<para>根据你使用的是JMS还是核心接口指定负载均衡的方法是有所不同的。如果你不指定策略默认的策略是<literal
>org.apache.activemq.api.core.client.loadbalance.RoundRobinConnectionLoadBalancingPolicy</literal>.</para>
<para>如果使用的是JMS并且JMS连接工厂注册到JNDI则你需要在<literal>activemq-jms.xml</literal>文件中定义策略,如:
<programlisting>
&lt;connection-factory name="ConnectionFactory"&gt;
&lt;discovery-group-ref discovery-group-name="my-discovery-group"/&gt;
&lt;entries&gt;
&lt;entry name="ConnectionFactory"/&gt;
&lt;/entries&gt;
&lt;connection-load-balancing-policy-class-name&gt;
org.apache.activemq.api.core.client.loadbalance.RandomConnectionLoadBalancingPolicy
&lt;/connection-load-balancing-policy-class-name&gt;
&lt;/connection-factory&gt;
</programlisting>上面的配置将部署一个连接工厂,它的连接负载均衡策略是随机策略。</para>
<para>如果使用JMS但是你在客户端是直接创建连接工厂的实例那么你需要用相应的方法在<literal
>ActiveMQConnectionFactory</literal>上直接设置:
<programlisting>
ConnectionFactory jmsConnectionFactory = ActiveMQJMSClient.createConnectionFactory(...);
jmsConnectionFactory.setLoadBalancingPolicyClassName("com.acme.MyLoadBalancingPolicy");
</programlisting></para>
<para>如果你使用核心接口的话,你要直接在<literal>ClientSessionFactory</literal>上设置策略:
<programlisting>
ClientSessionFactory factory = ActiveMQClient.createClientSessionFactory(...);
factory.setLoadBalancingPolicyClassName("com.acme.MyLoadBalancingPolicy");
</programlisting></para>
<para>连接工厂进行负载均衡的服务器组可以有两种方法来确定:</para>
<itemizedlist>
<listitem>
<para>显式指定服务器</para>
</listitem>
<listitem>
<para>使用发现组功能</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>显式指定集群服务器</title>
<para>有的网络并不开放UDP所以就不能使用服务器发现功能来获取服务器列表。</para>
<para>在这种情况下,可以显式地在每个节点或客户端指定服务器的列表。下面介绍如何做:</para>
<section>
<title>在客户端指定服务器列表</title>
<para>根据使用的是JMS还是核心接口所用的方法也不同。</para>
<section>
<title>使用JMS时指定服务器列表</title>
<para>如果使用JMS并且JMS连接工厂是注册到JNDI的话你需要在服务器端的配置文件
<literal>activemq-jms.xml</literal>中来指定,如下面的例子:</para>
<programlisting>&lt;connection-factory name="ConnectionFactory">
&lt;connectors>
&lt;connector-ref connector-name="my-connector1"
backup-connector-name="my-backup-connector1"/>
&lt;connector-ref connector-name="my-connector2"
backup-connector-name="my-backup-connector2"/>
&lt;connector-ref connector-name="my-connector3"
backup-connector-name="my-backup-connector3"/>
&lt;/connectors>
&lt;entries>
&lt;entry name="ConnectionFactory"/>
&lt;/entries>
&lt;/connection-factory></programlisting>
<para>其中的<literal>connection-factory</literal>内可以包含零或多个
<literal>connector-ref</literal>。每个<literal>connector-ref</literal>
都拥有<literal>connector-name</literal>属性和一个可选的<literal
>backup-connector-name</literal>属性。<literal
>connector-name</literal> 属性指向的是一个在<literal>activemq-configuration.xml</literal>
文件中定义的连接器。而<literal>backup-connector-name</literal>属性也是指向在
<literal>activemq-configuration.xml</literal>文件中定义的一个连接器。
有关连接器更多的信息参见<xref linkend="configuring-transports" /></para>
<para>连接工厂这样就保存有一组[连接器, 备份连接器]对,用于客户端在创建连接时的负载均衡。</para>
<para>如果你使用JMS但不使用JNDI你可以直接创建<literal>ActiveMQConnectionFactory</literal>
的实例,然后用相应的方法来设定连接器对列表,如下例:<programlisting>List&lt;Pair&lt;TransportConfiguration, TransportConfiguration>> serverList =
new ArrayList&lt;Pair&lt;TransportConfiguration, TransportConfiguration>>();
serverList.add(new Pair&lt;TransportConfiguration,
TransportConfiguration>(liveTC0, backupTC0));
serverList.add(new Pair&lt;TransportConfiguration,
TransportConfiguration>(liveTC1, backupTC1));
serverList.add(new Pair&lt;TransportConfiguration,
TransportConfiguration>(liveTC2, backupTC2));
ConnectionFactory jmsConnectionFactory = ActiveMQJMSClient.createConnectionFactory(serverList);
Connection jmsConnection1 = jmsConnectionFactory.createConnection();
Connection jmsConnection2 = jmsConnectionFactory.createConnection();</programlisting></para>
<para>上面的代码中我们创建了一组<literal>TransportConfiguration</literal>对象。每个
<literal>TransportConfiguration</literal>对象包括了如何连接某个特定服务器的信息。</para>
<para>然后,使用这个服务器列表创建了一个<literal>ActiveMQConnectionFactory</literal>实例。
这样通过这个工厂创建的连接就可以使用这个列表,由所用的客户连接负载均衡策略来进行连接的负载均衡。</para>
</section>
<section>
<title>使用核心接口指定服务器列表</title>
<para>如果使用核心接口,你可以直接在<literal>ClientSessionFactory</literal>实例上设置服务器列表。
如下例:</para>
<programlisting>List&lt;Pair&lt;TransportConfiguration, TransportConfiguration>> serverList =
new ArrayList&lt;Pair&lt;TransportConfiguration, TransportConfiguration>>();
serverList.add(new Pair&lt;TransportConfiguration,
TransportConfiguration>(liveTC0, backupTC0));
serverList.add(new Pair&lt;TransportConfiguration,
TransportConfiguration>(liveTC1, backupTC1));
serverList.add(new Pair&lt;TransportConfiguration,
TransportConfiguration>(liveTC2, backupTC2));
ClientSessionFactory factory = ActiveMQClient.createClientSessionFactory(serverList);
ClientSession sesison1 = factory.createClientSession(...);
ClientSession session2 = factory.createClientSession(...);</programlisting>
<para>在上面的代码中我们创建了一组<literal>ClientSessionFactoryImpl</literal>对象。每个
<literal>TransportConfiguration</literal>对象包括了如何连接某个特定服务器的信息。
有关信息请参见<xref linkend="configuring-transports" /></para>
<para>然后,使用这个服务器列表创建了一个<literal>ActiveMQConnectionFactory</literal>实例。
这样通过这个工厂创建的会话就可以使用这个列表,由所用的客户连接负载均衡策略来进行连接的负载均衡。</para>
</section>
</section>
<section id="clusters.static.servers">
<title>指定服务器列表以组成集群</title>
<para>下面我们考虑一个对称集群的例子,我们配置了每个集群连接,但是不使用发现功能来获得服务器信息。我们
采用配置的方法来显式指定集群的所有成员。</para>
<para>下面就是一个集群连接的配置:</para>
<programlisting>&lt;cluster-connections&gt;
&lt;cluster-connection name="my-explicit-cluster"&gt;
&lt;address&gt;jms&lt;/address&gt;
&lt;connector-ref connector-name="my-connector1"
backup-connector-name="my-backup-connector1"/>
&lt;connector-ref connector-name="my-connector2"
backup-connector-name="my-backup-connector2"/>
&lt;connector-ref connector-name="my-connector3"
backup-connector-name="my-backup-connector3"/>
&lt;/cluster-connection&gt;
&lt;/cluster-connections&gt;</programlisting>
<para><literal>cluster-connection</literal>中可以包括零或多个<literal>connector-ref</literal>,
每个<literal>connector-ref</literal>都有一个<literal>connector-name</literal>属性和
一个可选的<literal>backup-connector-name</literal>属性。<literal
>connector-name</literal>属性指向一个在<literal
>activemq-configuration.xml</literal>文件中定义的一个连接器,它是主连接器。可选的
<literal>backup-connector-name</literal>指向的也是在
<literal>activemq-configuration.xml</literal>文件中定义的一个连接器。
有关连接器的详细信息参见<xref linkend="configuring-transports" /></para>
<note>
<para>由于ActiveMQ 2.0.0的限制使用静态节点列表的集群不支持失效备援failover。要想支持失效备援
就必须使用发现组。</para>
</note>
</section>
</section>
<section id="clusters.message-redistribution">
<title>消息再分配</title>
<para>集群的另一个重要功能是消息的再分配。前面我们知道在服务器端可以对消息大集群节点间进行轮流方式的负载均衡。如果
<literal>forward-when-no-consumers</literal>参数为false消息将不会转发到那些没有相应接收者的节点中。
这样可以有效避免了消息被送到一个不可能被接收的节点上。但仍然有一个问题无法解决:就是如果在消息发到一个节点后,
它的接收者被关闭,那么这些消息仍然不能被接收了,造成了一种消息<emphasis>匮乏</emphasis>情形。
这种情况下如何处理?</para>
<para>这里就需要消息再分配功能。通过配置ActiveMQ可以将没有接收者的队列中的消息<emphasis>再次分配</emphasis>
到有接收者的节点上去。</para>
<para>通过配置,消息可以在队列最后一个接收者关闭时立即进行,也可以配置成等待一段时间再进行。默认消息再分配功能是
关闭的。</para>
<para>消息再分配功能可以基于地址进行配置,即在地址设置中指定再分配的延时。关于地址设置的更多信息,请参见
<xref linkend="queue-attributes" /></para>
<para>下面是从<literal>activemq-configuration.xml</literal>文件中提取的消息再分配的配置:</para>
<programlisting>&lt;address-settings>
&lt;address-setting match="jms.#">
&lt;redistribution-delay>0&lt;/redistribution-delay>
&lt;/address-setting>
&lt;/address-settings></programlisting>
<para>上面<literal>address-settings</literal>中设置的<literal
>redistribution-delay</literal>值为<literal>0</literal>。它适用于所有以“jms“开头的
地址。由于所有JMS队列与话题订阅都绑定到以”jms“为开头的地址所以上述配置的立即方式没有延迟消息
再分配适用于所有的JMS队列和话题订阅。</para>
<para><literal>match</literal>属性可以是精确匹配也可以使用通配符。通配符要符合ActiveMQ的通配符
语法(在<xref linkend="wildcard-syntax"
/>中描述)。</para>
<para><literal>redistribution-delay</literal>定义了队列最后一个接收者关闭后在进行消息再分配前所等待的
时间单位毫秒。如果其值是0表示立即进行消息再分配。<literal>-1</literal>表示不会进行消息再分配。
默认值是<literal>-1</literal></para>
<para>通常为消息分配定义一个延迟是有实际意义的。很多时候当一个接收者被关闭时,很快就会有一个新的接收者被创建。
在这种情况下加一延迟可以使消息继续在本地进行接收,而不会将消息转发到别处。</para>
</section>
<section>
<title>集群拓扑结构</title>
<para>ActiveMQ集群可以有多种拓扑结构。我们来看两个最常见的结构。</para>
<section id="symmetric-cluster">
<title>对称式集群</title>
<para>对称式集群可能是最常见的集群方式了。如果你接触过JBoss应用服务器的集群你就对这种方式很熟悉。</para>
<para>在一个对称集群中,每一个节点都与集群中其它任一节点相连。换句话说,集群中任意两个节点的连接都
只有一跳hop</para>
<para>要组成一个对称式的集群,每个节点在定义集群连接时要将属性<literal>max-hops</literal>
设为<literal>1</literal>。通常集群连接将使用服务器发现的功能来获得集群中其它服务器的连接
信息。当然在UDP不可用的时候也可以通过显式方式为集群连接指定服务器。</para>
<para>在对称集群中,每个服务器都知道集群中其它服务器中的所有队列信息,以及它们的接收者信息。利用这些
信息它可以决定如何进行消息的负载均衡及消息再分配。</para>
</section>
<section>
<title>链式集群</title>
<para>在链式集群中,并不是每个节点都与其它任何节点直接相连,而是由两个节点组成头和尾,其余节点在中间连接
成为一个链的结构。</para>
<para>比如有三个节点A、B和C。节点A在一个网络中它有许多消息的发送者向它发送订单消息。由于公司的政策订单
的接收者需要在另一个网络中接收消息,并且这个网络需要经过其它第三个网络才可以访问。这种情况下我们将节点
B部署到第三个网络中作为节点A与节点C的中间节点将两个节点连接起来。当消息到达节点A时被转发到节点B
然后又被转发到节点C上这样消息就被C上的接收者所接收。节点A不需要直接与节点C连接但是所有三个节点仍然
组成了一个集群。</para>
<para>要想组成一个这样的集群节点A的集群连接要指向节点B节点B的集群连接要指向C。本例我们只想组成一个单向
的链式集群即我们只将消息按节点A->B->C的方向流动而不要向 C->B->A方向流动。</para>
<para>对于这种集群拓扑,我们需要将<literal>max-hops</literal>设为<literal
>2</literal>. 这个值可以使节点C上队列的信息传送到节点B再传送到节点A。因此节点A就知道消息到达时即将
其转发给节点B。尽管节点B可能没有接收者可它知道再经过一跳就可以将消息转到节点C那里就有接收者了。</para>
</section>
</section>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -1,376 +0,0 @@
<?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="configuring-transports">
<title>传输层的配置</title>
<para>ActiveMQ的传输层是“可插拔的”。通过灵活的配置和一套服务提供接口SPIActiveMQ可以很容易地更换其传输层。</para>
<para>在本章中我们将对ActiveMQ的传输相关的概念作出解释并说明它的配置方法。</para>
<section id="configuring-transports.acceptors">
<title>接收器Acceptor</title>
<para>接收器(<emphasis>acceptor</emphasis>)是 ActiveMQ 的传输层中最为重要的概念之一。首先
介绍一下在文件<literal>activemq-configuration.xml</literal>中是怎样定义一个接收器的:</para>
<programlisting>
&lt;acceptors&gt;
&lt;acceptor name="netty"&gt;
&lt;factory-class&gt;
org.apache.activemq.core.remoting.impl.netty.NettyAcceptorFactory
&lt;/factory-class&gt;
&lt;param key="port" value="5446"/&gt;
&lt;/acceptor&gt;
&lt;/acceptors&gt;
</programlisting>
<para>所有接收器都在 <literal>acceptors</literal>单元element内定义。在<literal>acceptors</literal>
内可以有零个或多个接收器的定义。每个服务器所拥有的接收器的数量是没有限制的。</para>
<para>每个接收器都要定义其与ActiveMQ服务器连接的方式。</para>
<para>以上的例子中我们定义了一个<ulink
url="http://jboss.org/netty">Netty</ulink>接收器。它在端口<literal>5446</literal>监听连接请求。</para>
<para><literal>acceptor</literal>单元内有一个子单元<literal>factory-class</literal>。这个单元是用来
定义创建连接器的工厂类。一个连接器工厂类必须要实现<literal>AcceptorFactory</literal>接口。上例中我们定义
的连接器工厂是类NettyAcceptorFactory使用Netty来建立连接。有个这个类定义ActiveMQ就知道了用什么传输来建立连接了。</para>
<para><literal>acceptor</literal>中还可以配置零或多个参数<literal>param</literal>。在每个<literal>param</literal>
中定义的是键-值对key-value。这些参数用来配置某个传输实现。不同传输有不同的配置参数。</para>
<para>像IP地址、端口号等都是传输配置参数的例子。</para>
</section>
<section id="configuring-transports.connectors">
<title>连接器Connectors</title>
<para>接收器定义的是如何在服务器端接收连接,而连接器则是定义客户端如何连接到服务器。</para>
<para>以下是<literal>activemq-configuration.xml</literal>文件中一个连接器配置的例子。</para>
<programlisting>
&lt;connectors&gt;
&lt;connector name="netty"&gt;
&lt;factory-class&gt;
org.apache.activemq.core.remoting.impl.netty.NettyConnectorFactory
&lt;/factory-class&gt;
&lt;param key="port" value="5446"/&gt;
&lt;/connector&gt;
&lt;/connectors&gt;
</programlisting>
<para>连接器的配置在<literal>connectors</literal>单元中。可以定义一个或多个连接器。每个服务器配置的连接器
数量是没有限制的。</para>
<para>你可能注意到了,既然连接器是定义<emphasis>客户端</emphasis>如何连接服务器的,那么为什么要定义在
<emphasis>服务器</emphasis>端呢?原因如下:</para>
<itemizedlist>
<listitem>
<para>服务器有时也需要做为客户端去连接其它的服务器,比如当一个服务器通过桥连接到另一个服务器,或者是集群
中服务器之间的互相通迅。在这种情况下服务器就要知道如何与另一台服务器建立连接。因此需要在
<emphasis>connectors</emphasis>下定义连接器。</para>
</listitem>
<listitem>
<para>如果你使用JMS服务需要创建连接工厂的实例并绑定到JNDI。在ActiveMQ创建
<literal>ActiveMQConnectionFactory</literal>时需要连接器的必要信息,以便这个连接工厂
能知道它如何与ActiveMQ服务器相连接。</para>
<para>这一信息被定义在配置文件<literal
>activemq-jms.xml</literal>中的<literal>connector-ref</literal>单元下。下面这段配置
就是从该配置文件中提取的相关部分它展示了JMS的连接工厂是如何引用定义在配置文件<literal
>activemq-configuration.xml</literal>中的连接器的:</para>
<programlisting>
&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;entry name="XAConnectionFactory"/&gt;
&lt;/entries&gt;
&lt;/connection-factory&gt;
</programlisting>
</listitem>
</itemizedlist>
</section>
<section id="configuring-transports.client.side">
<title>在客户端直接配置传输层</title>
<para>怎样配置一个内核<literal>ClientSessionFactory</literal>以让它知道如何连接服务器的信息呢?</para>
<para>在直接配置内核<literal>ClientSessionFactory</literal>的时候,可以间接地使用连接器。当然在这种情况
下在服务器端定义连接器是没有意义的。我们通过将必要参数传给<literal>ClientSessionFactory</literal>
方法来告诉使用什么样的连接器工厂。</para>
<para>在下面的例子中,我们创建了一个<literal>ClientSessionFactory</literal>,它可以直接连接到我们先前定
义的接收器上。它使用的是标准的Netty TCP传输层连接主机是localhost默认端口5446</para>
<programlisting>
Map&lt;String, Object&gt; connectionParams = new HashMap&lt;String, Object&gt;();
connectionParams.put(org.apache.activemq.core.remoting.impl.netty.TransportConstants.PORT_PROP_NAME,
5446);
TransportConfiguration transportConfiguration =
new TransportConfiguration(
"org.apache.activemq.core.remoting.impl.netty.NettyConnectorFactory",
connectionParams);
ClientSessionFactory sessionFactory = ActiveMQClient.createClientSessionFactory(transportConfiguration);
ClientSession session = sessionFactory.createSession(...);
etc
</programlisting>
<para>如果在客户端直接使用JMS的连接工厂的话也可以用类似的方法而不需要在服务器端定义连接器或在
<literal>activemq-jms.xml</literal>配置文件中创建连接工厂:</para>
<programlisting>
Map&lt;String, Object&gt; connectionParams = new HashMap&lt;String, Object&gt;();
connectionParams.put(org.apache.activemq.core.remoting.impl.netty.TransportConstants.PORT_PROP_NAME, 5446);
TransportConfiguration transportConfiguration =
new TransportConfiguration(
"org.apache.activemq.core.remoting.impl.netty.NettyConnectorFactory",
connectionParams);
ConnectionFactory connectionFactory = ActiveMQJMSClient.createConnectionFactory(transportConfiguration);
Connection jmsConnection = connectionFactory.createConnection();
etc
</programlisting>
</section>
<section>
<title>配置 Netty 传输层</title>
<para>ActiveMQ当前使用<ulink url="http://www.jboss.org/netty/"
>Netty</ulink>作为其默认的连接层。Netty是一个高性能的底层网络库.</para>
<para>Netty传输的配置有几种不同的方法。它可以使用传统的Java IO阻塞方式、NIO非阻塞或直接使用
TCP socket及SSL。或者使用HTTP或HTTPS协议。同时还可能使用servlet进行传输。</para>
<para>采用Netty应该能满足绝大部分的传输要求。</para>
<section>
<title>配置 Netty TCP</title>
<para>Netty TCP 是简单的非加密的基于TCP socket的传输。它可以使用阻塞式的Java IO或非阻塞式的Java NIO。
我们建议在服务器端采用非阻塞式的NIO以获得良好的并发处理能力。当并发能力并不是很重要时可以使用阻塞式
的方式以增加响应的速度。</para>
<para>如果你的应用是运行在不信任的网络上你应该选择使用SSL或HTTPS。</para>
<para>Netty TCP的所有连接都是从客户端发起的。服务器端不向客户端发起任何连接。在有防火墙的环境中这种方式
是比较适合的。因为防火墙只允许单方向的连接。</para>
<para><literal>org.apache.activemq.core.remoting.impl.netty.TransportConstants</literal>类中定义了所
有的配置参数的名称key。它们当中绝大多娄既用于配置接收器也用于配置连接器有一些只适用于接收器。
下面列出的参数用以配置一个简单的Netty TCP</para>
<itemizedlist>
<listitem>
<para><literal>use-nio</literal>。如果设为<literal>true</literal>则使用非阻塞的Java
NIO。如果<literal>false</literal>则使用传统的阻塞方式的Java IO。</para>
<para>我们建议使用Java NIO处理并行连接。因为Java NIO不是为每一个连接分配一个线程所以它要比传统的阻塞式
Java IO具有更强的并发连接的处理能力。如果你不需要处理并发连接那么使用旧的阻塞式的IO性能会好一些。这个参
数的默认值在服务器端是<literal>false</literal>,在客户端是<literal>false</literal>
</para>
</listitem>
<listitem>
<para><literal>host</literal>。主机名或IP地址。对于接收器来说它是服务器接收连接的地址。
对于连接器端,它是客户端连接的目标地址。默认值是<literal>localhost</literal>
在配置接收器时可以指定多个主机名或IP地址中间用逗号隔开。如果指定的主机是<code>0.0.0.0</code>
则接收器将从主机上所有的网络接口中接受连接请求。连接器不允许指定多个主机地址,它只能与一个
地址建立连接。</para>
<note>
<para>一定不要忘记指定一个主机名或IP地址一个服务器要想接受来自其它节点的连接就必需有一个
主机名或IP地址来绑定及监听外部的连接请求。默认的主机名localhost是不能接受外部的
连接请求的!</para>
</note>
</listitem>
<listitem>
<para><literal>port</literal>。连接的端口。用于配置连接器或接收器。连接器用此端口来建立
连接。接收器在些端口上监听连接请求。默认值是<literal>5445</literal></para>
</listitem>
<listitem>
<para><literal>tcp-no-delay</literal>。将它设为<literal>true</literal>就会使用
<ulink url="http://en.wikipedia.org/wiki/Nagle's_algorithm">Nagle
算法</ulink>.默认值是<literal>true</literal></para>
</listitem>
<listitem>
<para><literal>tcp-send-buffer-size</literal>。这个参数指定了TCP的发送缓冲大小单位是字节。
默认值是<literal>32768</literal>字节(32KiB)。</para>
<para>这个参数要根据你的网络的带宽与时延的情况而调整。<ulink url="http://www-didc.lbl.gov/TCP-tuning/">
这个链接</ulink>对此有很好的论述。</para>
<para>简言之TCP的发送接收缓冲的大小可以用下面公式来计算</para>
<programlisting>
缓冲大小 = 带宽 * RTT
</programlisting>
<para>其中带宽的单位是 <emphasis>每秒字节数</emphasis>RTT网络往返程时间的单位是秒。
使用<literal>ping</literal>工具可以方便地测量出RTT。</para>
<para>对于快速网络可以适当加大缓冲的大小。</para>
</listitem>
<listitem>
<para><literal>tcp-receive-buffer-size</literal>。这个参数指定了TCP接收缓冲的大小单位是字节。
默认值是<literal>32768</literal>字节(32KiB)。</para>
</listitem>
<listitem>
<para><literal>batch-delay</literal>。ActiveMQ可以通过配置该参数在数据包写入传输层之前有一个
最大延时(毫秒),达到批量写入的目的。这样可以提高小消息的发送效率。但这样做会增加单个消息的平均发送
延迟。默认值为<literal>0</literal>毫秒。</para>
</listitem>
<listitem>
<para><literal>direct-deliver</literal>。消息到达服务器后,默认是由一个不同的线程来将消息传递
到接收者。这样可以使服务的呑吐量和可扩展性达到最佳,特别是在多核的系统上效果更为明显。但是线程切换
会带来一些传递的延迟。如果你希望延迟最小,并不在意呑吐量的话,可以将参数<literal
>direct-deliver</literal>设为true。默认值是<literal>true</literal>。如果你更希望有
较大的呑吐量的话,将它设为<literal>false</literal></para>
</listitem>
<listitem>
<para><literal>nio-remoting-threads</literal>。如果使用NIO默认情况下ActiveMQ会使用系统中处理
器内核(或超线程)数量三倍的线程来处理接收的数据包。内核的数量是通过调用<literal
>Runtime.getRuntime().availableProcessors()</literal>来得到的。如果你想改变这个数量,
你可以设定本参数。默认的值是<literal>-1</literal>,表示线程数为<literal
>Runtime.getRuntime().availableProcessors()</literal> * 3。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>配置Netty SSL</title>
<para>Netty SSL的配置与Netty TCP相似。它采用了安全套接字层SSL来提供加密的TCP连接。</para>
<para>我们提供了一个Netty SSL的例子来演示其配置和应用。</para>
<para>Netty SSL拥有Netty TCP一样的参数另外还有下列的附加参数</para>
<itemizedlist>
<listitem>
<para><literal>ssl-enabled</literal>。必须设为<literal>true</literal>以使用SSL。</para>
</listitem>
<listitem>
<para><literal>key-store-path</literal>。存放SSL密钥的路径key store)。这是存放客户端证书的地方。</para>
</listitem>
<listitem>
<para><literal>key-store-password</literal>。用于访问key store的密码。</para>
</listitem>
<listitem>
<para><literal>trust-store-path</literal>。服务器端存放可信任客户证书的路径。</para>
</listitem>
<listitem>
<para><literal>trust-store-password</literal>。用于访问可信任客户证书trust store)的密码。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>配置Netty HTTP</title>
<para>Netty HTTP 通过HTTP通道传送数据包。在有些用户环境中防火墙只允许有HTTP通信这时采用Netty HTTP作为ActiveMQ
的传输层就能解决问题。</para>
<para>我们提供了一个Netty HTTP的例子来演示其配置和应用。</para>
<para>Netty HTTP具有和Netty TCP同样的配置参数另外它还有以下参数</para>
<itemizedlist>
<listitem>
<para><literal>http-enabled</literal>。如果要使用HTTP这个参数必须设为<literal>true</literal></para>
</listitem>
<listitem>
<para><literal>http-client-idle-time</literal>。客户端空闲时间。如果客户端的空闲时间超过
这个值Netty就会发送一个空的HTTP请求以保持连接不被关闭。</para>
</listitem>
<listitem>
<para><literal>http-client-idle-scan-period</literal>。扫描空闲客户端的间隔时间。单位是毫秒。</para>
</listitem>
<listitem>
<para><literal>http-response-time</literal>。服务器端向客户端发送空的http响应前的最大等待时间。</para>
</listitem>
<listitem>
<para><literal>http-server-scan-period</literal>。服务器扫描需要响应的客户端的时间间隔。单位是毫秒。</para>
</listitem>
<listitem>
<para><literal>http-requires-session-id</literal>。如果设为true客户端在第一次请求后将等待
接收一个会话ID。http 连接器用它来连接servlet接收器不建议这样使用</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>配置Netty Servlet</title>
<para>ActiveMQ可以使用Netty servlet来传输消息。使用servlet可以将ActiveMQ的数据通过HTTP传送到一个
运行的servlet再由servlet转发给ActiveMQ服务器。</para>
<para>servlet与HTTP的不同之处在于当用HTTP传输时ActiveMQ如同一个web服务器它监听在某个端口上的HTTP
请求并返回响应。比如80端口或8080端口。而当使用servlet时ActiveMQ的传输数据是通过运行在某一servlet容器
中的一个特定的servlet来转发的。而这个sevlet容器中同时还可能运行其他的应用如web服务。当一个公司有多个应用
但只允许一个http端口可以访问时servlet传输可以很好的解决ActiveMQ的传输问题。</para>
<para>请参见ActiveMQ所提供的servlet例子来了解详细的配置方法。</para>
<para>要在ActiveMQ中使用Netty servlet传输方式需要以下步骤</para>
<itemizedlist>
<listitem>
<para>部署servlet。下面是一个web.xml例子</para>
<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?>
&lt;web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
&lt;servlet>
&lt;servlet-name>ActiveMQServlet&lt;/servlet-name>
&lt;servlet-class>org.jboss.netty.channel.socket.http.HttpTunnelingServlet&lt;/servlet-class>
&lt;init-param>
&lt;param-name>endpoint&lt;/param-name>
&lt;param-value>local:org.apache.activemq&lt;/param-value>
&lt;/init-param>
&lt;load-on-startup>1&lt;/load-on-startup>
&lt;/servlet>
&lt;servlet-mapping>
&lt;servlet-name>ActiveMQServlet&lt;/servlet-name>
&lt;url-pattern>/ActiveMQServlet&lt;/url-pattern>
&lt;/servlet-mapping>
&lt;/web-app>
</programlisting>
</listitem>
<listitem>
<para>我们还需要在服务器端加上一个特殊的Netty invm 接收器。</para>
<para>下面是从<literal>activemq-configuration.xml</literal>配置文件中摘取的定义接收器的配置部分:</para>
<programlisting>
&lt;acceptors>
&lt;acceptor name="netty-invm">
&lt;factory-class>
org.apache.activemq.core.remoting.impl.netty.NettyAcceptorFactory
&lt;/factory-class>
&lt;param key="use-invm" value="true"/>
&lt;param key="host" value="org.apache.activemq"/>
&lt;/acceptor>
&lt;/acceptors>
</programlisting>
</listitem>
<listitem>
<para>最后我们需要在客户端配置连接器,也是在<literal>activemq-configuration.xml</literal>文件中来做。如下所示:</para>
<programlisting>&lt;connectors>
&lt;connector name="netty-servlet">
&lt;factory-class>
org.apache.activemq.core.remoting.impl.netty.NettyAcceptorFactory
&lt;/factory-class>
&lt;param key="host" value="localhost"/>
&lt;param key="port" value="8080"/>
&lt;param key="use-servlet" value="true"/>
&lt;param key="servlet-path" value="/messaging/ActiveMQServlet"/>
&lt;/connector>
&lt;/connectors></programlisting>
</listitem>
</itemizedlist>
<para>下面列出了初始化参数以及它们的用途:</para>
<itemizedlist>
<listitem>
<para>endpoint - Netty接收器的名字。servlet将向它转发数据包。它与<literal
>host</literal>参数的值是对应的。</para>
</listitem>
</itemizedlist>
<para><literal>web.xml</literal>中定义的servlet的URL形式与在连接器配置文件中定义的
<literal>servlet-path</literal>值应该相匹配。</para>
<para>servlet可以与SSL一起使用。只需要在连接器配置中加上下面的配置即可<programlisting> &lt;connector name="netty-servlet">
&lt;factory-class>org.apache.activemq.core.remoting.impl.netty.NettyAcceptorFactory&lt;/factory-class>
&lt;param key="host" value="localhost"/>
&lt;param key="port" value="8443"/>
&lt;param key="use-servlet" value="true"/>
&lt;param key="servlet-path" value="/messaging/ActiveMQServlet"/>
&lt;param key="ssl-enabled" value="true"/>
&lt;param key="key-store-path" value="path to a keystoree"/>
&lt;param key="key-store-password" value="keystore password"/>
&lt;/connector>
</programlisting></para>
<para>另外你还需要为服务器指定一个KeyStore。打开<literal>server/default/deploy/jbossweb.sar</literal>
下的<literal>server.xml</literal>文件按照下面的内容编辑其中的SSLTLS连接器配置<programlisting>&lt;Connector protocol="HTTP/1.1" SSLEnabled="true"
port="8443" address="${jboss.bind.address}"
scheme="https" secure="true" clientAuth="false"
keystoreFile="path to a keystore"
keystorePass="keystore password" sslProtocol = "TLS" />
</programlisting>SSL需要keystore和访问密码。参见servlet ssl例子以了解更多的有关信息。</para>
</section>
</section>
</chapter>

View File

@ -1,139 +0,0 @@
<?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="connection-ttl">
<title>失效连接的检测</title>
<para>本章将讨论连接的生存时间TTL以及ActiveMQ如何处理出现故障的客户端或者异常退出的客户端即客户端在
退出时没有合理的关闭相关资源)。</para>
<section id="dead.connections">
<title>服务器端对失效连接的清除</title>
<para>当客户端的应用程序退出时,应该关闭所使用的资源。在<literal>finally</literal>进行资源的关闭
是一个很好的方法。</para>
<para>下面的例子中一个Hornet客户端在finally中关闭了它的会话session和会话工厂session factory</para>
<programlisting>
ClientSessionFactory sf = null;
ClientSession session = null;
try
{
sf = ActiveMQClient.createClientSessionFactory(...);
session = sf.createSession(...);
... do some stuff with the session...
}
finally
{
if (session != null)
{
session.close();
}
if (sf != null)
{
sf.close();
}
}
</programlisting>
<para>下面的例子给出了一个JMS客户端是如何适当关闭相关资源的</para>
<programlisting>
Connection jmsConnection = null;
try
{
ConnectionFactory jmsConnectionFactory = ActiveMQJMSClient.createConnectionFactory(...);
jmsConnection = jmsConnectionFactory.createConnection();
... do some stuff with the connection...
}
finally
{
if (connection != null)
{
connection.close();
}
}
</programlisting>
<para>然而有时候资源在客户端得不到合理的关闭。有的客户端应用在结束时忘记了关闭资源,有的客户端有时发生故障导致
程序突然中断,相关资源也没有来得及关闭!</para>
<para>如果上述情况发生了,那么这些资源就会留在服务器端而不会被清理。这就会造成资源泄漏现象并最終导致服务器内存
溢出或其它资源的溢出错误。</para>
<para>因此在服务器端要有某种机制来避免资源的泄漏。也就是对无效资源进行回收。在判断什么是无效资源时ActiveMQ
考虑到了客户端重新连接的情况。就是当一个连接由于网络临时中断后又恢复正常时,客户端有可能通过不断重试
成功地连接到服务器端。如果服务器端过早清除了相关的连接资源,则客户端就可能重试失败。</para>
<para>ActiveMQ的资源回收是完全可配置的。每个 <literal
>ClientSessionFactory</literal> 有一个<emphasis>连接 TTL</emphasis>的参数。
这个参数的意义是当客户端的一个连接没有任何数到达服务器时,服务器充许这个连接有效的最长时间。客户端通过定
时向服务器端发送“ping“数据包来维持连接的有效以免被服务器关掉。如果服务器在TTL指定的时间内没有收到任何
数据包则认为该连接无效继而关闭与该连接相关的所有的会话session</para>
<para>如果使用JMS<literal>ActiveMQConnectionFactory</literal><literal>ConnectionTTL</literal>
属性是用来定义连接的存活时间的。如果你将JMS连接工厂部署到JNDI中则应使用配置文件中的<literal
>connection-ttl</literal>参数来定义连接的TTL。</para>
<para>默认的连接TTL值是<literal>60000</literal>毫秒,即一分钟。 <literal>ConnectionTTL</literal>
设为<literal>-1</literal>表示服务器永远不检测超时的连接。</para>
<para>如果你不想让客户端来规定连接存活时间TTL你可以在服务器端的配置文件中定义
<literal>connection-ttl-override</literal>属性。它的默认值是<literal>-1</literal>,表示
服务器端该属性无效即客户端可以定义自己的连接TTL</para>
<section>
<title>关闭没有被成功关闭的核心会话或JMS连接</title>
<para>如前所述,在使用完毕后在<literal>finally</literal>中将所有的核心会话或JMS连接关闭是十分重要的。</para>
<para>如果你没有这样做ActiveMQ会在拉圾回收时进行检测并会在日志中打印类似以下的警告如果是JMS则在警告中
是相应的JMS连接</para>
<programlisting>
[Finalizer] 20:14:43,244 WARNING [org.apache.activemq.core.client.impl.DelegatingSession] I'm closin
g a ClientSession you left open. Please make sure you close all ClientSessions explicitly before let
ting them go out of scope!
[Finalizer] 20:14:43,244 WARNING [org.apache.activemq.core.client.impl.DelegatingSession] The sessi
on you didn't close was created here:
java.lang.Exception
at org.apache.activemq.core.client.impl.DelegatingSession.&lt;init&gt;(DelegatingSession.java:83)
at org.acme.yourproject.YourClass (YourClass.java:666)
</programlisting>
<para>ActiveMQ然后将未关闭的连接会话关闭。</para>
<para>注意在日志的警告中还给出了创建JMS连接客户端会话的具体行号以便准确地确定出错的地方。</para>
</section>
</section>
<section>
<title>客户端的故障检测</title>
<para>前面讲述了客户端如何向服务器发送ping以及服务器端如何清理失效的连接。发送ping还有另外一个目的就是能
让客户端检测网络或服务器是否出现故障。</para>
<para>从客户端的角度来看,只要客户端能从一个连接不断接收服务器的数据,那么这个连接就被认为是一个有效的连接。</para>
<para>如果在属性<literal>client-failure-check-period</literal>所定义的时间内(单位毫秒)客户端没有
收到任何数据客户端就认为这们连接发生了故障。根据不同的配置客户端在这种情况下要么进行failover要么
调用<literal>FailureListener</literal>的接口或者是JMS的<literal>ExceptionListener</literal>)。</para>
<para>如果使用JMS这个参数是<literal>ActiveMQConnectionFactory</literal><literal>ClientFailureCheckPeriod</literal>
如果你向JNDI部署JMS连接工厂那么相应的参数在<literal>activemq-jms.xml</literal>配置文件中,参数名
<literal>client-failure-check-period</literal></para>
<para>这个参数的默认值是<literal>30000</literal>毫秒,即半分钟。<literal>-1</literal>表示客户端不检查
连接的有效性。即不论是否有数据来自服务器连接都一直有效。这一参数通常要比服务器端的连接TTL小许多以使
客户端在出现短暂连接故障的情况下可以与服务器成功地重新连接。</para>
</section>
<section id="connection-ttl.async-connection-execution">
<title>配置异步连接任务执行</title>
<para>默认情况下,服务器接收到的数据包被远程模块的线程处理。</para>
<para>为了避免远程模块的线程被长时间占用,数据包可以转给另外的一个线程池来处理。要注意这样做的增加了一些时间延迟。
因此如果数据包处理耗时很少,还是由远程模块线程来处理较好。
要配置这样的异步连接很行任务,将<literal>activemq-configuration.xml</literal>文件中的
<literal>async-connection-execution-enabled</literal> 参数设为<literal>true</literal>
(默认值是 <literal>true</literal>)。</para>
</section>
</chapter>

View File

@ -1,179 +0,0 @@
<?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="core-bridges">
<title>核心桥</title>
<para>桥的功能是从一个源队列中接收消息再将消息转发到目的地址。通常这个目的地址在另外一个ActiveMQ服务器中。</para>
<para>源与目的不需要在同一个集群中。所以桥很适合将消息从一个集群中可靠地转发到另一个集群。比如通过一个WAN
internet等连接不稳定的网络。</para>
<para>桥有处理故障的能力。如果目的服务器的连接失败(像网络故障),桥会重试与目的服务器的连接,直接连接成功
为止。当连接成功后,桥则继续进行工作。</para>
<para>总之桥是可靠连接两个ActiveMQ服务器的一种手段。使用核心桥时源和目的服务器必须都是ActiveMQ服务器。</para>
<para>桥可以通过配置提供<emphasis>一次且只有一次</emphasis>的传递保证。其采用的方法是重复检测(详细
描述在<xref linkend="duplicate-detection"/>)。</para>
<note>
<para>核心桥的功能与JMS桥的功能相似但是不能将它们混淆</para>
<para>核心桥用来连接两个ActiveMQ节点它不使用JMS接口。JMS桥使用的是JMS接口它连接的是任何两个符合
JMS 1.1规范的服务器。因此JMS桥可以将两个不同的JMS服务器连接起来。从性能角度考虑核心桥由于采用
重复检测来实现<emphasis>一次且只一次</emphasis>的传递保证,可以提供更高的性能。
JMS桥则需要使用XA这种复杂的机制来提供同样的传递保证因些性能要比核心桥低。</para>
</note>
<section>
<title>桥的配置</title>
<para>桥的配置在<literal>activemq-configuration.xml</literal>文件中。让我们先看一个配置的例子
它实际上出自bridge例子</para>
<programlisting>
&lt;bridge name="my-bridge"&gt;
&lt;queue-name&gt;jms.queue.sausage-factory&lt;/queue-name&gt;
&lt;forwarding-address&gt;jms.queue.mincing-machine&lt;/forwarding-address&gt;
&lt;filter-string="name='aardvark'"/&gt;
&lt;transformer-class-name&gt;
org.apache.activemq.jms.example.HatColourChangeTransformer
&lt;/transformer-class-name&gt;
&lt;retry-interval&gt;1000&lt;/retry-interval&gt;
&lt;retry-interval-multiplier&gt;1.0&lt;/retry-interval-multiplier&gt;
&lt;reconnect-attempts&gt;-1&lt;/reconnect-attempts&gt;
&lt;failover-on-server-shutdown&gt;false&lt;/failover-on-server-shutdown&gt;
&lt;use-duplicate-detection&gt;true&lt;/use-duplicate-detection&gt;
&lt;confirmation-window-size&gt;10000000&lt;/confirmation-window-size&gt;
&lt;connector-ref connector-name="remote-connector"
backup-connector-name="backup-remote-connector"/&gt;
&lt;user&gt;foouser&lt;/user&gt;
&lt;password&gt;foopassword&lt;/password&gt;
&lt;/bridge&gt;
</programlisting>
<para>在上面的配置中包括了桥的所有参数。在实际应用中可能其中很多的参数可以使用默认值,不需要在配置中
指定。</para>
<para>下面我们对每个参数分别说明:</para>
<itemizedlist>
<listitem>
<para><literal>name</literal>参数。所有桥都必须有一个唯一的名字。</para>
</listitem>
<listitem>
<para><literal>queue-name</literal>。本地队列的名字。桥从本地队列中接收消息。
这是一个必要的参数。</para>
<para>这个队列在桥的启动之前必须已经存在。</para>
<note>
<para>如果使用JMSJMS的配置文件<literal
>activemq-jms.xml</literal>在核心配置文件<literal>
activemq-configuration.xml</literal>之后装载。所以如果你的桥要从JMS
队列接收消息就需要保证JMS队列同时要作为核心队列部署。具体方法可以参见
bridge例子。</para>
</note>
</listitem>
<listitem>
<para><literal>forwarding-address</literal>。目的服务器中的地址。消息将被转发到这个地址。
如果没有指定这个转发地址,消息的原始地址将会保留。</para>
</listitem>
<listitem>
<para><literal>filter-string</literal>。一个可选的过滤器表达式。它表示只有过滤器表达式选择
的消息才被转发。过滤器表达式的语法参见 <xref linkend="filter-expressions"
/>。</para>
</listitem>
<listitem>
<para><literal>transformer-class-name</literal>。可选的转换器类名。这是一个用户定义的
类,它需要实现接口<literal>org.apache.activemq.core.server.cluster.Transformer</literal>
</para>
<para>如果指定了这个类,每当一个消息被转发之前,它的<literal>transform()</literal>方法
就会被调用。用户利用这个机会可以对消息本身或消息头信息进行修改。</para>
</listitem>
<listitem>
<para><literal>retry-interval</literal>。这个可选参数决定了在进行连接重试时,两次重试
之间的时间间隔。默认值是<literal>2000</literal>毫秒。</para>
</listitem>
<listitem>
<para><literal>retry-interval-multiplier</literal>。这个可选参数基于前一次重试连接
的时间间隔来计算下一次重试的间隔,即前一次的间隔乘以该参数。</para>
<para>这样可以实现重试间隔的<emphasis>指数延迟exponential backoff</emphasis></para>
<para>让我们看一个例子:</para>
<para>假设<literal>retry-interval</literal><literal>1000</literal> ms并且我们
<literal>retry-interval-multiplier</literal>设为<literal>2.0</literal>,如果
第一次尝试失败,则等待<literal>1000</literal>毫秒后进行第二次重试,如果再失败,则每三次重
试要在<literal>2000</literal>毫秒后进行,第四次要等待<literal>4000</literal>毫秒,
以此类推。</para>
<para>默认值是<literal>1.0</literal>,表示每次重试间隔相同的时间。</para>
</listitem>
<listitem>
<para><literal>reconnect-attempts</literal>。可选参数。它表示要进行多少重试后才放弃
并退出。<literal>-1</literal>表示进行无限次重试。默认值是<literal>1</literal></para>
</listitem>
<listitem>
<para><literal>failover-on-server-shutdown</literal>。可选参数。它指定了当目的服务器正常
退出时桥是否尝试失效备援failover到备份服务器如果配置了的话上。</para>
<para>桥的连接器可以配置一个主服务器和一个备份服务器。如果配置了备份服务器,并且这个参数是
<literal>true</literal>时,在主服务器正常退出时,桥会自动地连接到备份服务器上继续工作。
如果桥的连接器没有配置备份服务器,则这个参数不起作用。</para>
<para>你的桥配置了备份服务器后,有时你需要临时将主服务器关闭进行一些维护,此时并不希望桥连接到备份服务
器中。使用该参数就可以达到这个目的。</para>
<para>这个参数的默认值是<literal>false</literal></para>
</listitem>
<listitem>
<para><literal>use-duplicate-detection</literal>。可选参数。它控制桥是否在转发的消息中自动
添加一个重复ID的属性。</para>
<para>添加这样一个属性可以使目的服务器对来自源服务器的消息进行重复检测。当出现连接故障或服务器崩溃时,
桥在恢复时将重新转发那些没有被通知的消息。这在目的服务器端有可能造成重复发送。使用重复检测功能,可
以将重复发送的消息过滤掉。</para>
<para>使用这个功能,服务器就可以保证 <emphasis>一次并且只有一次</emphasis>的传递,而不需要使用
重量级的方法如XA参见 <xref
linkend="duplicate-detection"/>)。</para>
<para>默认的值是<literal>true</literal>.</para>
</listitem>
<listitem>
<para><literal>confirmation-window-size</literal>。这个可选参数决定了向目的服务器转发消息时
所使用的确认窗口的大小。详细的描述在<xref linkend="client-reconnection"/></para>
<para>
<warning>当桥从一个设置了max-size-bytes参数的队列接收并转发消息时一个重要的事情就是要将
confirmation-window-size的值设置为小于等于
<literal>max-size-bytes</literal>的值,以避免造成消息流的停止。</warning>
</para>
</listitem>
<listitem>
<para><literal>connector-ref</literal>。这是一个必需的参数。它指定了桥用来连接目的服务器的
<emphasis>连接器</emphasis></para>
<para><emphasis>connector</emphasis>包含了所用的传输TCP, SSL, HTTP等以及服务器连接参数
(如主机名,端口等)。关于连接器的详细信息请参见(<xref linkend="configuring-transports"/>)。</para>
<para><literal>connector-ref</literal>有两个参数:</para>
<itemizedlist>
<listitem>
<para><literal>connector-name</literal>。这个指的是核心配置文件<literal
>activemq-configuration.xml</literal>中定义的连接器的名字。桥使用
这个连接器创建与目的服务器的连接。这个参数是必需指定的。</para>
</listitem>
<listitem>
<para><literal>backup-connector-name</literal>。这个可选参数同样指定一个在核心
配置文件<literal>activemq-configuration.xml</literal>中定义的连接器名字。
当目的服务器出现故障时,或者正常退出但是参数<literal
>failover-on-server-shutdown</literal>的值设为<literal
>true</literal>桥使用这个参数指定的连接器通过失效备援failover连接
到备用的服务器。</para>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para><literal>user</literal>。这个可选参数指定了桥在创建与远程服务器连接时所用的用户名。如果
没有指定用户名,在配置文件<literal>activemq-configuration.xml</literal>
<literal>cluster-user</literal>所定义的默认集群用户名将被使用。 </para>
</listitem>
<listitem>
<para><literal>password</literal>。这个可选的参数给出的是桥创建与远程服务器连接所使用的密码。
如果没有指定密码,在配置文件<literal>activemq-configuration.xml</literal>
<literal>cluster-password</literal>所定义的默认集群密码将被使用。</para>
</listitem>
</itemizedlist>
</section>
</chapter>

View File

@ -1,86 +0,0 @@
<?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="diverts">
<title>消息的转发divert)与分流</title>
<para>在ActiveMQ中可以配置一些称为转发器<emphasis>diverts</emphasis>)的对象。</para>
<para>转发器可以将路由到一个地址的消息透明地转发到其它的地址去,不需要客户端的参与。</para>
<para>转发器可以是<emphasis>唯一exclusive</emphasis>的,即消息只转发到新的地址,不发到原
来的地址;也可以是<emphasis>不唯一non-exclusive</emphasis>的,即消息在发往原有地址的
同时,它的一个<emphasis>拷贝</emphasis>被发往新的地址。不唯一的转发器可以在应用中将消息进行
<emphasis>分流splitting</emphasis>。比如在一个订单系统中它可以用于监视发往订单队列中
的每个订单消息。</para>
<para>转发器还可以带一个可选的消息选择器。只有被选择器选择的消息才会被转发。</para>
<para>转发器还可以带有一个<literal>转换器Transformer</literal>。它可以将消息进行转换。</para>
<para>转发器只在同一个服务器中的地址间进行转发。如果要向另外服务器中的地址进行转发,可以采用转发器与桥配合
来实现。先将消息通过转发器转发到一个存储与转发的队列中,再由一个桥将这个队列的消息转发到远程服务器的目的
地址中。</para>
<para>由转发器与桥进行配合可以组成复杂的路由系统。在服务器中由一组转发器可以形成一个消息路由表。如果加上桥,就
可以进一步形成一个分布式的可靠的消息路由网。</para>
<para>转发器的配置在<literal>activemq-configuration.xml</literal>中定义。可以配置零个或多个转发器。</para>
<para>参见转发器的例子<xref linkend="divert-example" />,它展示了如何配置与使用转发器。</para>
<para>让我们看一些转发器的配置例子:</para>
<section>
<title>唯一式转发器</title>
<para>下面是一个唯一式转发器的例子。它将所有符合条件的消息转发到新的地址,而旧的地址将不能得到这些消息。</para>
<para>以下配置来自于divert例子</para>
<programlisting>
&lt;divert name="prices-divert"&gt;
&lt;address&gt;jms.topic.priceUpdates&lt;/address&gt;
&lt;forwarding-address&gt;jms.queue.priceForwarding&lt;/forwarding-address&gt;
&lt;filter string="office='New York'"/&gt;
&lt;transformer-class-name&gt;
org.apache.activemq.jms.example.AddForwardingTimeTransformer
&lt;/transformer-class-name&gt;
&lt;exclusive&gt;true&lt;/exclusive&gt;
&lt;/divert&gt;
</programlisting>
<para>在这里我们定义了一相名为“<literal>prices-divert</literal>”的转发器,它将发往
<literal>jms.topic.priceUpdates</literal>对应JMS话题<literal
>priceUpdates</literal>)的消息转向另一个本地地址“<literal
>jms.queue.priceForwarding</literal>对应JMS队列
<literal>priceForwarding</literal>)。</para>
<para>我们还配置了一相消息过滤器。只有<literal>office</literal>属性值为<literal>New York</literal>
的消息才被转发到新地址,其它消息则继续发往原地址。如果没有定义过滤器,所有消息将被转发。</para>
<para>本例中还配置了一个转换器的类。当每转发一个消息时,该转换器就被执行一次。转换器可以对消息在转发前进行
更改。这里的转换器只是在消息中加入了一个记录转发时间的消息头。</para>
<para>本例中消息被转发到一个存贮与转发是队列然后通过一个桥将消息转发到另一个ActiveMQ服务器中。</para>
</section>
<section>
<title>不唯一转发器</title>
<para>下面我们来看一个不唯一的转发器。不唯一转发器将消息的<emphasis>拷贝</emphasis>转发到新的地址中,
原消息则继续发往原有地址。</para>
<para>因此不唯一转发器可以将消息进行分流splitting</para>
<para>不唯一转发器的配置与唯一转发器的配置中一样的,也可以带一个可选的过滤器和转换器。下面的配置也是出自
divert例子</para>
<programlisting>
&lt;divert name="order-divert"&gt;
&lt;address&gt;jms.queue.orders&lt;/address&gt;
&lt;forwarding-address&gt;jms.topic.spyTopic&lt;/forwarding-address&gt;
&lt;exclusive&gt;false&lt;/exclusive&gt;
&lt;/divert&gt;
</programlisting>
<para>The above divert example takes a copy of every message sent to the address '<literal
>jms.queue.orders</literal>' (Which corresponds to a JMS Queue called '<literal
>orders</literal>') and sends it to a local address called '<literal
>jms.topic.SpyTopic</literal>' (which corresponds to a JMS Topic called '<literal
>SpyTopic</literal>').</para>
</section>
</chapter>

View File

@ -1,120 +0,0 @@
<?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="duplicate-detection">
<title>重复消息检测</title>
<para>ActiveMQ具有强大的自动检测重复消息的功能。应用层无需实现复杂的重复检测。本章解释了什么是重复检测
在ActiveMQ中如何工作的以及如何进行配置。</para>
<para>当客户端向服务器端发送消息时,或者从一个服务器向另一个服务器传递消息时,如果消息发送后目标服务器或者
连接出现故障,导致发送一方没有收到发送成功的确认信息,发送方因此就无法确定消息是否已经成功发送到了目标地
址。</para>
<para>如果上述的故障发生在消息被成功接收并处理后,但是在向发送方返回功能确认信息之前,那么消息实际上可以到达
其目的地址;如果故障发生在消息的接收及处理过程中,则消息不会到达其目的地址。从发送方的角度看,它无法区分
这两种情况。</para>
<para>当服务器恢复后,客户端面临的困难的选择。它知道服务器出了故障,但是不知道刚刚发送的消息是否成功到达目的
地址。如果它重新发送这个消息,就有可能造成消息的重复。如果这个消息是一个订单的话,重复发送消息就会产生两
个相同的订单,这当然不是所希望的結果。</para>
<para>将消息的发送放到一个事务中也不能解决这个问题。如果在事务提交的过程中发生故障,同样不能确定这个事务是否提交
成功!</para>
<para>为了解决这个问题ActiveMQ提供了自动消息重复检测功能。</para>
<section>
<title>在消息发送中应用重复检测</title>
<para>在消息发送中启用重复检测功能十分简单:你只需将消息的一个特殊属性设置一个唯一值。你可以用任意方法来
计算这个值,但是要保证它的唯一性。当目标服务器接收到这个消息时,它会检查这个属性是否被设置,如果设置了,
就检查内存缓存中是否已经接收到了相同值的消息。如果发现已经接收过具有相同属性值的消息,它将忽略这个消息。</para>
<note>
<para>在节点之间的消息传递使用重复消息检测可以保证<emphasis>一次且只一次</emphasis>的传递,和使用
XA事务接收消息的效果一样但是比XA消耗的资源要少并且更容易。</para>
</note>
<para>如果是在一个事务中发送消息,则只需要设置其中一个消息的属性值。在服务器端如果服务器检测到一个事务中某一个
消息重复,则会忽略整个事务。</para>
<para>这个属性的名称由<literal
>org.apache.activemq.api.core.HDR_DUPLICATE_DETECTION_ID</literal>定义,即:
<literal>_HQ_DUPL_ID</literal></para>
<para>该属性的值可以是<literal>byte[]</literal>类型或<literal
>SimpleString</literal>类型核心接口。如果使用JMS它必须是<literal>String</literal>
类型。它的值一定是唯一的。一个简单的方法是使用UUID。</para>
<para>下面是一个使用核心接口设置这个属性的例子:</para>
<programlisting>
...
ClientMessage message = session.createMessage(true);
SimpleString myUniqueID = "This is my unique id"; // Could use a UUID for this
message.setStringProperty(HDR_DUPLICATE_DETECTION_ID, myUniqueID);
...
</programlisting>
<para>下面则是一个使用JMS的例子</para>
<programlisting>
...
Message jmsMessage = session.createMessage();
String myUniqueID = "This is my unique id"; // Could use a UUID for this
message.setStringProperty(HDR_DUPLICATE_DETECTION_ID.toString(), myUniqueID);
...
</programlisting>
</section>
<section id="duplicate.id.cache">
<title>配置重复ID缓存</title>
<para>服务器缓存中保存接收到的消息的<literal
>org.apache.activemq.core.message.impl.HDR_DUPLICATE_DETECTION_ID</literal>属性值。每个地址有
单独的缓存。</para>
<para>缓存的大小是固定的,循环使用。如果缓存的最大可以存放<literal
>n</literal>条记录,那么<literal>n + 1</literal>条记录将会覆盖缓存中的第<literal>0</literal>
条记录。</para>
<para>缓存的最大容量在文件<literal>activemq-configuration.xml</literal>中配置,参数是<literal
>id-cache-size</literal>。默认值是<literal>2000</literal>条记录。</para>
<para>在文件<literal>activemq-configuration.xml</literal>中还可以配置将缓存持久化到磁盘。相应的参数
<literal>persist-id-cache</literal>。如果设为<literal>true</literal>则每加入一个id就将
它同时保存到磁盘中。默认值是<literal>true</literal></para>
<note>
<para>注意在设置缓存大小时一定要保证缓存能保存足夠数量的记录当消息被重新发送时之前发送的ID不被
覆盖掉。</para>
</note>
</section>
<section>
<title>桥与重复检测</title>
<para>核心桥可以通过配置在将消息发向目的服务器之前自动向消息中添加唯一的id如果消息中还没有的话。这样
如果目的服务器发生故障,核心桥在重新发送消息时,目的服务器就可以自动检测重复的消息,发现重复消息即丢弃。</para>
<para>要配置核心桥的自动添加id的功能需要在<literal>activemq-configuration.xml</literal>中桥的配置
里将<parameter>use-duplicate-detection</parameter>参数设为<literal>true</literal></para>
<para>这个参数的默认值是<literal>true</literal></para>
<para>关于核心桥的配置和使用,参见<xref linkend="core-bridges" /></para>
</section>
<section>
<title>重复检测与集群连接</title>
<para>集群连接内部使用核心桥在节点间可靠地移动消息因此它们的核心桥也可以配置自动添加id的功能。</para>
<para>配置的方法是在<literal>activemq-configuration.xml</literal>文件中将集群连接的
<parameter>use-duplicate-detection</parameter>参数设为<literal>true</literal></para>
<para>这个参数的默认值是<literal>true</literal></para>
<para>有关集群连接配置的更多信息,请参见<xref linkend="clusters"/></para>
</section>
<section>
<title>分页转存与重复检测</title>
<para>ActiveMQ在将消息进行分页转存中也使用了重复检测。当分页转存消息被从磁盘中读回到内存时如果服务器发生故障
重复检测可以避免在这一过程中有消息被重复读入,即避免了消息的重复传递。</para>
<para>关于分页转存的配置信息请参见<xref linkend="paging" /></para>
</section>
</chapter>

View File

@ -1,178 +0,0 @@
<?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="embedding-activemq">
<title>嵌入式ActiveMQ</title>
<para>ActiveMQ是由简单传统Java对象POJO实现因此它可以在任何依赖注入的框架中运行比如JBoss
MicrocontainerSprint或Google Guice。另外如果你的应用程序内部需要消息功能你可以在程序中
<emphasis>直接实例化</emphasis>ActiveMQ的客户端或服务器端。我们称之为<emphasis>嵌入式</emphasis>
ActiveMQ。</para>
<para>有些应用需要高性能、事务性及持久化的消息服务但是又不希望自己去费时费力实现它。于是嵌入式ActiveMQ就
成为了一个很适当的选择。</para>
<para>要使用嵌入式ActiveMQ只需要几个简单的步骤。首先初始化配置对象再初始化服务器并启动它在你的虚拟机
中就运行越来了一个ActiveMQ服务器。就是这么简单。</para>
<section>
<title>POJO的初始化</title>
<para>按照以下步骤去做:</para>
<para>创建配置对象这个对象包装了ActiveMQ的配置信息。如果你想使用配置文件则使用<literal
>FileConfigurationImpl</literal></para>
<programlisting>import org.apache.activemq.core.config.Configuration;
import org.apache.activemq.core.config.impl.FileConfiguration;
...
Configuration config = new FileConfiguration();
config.setConfigurationUrl(urlToYourconfigfile);
config.start();</programlisting>
<para>如果不需要配置文件,可以用<literal>ConfigurationImpl</literal>,只要将其中的各种配置参数设置好
即可。如添加适当的接收器。</para>
<para><literal>ConfigurationImpl</literal>用来配置接收器。和主要配置文件相似,只需要添加
<literal>NettyAcceptorFactory</literal>即可。</para>
<programlisting>import org.apache.activemq.core.config.Configuration;
import org.apache.activemq.core.config.impl.ConfigurationImpl;
...
Configuration config = new ConfigurationImpl();
HashSet&lt;TransportConfiguration> transports = new HashSet&lt;TransportConfiguration>();
transports.add(new TransportConfiguration(NettyAcceptorFactory.class.getName()));
transports.add(new TransportConfiguration(InVMAcceptorFactory.class.getName()));
config.setAcceptorConfigurations(transports);</programlisting>
<para>接着就需要初始化并启动ActiveMQ服务。<literal
>org.apache.activemq.api.core.server.ActiveMQ</literal>类有一些静态方法可用来创建ActiveMQ服务器。</para>
<programlisting>import org.apache.activemq.api.core.server.ActiveMQ;
import org.apache.activemq.core.server.ActiveMQServer;
...
ActiveMQServer server = ActiveMQ.newActiveMQServer(config);
server.start();</programlisting>
<para>你还可以直接实例化<literal>ActiveMQServerImpl</literal></para>
<programlisting>ActiveMQServer server =
new ActiveMQServerImpl(config);
server.start();</programlisting>
</section>
<section>
<title>使用依赖注入框架</title>
<para>你还可以使用一个依赖注入框架来启动ActiveMQ比如<trademark>JBoss
Microcontainer</trademark><trademark>Spring框架</trademark></para>
<para>ActiveMQ独立服务器使用的是JBoss Microcontainer作为其框架。在ActiveMQ的发布中包括的<literal
>ActiveMQBootstrapServer</literal><literal>activemq-beans.xml</literal>文件共同实现了
在JBoss Microcontainer中对ActiveMQ服务器的引导。</para>
<para>要使用JBoss Microcontainer需要在xml文件中声明<literal>ActiveMQServer</literal>
<literal>Configuration</literal>对象。另外还可以注入一个安全管理器和一个MBean服务器。但是这些
注入是可选的。</para>
<para>下面是一个基本的JBoss Microcontainer的XML Bean的声明</para>
<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?>
&lt;deployment xmlns="urn:jboss:bean-deployer:2.0">
&lt;!-- The core configuration -->
&lt;bean name="Configuration"
class="org.apache.activemq.core.config.impl.FileConfiguration">
&lt;/bean>
&lt;!-- The core server -->
&lt;bean name="ActiveMQServer"
class="org.apache.activemq.core.server.impl.ActiveMQServerImpl">
&lt;constructor>
&lt;parameter>
&lt;inject bean="Configuration"/>
&lt;/parameter>
&lt;/constructor>
&lt;/bean>
&lt;/deployment></programlisting>
<para><literal>ActiveMQBootstrapServer</literal>实现了JBoss Microcontainer的简单封装。</para>
<programlisting>ActiveMQBootstrapServer bootStrap =
new ActiveMQBootstrapServer(new String[] {"activemq-beans.xml"});
bootStrap.run();</programlisting>
</section>
<section>
<title>连接嵌入式ActiveMQ</title>
<para>嵌入式ActiveMQ的连接和普通的连接一样要创建连接工厂</para>
<section>
<title>核心接口</title>
<para>使用核心接口,需要创建一个<literal>ClientSessionFactory</literal>然后正常建立连接。</para>
<programlisting>ClientSessionFactory nettyFactory = ActiveMQClient.createClientSessionFactory(
new TransportConfiguration(
InVMConnectorFactory.class.getName()));
ClientSession session = factory.createSession();
session.createQueue("example", "example", true);
ClientProducer producer = session.createProducer("example");
ClientMessage message = session.createMessage(true);
message.getBody().writeString("Hello");
producer.send(message);
session.start();
ClientConsumer consumer = session.createConsumer("example");
ClientMessage msgReceived = consumer.receive();
System.out.println("message = " + msgReceived.getBody().readString());
session.close();</programlisting>
</section>
<section>
<title>JMS接口</title>
<para>使用JMS接口连接嵌入ActiveMQ同样简单。只需要直接实例化
<literal>ConnectionFactory</literal>即可。如下面例子所示:</para>
<programlisting>ConnectionFactory cf =
ActiveMQJMSClient.createConnectionFactory(
new TransportConfiguration(InVMConnectorFactory.class.getName()));
Connection conn = cf.createConnection();
conn.start();
Session sess = conn.createSession(true, Session.SESSION_TRANSACTED);
MessageProducer prod = sess.createProducer(queue);
TextMessage msg = sess.createTextMessage("Hello!");
prod.send(msg);
sess.commit();
MessageConsumer consumer = sess.createConsumer(queue);
TextMessage txtmsg = (TextMessage)consumer.receive();
System.out.println("Msg = " + txtmsg.getText());
sess.commit();
conn.close();</programlisting>
</section>
</section>
<section>
<title>JMS嵌入式ActiveMQ的例子</title>
<para>有关如何设置与运行JMS嵌入式ActiveMQ的例子请参见<xref linkend="examples.embedded"/></para>
</section>
</chapter>

View File

@ -1,427 +0,0 @@
<?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="examples">
<title>例子</title>
<para>在ActiveMQ的发布包中有超过70个不同的例子。这些例子直接可以运行。它们分别展示了ActiveMQ所具有的各种功能。</para>
<para>所有的例子都在ActiveMQ发布包的 <literal>examples</literal>目录下。所有的例子被分成了两大类:
JMS例子和内核例子。JMS例子展现的是JMS的各种功能内核的例子则展示的是内核API的功能。
</para>
<para>此外ActiveMQ还提供了一些Java EE的例子这些例子需要JBoss应用服务器才能运行。</para>
<section>
<title>JMS 例子</title>
<para>要运行一个JMS例子只要进入到相应例子的子目录运行 <literal>./build.sh</literal> (或者
在Windows平台上运行<literal>build.bat</literal>)即可。</para>
<para>下面列出的这些JMS例子并配有简要的说明。</para>
<section id="application-level-failover">
<title>应用层的失效备援Failover</title>
<para>ActiveMQ支持应用层的失效备援。这在服务器端没有复制replication配置的情况下是很有用的。</para>
<para>应用程序可以注册一个JMS <literal>ExceptionListener</literal>。当ActiveMQ检测到连接故障时它会
通知这个注册的Listener。</para>
<para>这个<literal>ExceptionListener</literal>在接到ActiveMQ的通知后可以与其它的节点创建
新的连接、会话等对象,以使应用程序能继续运行。</para>
<para>应用层的失效备援是实现高可获得性HA的一种方法。它与自动失效备援不同之处在于它需要编写额外的代码。
同时由于发生故障时旧的会话结束,这会造成那些还没来得及提交的工作丢失,还会造成任何没有通知的消息被重发。</para>
</section>
<section id="examples.bridge">
<title>内核桥Bridge例子</title>
<para><literal>bridge</literal>例子展示的是将一个内核桥部署到一个服务器上从本地的queue接收消息并将其转发到
另一服务器的地址上。</para>
<para>内核的bridge可用来在两个互相分开的ActiveMQ的服务器间建立一个消息流。它可以处理临时性的连接故障特别适用于
不可靠的网络的情况。广域网就是一个例子。</para>
</section>
<section id="examples.browsers">
<title>浏览器Browser</title>
<para><literal>browser</literal>例子展示的是在ActiveMQ中如何使用JMS <literal
>QueueBrowser</literal></para>
<para>有关JMS queue的概念在JMS 1.1 specification有明确的定义这里就不再叙述。</para>
<para> 一个<literal>QueueBrowser</literal>可以用来观察queue中的消息而影响它们。它可以观察queue中的全部
消息也可以定义一个选择器selector来选择性地察看消息。</para>
</section>
<section>
<title>Client Kickoff</title>
<para><literal>client-kickoff</literal>例子展示的是如何利用JMX管理接口通过已知的IP地址来断开客户端的连接。</para>
</section>
<section>
<title>客户端的负载均衡</title>
<para><literal>client-side-load-balancing</literal>例子展示的是通过一个JMS连接可以在集群的不同节点上创建
会话。也就是说ActiveMQ可以对客户端的会话创建进行集群内的负载均衡。</para>
</section>
<section id="examples.clustered.grouping">
<title>集群分组</title>
<para>与分组grouping例子相似只是本例所展示的是集群的情况。发向不同节点的具有相同组id的消息
都会传送到同一个节点上的同一个接收者consumer</para>
</section>
<section>
<title>集群队列</title>
<para><literal>clustered-queue</literal> 例子将一个JMS queue部署到两个节点上。这两个节点组成一个集群。
我们在每个节点上各创建一个接收者consumer但只在其中一个节点上创建一个发送者producer。利用发送者
发送一些消息然后确认两个接收者以轮换方式round-robin接收这些消息。</para>
</section>
<section>
<title>单机集群</title>
<para><literal>clustered-standalone</literal>例子所展示的是如何在同一台机器上配置并运行
3个节点的集群。在每个节点上都创建了一个JMS topic的订阅者subscriber。只在其中一个节点上
创建了一相发送者来向这个topic发送一些消息。然后我们确认所有的subscriber都接收到了这些消息。</para>
</section>
<section>
<title>集群的Topic</title>
<para><literal>clustered-topic</literal>例子将一个JMS topic部署到两个节点上。这两个节点组成一个集群。
然后在每个节点上创建了一个订阅者subscriber只在一个节点上创建一个发送者producer。通过这个发
送者发送一些消息,确认两个订阅者都收到了这些消息。</para>
</section>
<section id="examples.consumer-rate-limit">
<title>限制接收速率</title>
<para>ActiveMQ可以控制一个JMS消息接收者接收消息的速度。这是在创建或部署连接工厂时通过其配置参数来完成的。</para>
<para>如果设置了这个速度的限制ActiveMQ会保证其向接收者传递消息的速度永远不会超过这个限制。</para>
</section>
<section id="examples.dead-letter">
<title>死消息Dead Letter</title>
<para><literal>dead-letter</literal>例子让你了解如何定义和处理死消息。有时候消息由于某种原因不能成功
地传递出去,比如接收者在接收消息的交易中发生回滚。</para>
<para>发生回滚后消息被”退回“到JMS目标destination准备进行重发。这一过程可能会被不停地重复下去造成
消息永远发不出去,而且浪费系统的时间。</para>
<para>为了避免上述情况的发生,消息系统引入了死消息的概念:即当一个消息被反复重发不成功达到一定的次数时,该消息
便成为了死消息它将从所属目标destination中删除并发送到一个称为死消息目标的目标。用户可以从死消息目标
          上接收这些死消息以便进行分析。</para>
</section>
<section id="examples.delayed-redelivery">
<title>延迟再发送</title>
<para><literal>delayed-redelivery</literal>是一个展示如何配置ActiveMQ延迟再发送消息的例子。</para>
<para>当客户端经常发生故障或发生事务回滚时消息会不停地重复发送这样会造成CPU和网络资源被不间断的
重复发送所占用,影响其它工作的进行。延迟再发送可以有效地减轻这种情况。</para>
</section>
<section id="divert-example">
<title>转移Divert</title>
<para>ActiveMQ通过配置可以将消息从一个地址自动地转移到另一地址。这个例子就是向用户展示转移的配置和使用。</para>
</section>
<section>
<title>持久订阅Durable Subscription</title>
<para><literal>durable-subscription</literal>是一个在ActiveMQ中如何使用持久订阅durable
subscription的例子。持久订阅是标准JMS的一部分在JMS 1.1规范中有它的详细定义。</para>
<para>对于一个持久订阅来说,它的消息可以在订阅没有处于接收状态时被保留。另外,如果发到它的消息是持久
消息的话,这些消息可以在服务器故障或重启时不丢失。 </para>
</section>
<section>
<title>嵌入方式Embedded</title>
<para><literal>embedded</literal>是一个如何将ActiveMQ服务嵌入到你的应用中的例子。</para>
</section>
<section>
<title>HTTP 传输协议的支持</title>
<para><literal>http-transport</literal>展示了ActiveMQ如何支持在传输层使用HTTP协议来发送和接收消息。</para>
</section>
<section>
<title>直接实例化JMS对象</title>
<para>JMS 对象是指 <literal>连接工厂ConnectionFactory</literal><literal
>队列Queue</literal><literal>话题Topic</literal> 的实例。通常情况下它们通过JNDI服务
来获取。它们在JMS术语中被称为“被管理的对象administered objects”。</para>
<para>有的时候客户端没有JNDI服务可用或者不适合使用JNDI。那么在没有JNDI的情况下ActiveMQ允许直接在客户端
将这些JMS对象实例化。</para>
</section>
<section id="examples.interceptor">
<title>拦截器Interceptor</title>
<para>ActiveMQ可以配置拦截器以便用户可以自己处理各种各样的消息事件。这个例子就是给用户展示如何使用
拦截器。</para>
</section>
<section id="examples.jaas">
<title>JAAS</title>
<para><literal>jaas</literal>是一个如何配置JAAS安全模式的例子。ActiveMQ可以使用JAAS来进行用户的验证与权限控制。</para>
</section>
<section id="examples.jms.jms-bridge">
<title>JMS桥Bridge</title>
<para><literal>jms-brige</literal>是一个在两个单独ActiveMQ服务器之间设置桥的例子。</para>
</section>
<section id="examples.jmx">
<title>JMX管理</title>
<para><literal>jmx</literal>例子展示了如何使用JMX来管理ActiveMQ。</para>
</section>
<section id="examples.large-message">
<title>大消息</title>
<para><literal>large-message</literal>例子给用户展示了使用ActiveMQ来发送和接收大消息的功能。ActiveMQ
支持超大消息的发送与接收。这些消息可以大到内存无法装下。它的大小只受服务器的硬盘空间的限制。</para>
<para>在服务器端大消息是被持久化的,所以它可以承受服务器的崩溃或重启而不丢失或损坏。</para>
</section>
<section id="examples.last-value-queue">
<title>最新值队列</title>
<para><literal>last-value-queue</literal>向用户展示了如何定义与使用最新值队列。当在配置文件定义好
最新值的参数后,这些最新值队列就会自动地用新的消息取代旧的消息,也就是说旧的消息被抛弃掉。这样一个最新
值的队列总是保留最新的消息在队列中。</para>
<para>股票价格消息就是一个典型的最新值队列的用例。对用户来说他所关心的是一支股票的最新价格,对于过去的价格
是没有多大兴趣的。</para>
</section>
<section>
<title>分布式队列的负载均衡</title>
<para><literal>clustered-queue</literal>例子中配置了一个2节点的ActiveMQ服务集群。在集群上部署了
一个分布式JMS队列。</para>
<para>然后在一个节点上创建了一个发送者producer在两个节点上分别创建一个接收者consumer。通过
发送者向队列发送一些消息然后被两的接收者以轮流round-robin的方式接收。</para>
<para>本例说明了ActiveMQ可以将消息向集群中的每个接收者分布式地传递消息。</para>
</section>
<section id="examples.management">
<title>管理</title>
<para><literal>management</literal>例子展示的是如何使用JMS消息来实现对ActiveMQ的管理。</para>
</section>
<section id="examples.management-notifications">
<title>管理通知</title>
<para><literal>management-notification</literal>展示了ActiveMQ如何以JMS消息的形式向用户发送
管理通知。当某些事件发生时如接收都创建关闭地址创建与删除安全验证失败等等ActiveMQ会向客户
发出JMS消息以通知客户这些事件的相关信息。客户接收到这些信息后可以作出相应的处理。</para>
</section>
<section id="examples.message-counters">
<title>消息计数器</title>
<para><literal>message-counters</literal>是一个展示如何使用消息计数器获取JMS队列中的消息信息。</para>
</section>
<section id="examples.expiry">
<title>消息失效</title>
<para><literal>expiry</literal>例子中包括了如何定义和使用消息失效期。消息如果在消息服务器中存留超过一定
的时间就可以被删除。根据JMS规范接收者就不应该接收到已经过了失效期的消息。但是并不保证一定接收不到</para>
<para>ActiveMQ可以给一个队列配上一个失效地址当队列中的消息失效时它们就会从队列中删除并转移到该失效地址。
这些“失效"的消息可以从失效地址中接收并进行分析。</para>
</section>
<section id="examples.message-group">
<title>消息组</title>
<para><literal>message-group</literal>展示的是如何在ActiveMQ中配置消息组。消息组可以让你的消息
只被一个接收者接收。属于一个消息组中的消息有如下特性:</para>
<para>
<itemizedlist>
<listitem>
<para>同一个消息组中的消息都有相同的组ID。即它们的JMSXGroupID属性值相同。</para>
</listitem>
<listitem>
<para>第一个接收到消息组中的消息的接收者将会接收到所有该组中的消息。</para>
</listitem>
</itemizedlist>
</para>
</section>
<section id="examples.message-group2">
<title>消息组例2</title>
<para><literal>message-group2</literal>是另外一个消息组的例子。它展示的是通过配置连接工厂来实现
消息组的功能。</para>
</section>
<section id="examples.message-priority">
<title>消息优先级</title>
<para>消息优先级会影响消息的传递顺序。</para>
<para>消息优先级由标准的JMS消息头属性JMSPriority的值确定。参见JMS 1.1规范。</para>
<para>优先级是一个0到9之间的整数值。当消息被传递时根据优先级的不同消息的传递顺序会收到影响。优先级
高的消息往往会比优先级低的先传递给接收者。 </para>
<para>优先级相同的消息会按照它们到达目标的顺序来传递。在JMS 1.1规范中有详细的规定。</para>
</section>
<section id="examples.no-consumer-buffering">
<title>零接收缓冲</title>
<para>默认时ActiveMQ的接收者客户端有一个消息缓冲它用来保存从服务器上预先接收的消息。这样做是为了提高
性能。因为如果没有这个缓冲每次调用receive()或onMessage()后ActiveMQ就会访问一次服务器请求下
一个消息。</para>
<para>这样每接收一个消息就会增加一次网络往返的传输。因此ActiveMQ在默认情况下使用客户端的接收缓冲来
预先接收消息,以提高效率。</para>
<para>然而在某些情况下这样的缓冲不符合应用需要。那么可以将缓冲关闭。本例就是展示如何关闭接收缓冲。</para>
</section>
<section id="examples.non-transaction-failover">
<title>带有服务器数据复制的非事务失效备援</title>
<para><literal>non-transaction-failover</literal>例子展示了由两个服务器组成的高可获得性主/从关系。
客户端使用一个非交易的JMS会话session可以在主节点崩溃的情况下从主节点失效备援到备份节点。</para>
<para>ActiveMQ的这一功能是通过主、备节点间的状态复制来实现的。当主节点发生故障崩溃时客户端的连接可以自动
转向备份节点以继续的发送或接收消息。当使用非事务性的会话时,有可能发生消息丢失或重复传递的情况。</para>
</section>
<section id="examples.paging">
<title>分页paging</title>
<para><literal>paging</literal>例子展示了ActiveMQ在内存有限时如何支持超大容量的队列。当内存不够时
ActiveMQ会将消息保存到磁盘上需要时再将它们从磁盘读入内存。这一过程对用户是透明的。</para>
</section>
<section id="examples.pre-acknowledge">
<title>预先通知</title>
<para>标准的JMS支持3种通知模式<literal>
AUTO_ACKNOWLEDGE</literal>(自动通知)、<literal>CLIENT_ACKNOWLEDGE</literal>客户通知以及 <literal
>DUPS_OK_ACKNOWLEDGE</literal>可重复通知。请参阅JMS规范和教程来进一步了解这几种通知方式。</para>
<para>所有方式都需要从客户端发通知到服务器端。有时当发生故障时你并不在乎丢失一些消息,这样可以采用在服务器端在消息
传递前进行通知就显得比较合理。本例就是展示如何使用这一ActiveMQ独有的通知方式。</para>
</section>
<section id="producer-rate-limiting-example">
<title>消息发送速度限制</title>
<para><literal>producer-rte-limit</literal>例子展示了如何设置ActiveMQ的最大消息发送速率。它控制消息的
发送者JMS producer发送消息的最大速度。</para>
</section>
<section>
<title>队列</title>
<para>这一个简单的JMS队列的例子。</para>
</section>
<section>
<title>Message再分配</title>
<para><literal>queue-message-redistribution</literal>例子展示了如何将消息在集群的各节点同名的队列
间进行再分配。</para>
</section>
<section>
<title>队列请求</title>
<para>这是一个简单的实现队列请求的例子。</para>
</section>
<section>
<title>带消息选择器selector的队列</title>
<para><literal>queue-selector</literal>例子展示了如何使用选择器来有条件地选择消息进行接收。</para>
</section>
<section>
<title>节点连接重试</title>
<para><literal>reattach-node</literal>例子展示了如何使客户端在发生故障时重试连接到原有服务器,而不是
直接放弃并通知用户的ExceptionListener。通过配置客户端可以自动的不断重试连接直到服务器连接上为止。</para>
</section>
<section>
<title>请求/应答</title>
<para>一个简单的展示JMS 请求/应答消息方式的例子。</para>
</section>
<section id="examples.scheduled-message">
<title>定时消息</title>
<para><literal>scheduled-message</literal>例子展示了如何向ActiveMQ发送定时消息scheduled message
所谓定时消息就是在规定的将来的某一时间传递的消息。</para>
</section>
<section>
<title>安全</title>
<para><literal>security</literal>例子展示了如何配置ActiveMQ的安全参数。</para>
</section>
<section id="asynchronous-send-acknowledgements-example">
<title>发送通知</title>
<para><literal>send-acknowledgements</literal>例子展示了如何使用ActiveMQ提供的高级异步发送通知功能
<emphasis>asynchronous send acknowledgements</emphasis>)。这是服务器向客户端通知消息已经
被接收。</para>
</section>
<section>
<title>SSL传输支持</title>
<para><literal>ssl-enabled</literal>例子展示了如何配置使用SSL来发送与接收消息。</para>
</section>
<section>
<title>静态消息选择器</title>
<para><literal>static-selector</literal>例子展示了如何配置ActiveMQ核心队列的静态消息选择器又称过滤器</para>
</section>
<section>
<title>使用JMS方法来配置静态消息选择器</title>
<para><literal>static-selector-jms</literal>例子采用JMS方法来配置ActiveMQ的队列的静态选择器过滤器</para>
</section>
<section>
<title>Stomp</title>
<para><literal>stomp</literal>例子展示了如何配置ActiveMQ来发送与接收Stomp消息。</para>
</section>
<section>
<title>Stomp与Web Sockets</title>
<para><literal>stomp-websockets</literal>例子给出了如何配置一个ActiveMQ服务器直接从Web浏览器
需要支持Web Socket发送和接收Stomp消息。</para>
</section>
<section>
<title>对称型集群</title>
<para><literal>symmetric-cluster</literal>例子展示如何设置一个ActiveMQ的对称型集群。</para>
<para>ActiveMQ的集群配置是非常灵活的。你可以根据需要设置不同的集群结构。最常用的就是对称型的集群了。这是在应用
服务器中常见的集群类型。</para>
<para>对称型的集群具有同一性,即每个节点与其他节点处于同等地位,并且每一个节点都与其他任一节点相连接。</para>
</section>
<section>
<title>临时队列</title>
<para>本例展示的是如何使用一个JMS临时队列temporary queue</para>
</section>
<section>
<title>话题Topic</title>
<para>一个简单的JMS topic的例子。</para>
</section>
<section id="topic-hierarchy-example">
<title>话题体系Topic Hierarchy</title>
<para>ActiveMQ支持话题体系。所谓话题体系就是允许你使用通配符来注册一个订阅subscriber),这样所有发送到
与该通配符相匹配的地址的消息都可以被该订阅收到。</para>
</section>
<section>
<title>话题选择器例1</title>
<para><literal>topic-selector-example1</literal>例子展示的是如何创建带有选择器的JMS话题Topic订阅。</para>
</section>
<section>
<title>话题选择器例2</title>
<para><literal>topic-selector-example2</literal> 是另一个使用带有选择器的JMS话题Topic订阅的例子。</para>
</section>
<section id="examples.transaction-failover">
<title>带有数据复制的事务性失效备援</title>
<para><literal>transaction-failover</literal>例子展示了由两个服务器组成的高可获得性主/备关系。
客户端使用一个交易的JMS会话session可以在主节点崩溃的情况下从主节点失效备援到备份节点。</para>
<para>ActiveMQ的这一功能是通过主、备节点间的状态复制来实现的。当主节点发生故障崩溃时客户端的连接可以自动
转向备份节点以继续的发送或接收消息。当使用事务性的会话时,能够保证消息被传递并且只被传递一次。</para>
</section>
<section>
<title>事务性会话</title>
<para><literal>transactional</literal>例子展示了如何在ActiveMQ中使用事务性会话。</para>
</section>
<section>
<title>XA Heuristic</title>
<para><literal>xa-heuristic</literal>例子给出了如何通过ActiveMQ的管理接口来做出一个XA的heuristic决定。
一个XA的heuristic决定是一个单方面的对一个已经准备的preparedXA事务分支提交或回滚的决定。</para>
</section>
<section>
<title>XA 接收</title>
<para><literal>xa-receive</literal>例子展示的是如何使用ActiveMQ在一个XA事务内接收消息。</para>
</section>
<section>
<title>XA 发送</title>
<para><literal>xa-send</literal>例子展示的是如何使用ActiveMQ在一个XA事务内发送消息。</para>
</section>
<section>
<title>XA与事务管理器transaction manager</title>
<para><literal>xa-with-jta</literal>展示了如何在ActiveMQ中使用JTA接口来控制事务。</para>
</section>
</section>
<section>
<title>核心API的例子</title>
<para>运行核心API的例子很简单只要进到相应的例子目录下运行“<literal>ant</literal>"即可。 </para>
<section id="examples.embedded">
<title>嵌入式</title>
<para>本例展示了如何将ActiveMQ服务器嵌入到你的代码中。</para>
</section>
</section>
<section>
<title>Java EE 例子</title>
<para>绝大多数的Java EE例子都可以按如下步骤运行进入到相应的目录中先运行<literal>ant deploy</literal>
这一步创建了一个新的JBoss的服务器配置方案并启动它。当JBoss服务器启动后再运行<literal>ant run</literal>
启动例子程序。有些例子需要额外的步骤,请参见相关的例子的文档。</para>
<section>
<title>EJB/JMS 事务</title>
<para>这个例子展示了在一个事务内使用EJB和JMS的方法。</para>
</section>
<section>
<title>HAJNDI (High Availability)</title>
<para>这个例子展示了如何使用集群中的JNDI服务。</para>
</section>
<section>
<title>资源适配器的配置JCA</title>
<para>本例展示了如何配置ActiveMQ的JCA适配器的各种参数。</para>
</section>
<section>
<title>资源适配器运程服务器的配置</title>
<para>本例展示了如何配置ActiveMQ的JCA适配器来与远程的ActiveMQ服务器通迅。</para>
</section>
<section id="examples.javaee.jms-bridge">
<title>JMS 桥Bridge</title>
<para>本例展示了如何使用ActiveMQ的JMS bridge。</para>
</section>
<section>
<title>MDB (消息驱动Bean)</title>
<para>一个消息驱动bean的例子。</para>
</section>
<section>
<title>Servlet传输</title>
<para>一个展示在ActiveMQ中使用servlet作为传输层的例子。</para>
</section>
<section>
<title>Servlet SSL 传输</title>
<para>一个展示在ActiveMQ中使用基于SSL之上的servlet传输的例子。</para>
</section>
<section id="xa-recovery-example">
<title>XA 恢复recovery</title>
<para>这是一个展示ActiveMQ在JBoss应用服务器中的XA recovery是如何工作的例子。</para>
</section>
</section>
</chapter>

View File

@ -1,71 +0,0 @@
<?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="filter-expressions">
<title>过滤器表达式</title>
<para>ActiveMQ提供了一套强大的过滤器filter语言。它的语法是基于SQL 92表达式的部分语法。</para>
<para>实际上它与JMS选择器selector的语法是相同的。只是其中有一些预定义的标识符有所不同。有关
JMS选择器的相关知识参见 <ulink
url="http://java.sun.com/javaee/5/docs/api/javax/jms/Message.html"
>javax.jms.Message</ulink></para>
<para>ActiveMQ在以下以个地方使用了过滤器表达式</para>
<itemizedlist>
<listitem>
<para>预定义的队列。当在<literal
>activemq-configuration.xml</literal><literal>activemq-jms.xml</literal>定义
队列时,可以使用过滤器。只有与过滤器表达式相匹配的消息才能达到该队列中。</para>
</listitem>
<listitem>
<para>核心桥可以使用可选的过滤器表达式。只有与表达式相匹配的消息才被桥处理。
参见(<xref linkend="core-bridges"/>)。</para>
</listitem>
<listitem>
<para>转移Divert也可以使用可选的过滤器表达式。只有与表达式匹配的消息才被转移。
参见(<xref linkend="diverts" />)。</para>
</listitem>
<listitem>
<para>另外过滤器还可以在编程方式创建接收者consumer和队列时使用。还有一些应用过滤器的地方在
<xref linkend="management"/>中有所描述。</para>
</listitem>
</itemizedlist>
<para>ActiveMQ的内核过滤器表达式与JMS选择器表达式是有所不同的。JMS选择器应用于JMS消息而ActiveMQ的内核过滤
器表达式则用于内核消息。</para>
<para>以下标识符可以用在内核消息的过滤器表达式中,用来引用内核消息的属性:</para>
<itemizedlist>
<listitem>
<para><literal>HQPriority</literal>。代表消息的优先级。消息优先级属性的有效值为0到9间的整数。
0为最低优先级9为最高优先级。例<literal>HQPriority = 3 AND animal = 'aardvark'</literal></para>
</listitem>
<listitem>
<para><literal>HQExpiration</literal>。代表消息的失效时间。其值为一长整形数。</para>
</listitem>
<listitem>
<para><literal>HQDurable</literal>。代表消息是否是持久消息。它是一个字符型的属性,有效值为 <literal>DURABLE</literal>
<literal>NON_DURABLE</literal></para>
</listitem>
<listitem>
<para><literal>HQTimestamp</literal>。代表消息的创建时间,其值为一长整形数。</para>
</listitem>
<listitem>
<para><literal>HQSize</literal>。消息的大小。单位为字节。其值是一个整形数。</para>
</listitem>
</itemizedlist>
<para>任何其它的标识符在内核过滤器的表达式中都认为是代表着该消息的一个属性。</para>
</chapter>

View File

@ -1,243 +0,0 @@
<?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="flow-control">
<title>流控制</title>
<para>流控制是指对客户端与服务器之间,或者服务器之间的数据流量进行限制,目的是防止通迅双方由于大量数据而过载。</para>
<section>
<title>接收者consumer流控制</title>
<para>这是指对客户端的接收者接收消息流的控制。通常为了提高效率,在客户端通常将消息放入缓存,然后再将缓存中
的消息传递给接收者consumer。当接收者处理消息的速度小于服务器向其发送消息的速度时就可能造成消息在
客户端不断积累,最終引起内存溢出的错误。</para>
<section id="flow-control.consumer.window">
<title>基于窗口的流控制</title>
<para>默认情况下ActiveMQ的接收者一端会将消息进行缓存以提高性能。如果不这样做那每次接收者收到一个消息
都得通知服务器传递下一个消息,然后服务器再将下一个消息传递过来。这就增加了通信的次数。</para>
<para>对于每一次消息传递都有一个网络的往返通信,这样降低了性能。</para>
<para>为了避免这样ActiveMQ将每个接收者的消息提前接收到一处缓存中。每个缓存的最大值由
<literal>consumer-window-size</literal>参数决定(单位字节)。</para>
<para><literal>consumer-window-size</literal>的默认值是 1 MiB 1024 * 1024
字节)。</para>
<para>它的值可以是:</para>
<itemizedlist>
<listitem>
<para><literal>-1</literal> 代表<emphasis>大小无限制</emphasis>的缓存。</para>
</listitem>
<listitem>
<para><literal>0</literal> 代表不缓存消息。参见相关的例子 <xref
linkend="examples.no-consumer-buffering"/>。</para>
</listitem>
<listitem>
<para><literal>&gt;0</literal> 代表缓存的最大字节数。</para>
</listitem>
</itemizedlist>
<para>合理设置接收者的窗口大小可以显著提高性能。下面是两个极端的例子:</para>
<variablelist>
<varlistentry>
<term>快速接收者</term>
<listitem>
<para>所谓快速接收者是指消息的接收者处理消息的速度大于等于它的接收速度。</para>
<para>对于快速接收者或以将<literal>consumer-window-size</literal>设为
-1使得客户端的消息缓存的大小 <emphasis>无限制</emphasis></para>
<para>请谨慎使用这一设置值: 如果接收者的消息处理速度比接收速度小,可造成客户端内存溢出。</para>
</listitem>
</varlistentry>
<varlistentry>
<term>慢接收者</term>
<listitem>
<para>所谓慢接收者是指接收者每处理一个消息就要花很多时间。这样将缓存关闭就比较合理。服务器可以将多余的
消息传递给其它的接收者。</para>
<para>假设一个队列有2个接收者。其中一个接收者非常慢。消息被轮流传递到两个接收者。其中的快速接收者
很快将其缓存中的消息处理完毕。同时慢接收者的缓存中还有一些消息等待处理。这样快速接收者在一段时间
内就处于空闲状态。</para>
<para>这时,将<literal>consumer-window-size</literal> 设为0 (没有缓存),就可以将它变成
慢接收者。这样在慢接收者一方不会缓存消息,这使得快的接收者可以处理更多的消息,而不至于处于空闲
状态。</para>
<para>这说明将它设置为0可以控制一个队列的消息在多个接收者之间的消息分配。</para>
</listitem>
</varlistentry>
</variablelist>
<para>大多数情况下很难判断哪些接收者是快速的,哪些是慢速的。往往很多接收者是处于两者之间。这样对于
<literal>consumer-window-size</literal>的值就要视具体情况而定。有时需要进行一定的测试
来决定它的最佳值。通常情况下将其设为1MiB可以满足大多数的应用情况。</para>
<section id="flow-control.core.api">
<title>使用核心接口Core API进行流控制</title>
<para>Hornet的核心接口中<literal
>ClientSessionFactory.setConsumerWindowSize()</literal>方法和一些
<literal>ClientSession.createConsumer()</literal>方法可以控制流的窗口大小。</para>
</section>
<section>
<title>使用JMS的流控制</title>
<para>若使用JNDI来获得连接工厂则需要通过配置<literal>activemq-jms.xml</literal>文件来设定窗口大小:</para>
<programlisting>
&lt;connection-factory name="ConnectionFactory"&gt;
&lt;connectors>
&lt;connector-ref connector-name="netty-connector"/&gt;
&lt;/connectors>
&lt;entries&gt;
&lt;entry name="ConnectionFactory"/&gt;
&lt;/entries&gt;
&lt;!-- Set the consumer window size to 0 to have *no* buffer on the client side --&gt;
&lt;consumer-window-size&gt;0&lt;/consumer-window-size&gt;
&lt;/connection-factory&gt;
</programlisting>
<para>如果直接实例化连接工厂,则使用<literal>ActiveMQConnectionFactory.setConsumerWindowSize()</literal>
方法来设定窗口大小。</para>
<para>参见例子<xref linkend="examples.no-consumer-buffering"/>来了解如何配置ActiveMQ来
关闭接收者的缓存。</para>
</section>
</section>
<section>
<title>速率流控制</title>
<para>我们还可以通过控制 <emphasis>速率</emphasis>的方法来控制流。这是一种像调节节流阀的形式。
这种方法保证一个接收者接收消息的速率不会超过设定的值。 </para>
<para>速率必须是一个正整数。它代表最大接收速度,单位是消息每秒。将它设为<literal>-1</literal>就会关闭速率流控制。
默认值是<literal>-1</literal></para>
<para>参见有关速率流控制的例子<xref linkend="examples.consumer-rate-limit"/>以进一步了解它的工作原理。</para>
<section id="flow-control.rate.core.api">
<title>使用核心接口Core API</title>
<para>ActiveMQ的核心接口的<literal
>ClientSessionFactory.setConsumerMaxRate(int consumerMaxRate)</literal>方法或
某些<literal>ClientSession.createConsumer()</literal>方法可以实现对流的速率控制。</para>
</section>
<section>
<title>使用JMS</title>
<para>如果从JNDI中获取连接工厂需要通过配置<literal>activemq-jms.xml</literal>来进行速率流控制:</para>
<programlisting>&lt;connection-factory name="ConnectionFactory">
&lt;connectors>
&lt;connector-ref connector-name="netty-connector"/>
&lt;/connectors>
&lt;entries>
&lt;entry name="ConnectionFactory"/>
&lt;/entries>
&lt;!-- We limit consumers created on this connection factory to consume messages
at a maximum rate
of 10 messages per sec -->
&lt;consumer-max-rate>10&lt;/consumer-max-rate>
&lt;/connection-factory></programlisting>
<para>如果是直接实例化连接工厂,则通过<literal>ActiveMQConnectionFactory.setConsumerMaxRate(int
consumerMaxRate)</literal>方法来设定最大流速率。</para>
<note>
<para>速率流控制可以与窗口流控制结合使用。速率控制只规定了客户端每秒接收多少消息。因此如果你设定
了一个较低的速率,同时又设定了一个大的缓存窗口,那么客户端的缓存将会很快饱和。</para>
</note>
<para>参见接收速率流控制的例子<xref linkend="examples.consumer-rate-limit"/>进一步了解速率流控制的配置和使用。</para>
</section>
</section>
</section>
<section>
<title>发送者producer的流控制</title>
<para>ActiveMQ还可以控制客户端向服务器发送消息的速度以避免服务器因大量数据过载。</para>
<section>
<title>基于窗口的流控制</title>
<para>与接收者的相应的控制相似。在默认条件下发送者要有足够的份额credits才可以向服务器的地址发送消息。
这个份额就是消息的大小。</para>
<para>当发送者的份额不足时,它要向服务器请求更多的份额以便发送更多的消息。</para>
<para>发送者一次向服务器请求的份额值被称为<emphasis
role="italic">窗口大小</emphasis></para>
<para>于是窗口大小就是指发送者向服务器不间断发送消息的总最大字节数。当发送完毕时需再向服务器请求份额。这样就避免了
服务器消息过载的情况。</para>
<section>
<title>使用核心接口Core API</title>
<para>若使用核心接口,<literal
>ClientSessionFactory.setProducerWindowSize(int producerWindowSize)</literal>
方法可以对窗口大小进行设定。</para>
</section>
<section>
<title>使用JMS</title>
<para>如果使用JNDI来获得连接工厂则需要配置<literal>activemq-jms.xml</literal>文件以设定窗口大小:</para>
<programlisting>
&lt;connection-factory name="ConnectionFactory">
&lt;connectors>
&lt;connector-ref connector-name="netty-connector"/>
&lt;/connectors>
&lt;entries>
&lt;entry name="ConnectionFactory"/>
&lt;/entries>
&lt;producer-window-size>10&lt;/producer-window-size>
&lt;/connection-factory></programlisting>
<para>如果是直接实例化连接工厂,则使用<literal>ActiveMQConnectionFactory.setProducerWindowSize(int
producerWindowSize)</literal>方法来设定窗口大小。</para>
</section>
<section>
<title>限定发送者窗口流控制</title>
<para>通常情况下客户端请求多少份额ActiveMQ服务器就给予多少份额。然而我们还可以针对每个地址来设定一个最大
的份额值,以使服务器给出的份额都不大于该值。这样可以防止一个地址的内存溢出。</para>
<para>例如如果有一个队列称为“myqueue”。将它的最大内存值设为10MiB则服务器就会控制给出的份额以保证向该队列的地
址发送消息时不会占大于10MiB的内存空间。</para>
<para>当一相地址已经满了的时候,发送者将会阻塞直到该地址有了多余的空间为止,即地址中的消息被接收了一部分后使得
地址腾出了一些空间。</para>
<para>我们将这种控制方法称为限定发送者窗口流控制。这是一种有效的防止服务器内存溢出的手段。</para>
<para>它可以看成是分页转存paging的另一种方法。分页转存不阻塞发送者它将消息转存到存贮介质上以节省内存的空间。</para>
<para>要配置一个地址的最大容量并告诉服务器在地址满了的情况下阻塞发送者,你需要为该地址定义一个
AddressSettings (<xref linkend="queue-attributes.address-settings"/>) 并设定
<literal>max-size-bytes</literal><literal
>address-full-policy</literal></para>
<para>这个配置对所有注册到该地址的队列有效。即所有注册队列的总内存将不超过 <literal
>max-size-bytes</literal>。对于JMS topic情况则意谓着该topic的所有订阅的内存不能超过
max-size-bytes的设定值。</para>
<para>下面是一个例子:</para>
<programlisting>
&lt;address-settings>
&lt;address-setting match="jms.queue.exampleQueue">
&lt;max-size-bytes>100000&lt;/max-size-bytes>
&lt;address-full-policy>BLOCK&lt;/address-full-policy>
&lt;/address-setting>
&lt;/address-settings></programlisting>
<para>上面的例子将JMS队列"exampleQueue"的最大内存值设为
100000 字节并且阻塞发送者以防止消息量超过这个值。</para>
<para>注意必须设置 <literal>BLOCK</literal>的策略才能打开限定发送者窗口控制。</para>
<note><para>请注意默认的配置下当一个地址中的消息量达到10MiB时其所有的消息发送者将变为阻塞状态也就是说
在没有接收的情况下你不能向一个地址不阻塞地一次发送超过10MiB的消息。要想增加这个限制可以加大
<literal>max-size-bytes</literal>参数的值,或者调整地址的消息容量限制。</para>
</note>
</section>
</section>
<section>
<title>速率流控制</title>
<para>ActiveMQ也可以控制发送者发送消息的速率。单位是每秒消息数。通过设定速率可保证发送者的发送速率不超过某个值。</para>
<para>速率必须是一个正整数。如果设为 <literal>-1</literal> 则关闭速率流控制。默认值是<literal>-1</literal></para>
<para>请参见例子<xref linkend="producer-rate-limiting-example"/>进一步了解速率流控制的使用方法。</para>
<section id="flow-control.producer.rate.core.api">
<title>使用核心接口Core API</title>
<para>如果使用核心接口,<literal
>ClientSessionFactory.setProducerMaxRate(int consumerMaxRate)</literal>方法或
某些 <literal>ClientSession.createProducer()</literal>方法可以设置最大速率值。</para>
</section>
<section>
<title>使用 JMS</title>
<para>如果使用JNDI需要配置<literal>activemq-jms.xml</literal>文件:</para>
<programlisting>&lt;connection-factory name="ConnectionFactory">
&lt;connectors>
&lt;connector-ref connector-name="netty-connector"/>
&lt;/connectors>
&lt;entries>
&lt;entry name="ConnectionFactory"/>
&lt;/entries>
&lt;!-- We limit producers created on this connection factory to produce messages
at a maximum rate
of 10 messages per sec -->
&lt;producer-max-rate>10&lt;/producer-max-rate>
&lt;/connection-factory></programlisting>
<para>如果直接实例化连接工厂,则使用<literal>ActiveMQConnectionFactory.setProducerMaxRate(int
consumerMaxRate)</literal>方法来设置。</para>
</section>
</section>
</section>
</chapter>

View File

@ -1,283 +0,0 @@
<?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="ha">
<title>高可获得性High Availability和失效备援Failover</title>
<para>高可获得性是指<emphasis>当系统中有一台甚至多台服务器发生故障时还能继续运转的能力</emphasis></para>
<para>作为高可获得性的一部分,<emphasis>失效备援</emphasis>的含意是
<emphasis>当客户端当前连接的服务器发故障时,客户端可以将连接转到另一台正常的服务器,从而能够继续工作</emphasis></para>
<section>
<title>主要-备份对</title>
<para>ActiveMQ可以将两个服务器以<emphasis>主要-备份对</emphasis>的形式连接在一起。目前ActiveMQ允许一个
主要服务器有一个备份服务器,一个备份服务器只有一个主要服务器。在正常情况下主要服务器工作,备份服务器只有当
发生失效备援发生时工作。</para>
<para>没有发生失效备援时,主要服务器为客户端提供服务,备份服务器处于待机状态。当客户端在失效备援后连接到备份服务
器时,备份服务器开始激活并开始工作。</para>
<section id="ha.mode">
<title>高可获得性HA的模式</title>
<para>ActiveMQ的高可获得性有两种模式一种模式通过由主服务器日志向备份服务器日志
<emphasis>复制数据</emphasis>。另一种模式则是主服务器与备份服务器间<emphasis>存贮共享</emphasis></para>
<note>
<para>只有持久消息才可以在失效备援时不丢失。所有非持久消息则会丢失。</para>
</note>
<section id="ha.mode.replicated">
<title>数据复制</title>
<para>在这种模式下保存在ActiveMQ主服务器中日志中的数据被复制到备份服务器日志中。注意我们并不复制
服务器的全部状态,而是只复制日志和其它的持久性质的操作。</para>
<para>复制的操作是异步进行的。数据通过流的方式复制,复制的結果则通过另一个流来返回。通过这样的异步方式
我们可以获得比同步方式更大的呑吐量。</para>
<para>当用户得到确认信息如一个事务已经提交、准备或加滚或者是一个持久消息被发送时ActiveMQ确保这些状态
已经复制到备份服务器上并被持久化。</para>
<para>数据复制这种方式不可避免地影响性能但是另一方面它不依赖于昂贵的文件共享设备如SAN。它实际上是
一种<emphasis role="italic">无共享</emphasis>的HA方式。</para>
<para>采用数据复制的失效备援比采用共享存储的失效备援要快,这是因为备份服务器在失效备援时不用重新装载日志。</para>
<graphic fileref="images/ha-replicated-store.png" align="center"/>
<section id="configuring.live.backup">
<title>配置</title>
<para>首先在主服务器的 <literal>activemq-configuration.xml</literal>文件中配置备份服务器。
配置的参数是<literal>backup-connector-ref</literal>。这个参数指向一个连接器。这个连接器
也在主服务器上配置。它定义了如何与备份服务器建立连接。</para>
<para>下面就是一个在<literal>activemq-configuration.xml</literal>文件中的例子:</para>
<programlisting>
&lt;backup-connector-ref connector-name="backup-connector"/>
&lt;connectors>
&lt;!-- 这个连接器用于连接备份服务喝咖啡 -->
&lt;!-- 备份服务器在主机"192.168.0.11"上,端口"5445" -->
&lt;connector name="backup-connector">
&lt;factory-class>org.apache.activemq.core.remoting.impl.netty.NettyConnectorFactory&lt;/factory-class>
&lt;param key="host" value="192.168.0.11"/>
&lt;param key="port" value="5445"/>
&lt;/connector>
&lt;/connectors></programlisting>
<para>其次在备份服务器上,我们设置了备份服务器的标志,并且配置了相应的接受器以便主服务器能够建立
连接。同时我们将shared-store参数设为false。</para>
<programlisting>
&lt;backup>true&lt;/backup>
&lt;shared-store>false&lt;shared-store>
&lt;acceptors>
&lt;acceptor name="acceptor">
&lt;factory-class>org.apache.activemq.core.remoting.impl.netty.NettyConnectorFactory&lt;/factory-class>
&lt;param key="host" value="192.168.0.11"/>
&lt;param key="port" value="5445"/>
&lt;/acceptor>
&lt;/acceptors>
</programlisting>
<para>为了使备份服务器正常工作,一定要保证它与主服务器有着同样的桥、预定义的队列、集群连接、
广播组和发现组。最简单的作法是拷贝主服务器的全部配置然后再进行上述的修改。 </para>
</section>
<section>
<title>备份服务器与主服务器间的同步</title>
<para>为了能正常工作,备份服务器与主服务器必须同步。这意谓着备份服务器不能是当前任意一个备份服
务器。如果你这样做,主服务器将不能成功启动,在日志中会出现异常。</para>
<para>要想将一个现有的服务器配置成一个备份服务器,你需要将主服务器的<literal>data</literal>
文件夹拷贝到并覆盖这个备份
服务器的相同文件夹,这样做保证了备份服务器与主服务器的持久化数据完全一致。</para>
<para>当失效备援发生后,备份服务器代替主服务器工作,原来的主服务器失效。这时简单的重启主服务
器是不行的。要想将主服务器与备份重新进行同步,就必须先将主服务器和备份服务器同时停止,再将
主服务器的数据拷贝到备份服务器,然后再启动。</para>
<para>ActiveMQ以后将支持备份与主服务器间的自动同步无需停止主服务器。</para>
</section>
</section>
<section id="ha.mode.shared">
<title>存贮共享</title>
<para>使用存贮共享,主服务器与备份服务器共用<emphasis>相同</emphasis>目录的日志数据,通常是一个共享的
文件系统。这包括转存目录,日志目录,大消息及绑定日志。</para>
<para>当发生失效备援时,工作由备份服务器接管。它首先从共享的文件系统中读取主服务器的持久数据,然后
才能接受客户端的连接请求。</para>
<para>与数据复制方式不同的是这种方式需要一个共享的文件系统,主服务器与备份服务器都可以访问。典型的
高性能的共享系统是存贮区域网络SAN系统。我们不建议使用网络附加存贮NAS如NFS来存贮共享
日志(主要的原因是它们比较慢)。</para>
<para>共享存贮的优点是不需要在主服务器与备份服务器之间进行数据复制,因此对性能不会造成影响。</para>
<para>共享存贮的缺点是它需要一个共享文件系统。同时,当备份服务器激活时它需要首先从共享日志中读取相应
的信息,从而占用一定的时间。</para>
<para>如果你需要在一般工作情况下保持高性能并且拥有一个快速的SAN系统同时能够容忍较慢的失效备援
过程(取决于数据量在多少),我们建议你采用存贮共享方式的高可获得性。</para>
<graphic fileref="images/ha-shared-store.png" align="center"/>
<section id="ha/mode.shared.configuration">
<title>配置</title>
<para>要使用存贮共享模式,在两个服务器的配置文件<literal>activemq-configuration.xml</literal>
中将作如下设置:</para>
<programlisting>
&lt;shared-store>true&lt;shared-store>
</programlisting>
<para>此外,备份服务器必须显式地指定:</para>
<programlisting>
&lt;backup>true&lt;/backup>
</programlisting>
<para>另外,需要将主服务器和备份服务器的日志文件位置指向<emphasis>同一个共享位置</emphasis>
(参见<xref linkend="configuring.message.journal"/></para>
<para>如果客户端使用JMS自动失效备援主服务器除了要配置一个连接器以连接到备份服务器外还要在
配置文件<literal>activemq-jms.xml</literal>中指向这个连接器,如
<xref linkend="ha.automatic.failover"/>中所解释的那样。</para>
</section>
<section>
<title>备份服务器与主服务器间的同步。</title>
<para>由于主备服务器之间共享存贮,所以它们不需要进行同步。但是它需要主备服务器同时工作以提供
高可获得性。如果一量发生失效备援后,就需要在尽可能早的时间内将备份服务器(处于工作状态)停下来,
然后再启动主服务器和备份服务器。</para>
<para>ActiveMQ以后将支持自动同步功能不需要先停止服务器。</para>
</section>
</section>
</section>
</section>
<section id="failover">
<title>失效备援的模式</title>
<para>ActiveMQ定义了两种客户端的失效备援</para>
<itemizedlist>
<listitem>
<para>自动客户端失效备援</para>
</listitem>
<listitem>
<para>应用层的客户端失效备援</para>
</listitem>
</itemizedlist>
<para>ActiveMQ还支持100透明的同一个服务器的自动连接恢复适用于网络的临时性故障。这与失效备援很相似
只不过连接的是同一个服务器,参见<xref linkend="client-reconnection"/></para>
<para>在发生失效备援时,如果客户端有非持久或临时队列的接收者时,这些队列会自动在备份服务器上重新创建。对于
非持久性的队列,备份服务器事先是没有它们的信息的。</para>
<section id="ha.automatic.failover">
<title>自动客户端失效备援</title>
<para>ActiveMQ的客户端可以配置主备份服务器的信息当客户端与主服务器的连接发生故障时可以自动检测到故障并
进行失效备援处理,让客户端连接到备份服务器上。备份服务器可以自动重新创建所有在失效备援之前存在的会话与接收
者。客户端不需要进行人工的连接恢复工作,从而节省了客户端的开发工作。</para>
<para>ActiveMQ的客户端在参数<literal>client-failure-check-period</literal>(在
<xref linkend="connection-ttl"/>中进行了解释)规定的时间内如果没有收到数据包,则认为连接发生故障。
当客户端认为连接故障时,它就会尝试进行失效备援。</para>
<para>ActiveMQ有几种方法来为客户端配置主备服务器对的列表。可以采用显式指定的方法或者采用更为常用的
<emphasis>服务器发现</emphasis>的方法。有关如何配置服务器发现的详细信息,请参见
<xref linkend="clusters.server-discovery"/>
关于如何显式指定主/备服务器对的方法,请参见<xref linkend="clusters.static.servers"/>中的解释。</para>
<para>要使客户端具备自动失效备援,在客户端的配置中必须要指定重试的次数要大于零(参见
<xref linkend="client-reconnection"/>中的解释)。</para>
<para>有时你需要在主服务器正常关机的情况下仍然进行失效备援。如果使用JMS你需要将<literal
>ActiveMQConnectionFactory</literal><literal
>FailoverOnServerShutdown</literal>属性设为true或者是在<literal
>activemq-jms.xml</literal>参数为failover-on-server-shutdown文件中进行相应的配置。如果使用的是核心接口可以在创建
<literal>ClientSessionFactoryImpl</literal>实例时将上述同名属性设置为true。
这个属性的默认值是false。这表示如果主服务器是正常关机<emphasis>客户端将不会进行失效备援</emphasis></para>
<para>
<note>
<para>默认正常关机<emphasis role="bold">不会</emphasis>不会导致失效备援。</para>
<para>使用CTRL-C来关闭ActiveMQ服务器或JBoss应用服务器属于正常关机所以不会触发客户端的失效
备援。</para>
<para>要想在这种情况下进行失效备援必须将属性<literal>FailoverOnServerShutdown</literal>
设为true。</para>
</note>
</para>
<para>默认情况下至少创建了一个与主服务器的连接后失效备援才会发生。换句话说,如果客户端每一次创建与
主服务器的连接失败它会根据参数reconnection-attempts的设置进行连接重试而不是进行失效备援。
如果重试次数超过的该参数的值,则连接失败。</para>
<para>在有些情况下,你可能希望在初始连接失败和情况下自动连接到备份服务器,那么你可以直接在
<literal>ClientSessionFactoryImpl</literal><literal
>ActiveMQConnectionFactory</literal>上设置<literal>FailoverOnInitialConnection</literal>
参数,或者在配置文件中设置<literal
>failover-on-initial-connection</literal>。默认的值是<literal>false</literal></para>
<para>有关事务性及非事务性JMS会话的自动失效备援的例子请参见
<xref linkend="examples.transaction-failover"/><xref
linkend="examples.non-transaction-failover"/>。</para>
<section id="ha.automatic.failover.noteonreplication">
<title>关于服务器的复制</title>
<para>ActiveMQ在主服务器向备份服务器复制时并不复制服务器的全部状态。所以当一个会话在备份服务器
中重新创建后,它并不知道发送过的消息或通知过的消息。在失效备援的过程中发生的消息发送或通知也可
能丢失。</para>
<para>理论上如果进行全部状态的复制我们可以提供100的透明的失效备援不会失去任何的消息或通知。
但是这样做要付出很大的代价:即所有信息都要进行复制(包括队列,会话等等)。也就是要求复制服务
器的每个状态信息,主服务器的每一步操作都将向其备份进行复制,并且要在全局内保持顺序的一致。这样
做就极难保证高性能和可扩展性,特别是考虑到多线程同时改变主服务器的状态的情况,要进行全状态复制
就更加困难。</para>
<para>一些技术可以用来实现全状态复制,如<emphasis role="italic">虚拟同步技术
virtual synchrony</emphasis>。但是这些技术往往没有很好的可扩展性,并且将所有操作都
进行序列化,由单一线程进行处理,这样明显地将底了并行处理能力。</para>
<para>另外还有其它一些多线程主动复制技术比如复制锁状态或复制线程调度等。这些技术使用Java语言非常
难于实现。</para>
<para>因此得出结论采用大量牺牲性能来换取100透明的失效备援是得不偿失的。没有100透明的失效
备援我们仍然可以轻易地保证一次且只有一次的传递。这是通过在发生故障时采用重复检测结合事务重试
来实现的。</para>
</section>
<section id="ha.automatic.failover.blockingcalls">
<title>失效备援时阻塞调用的处理</title>
<para>如果当发生失效备援时客户端正面进行一个阻塞调用并等待服务器的返回,新创建的会话不会知道这个调用,
因此客户端可能永远也不会得到响应,也就可能一直阻塞在那里。</para>
<para>为了防止这种情况的发生ActiveMQ在失效备援时会解除所有的阻塞调用同时抛出一个
<literal>javax.jms.JMSException</literal>异常如果是JMS<literal
>ActiveMQException</literal>异常。异常的错误代码是<literal
>ActiveMQException.UNBLOCKED</literal>。客户端需要自行处理这个异常,并且进行
必要的操作重试。</para>
<para>如果被解除阻塞的调用是commit()或者prepare()那么这个事务会被自动地回滚并且ActiveMQ
会抛出一个<literal>javax.jms.TransactionRolledBackException</literal>如果是JMS
或都是一个<literal>ActiveMQException</literal>,错误代码为 <literal
>ActiveMQException.TRANSACTION_ROLLED_BACK</literal>(如果是核心接口)。</para>
</section>
<section id="ha.automatic.failover.transactions">
<title>事务的失效备援处理</title>
<para>如果在一个事务性会话中,在当前事务中消息已经发出或通知,则服务器在这时如果发生失效备援,它不
能保证发出的消息或通知没有丢失。</para>
<para>因此这个事务就会被标记为只回滚,任何尝试提交的操作都会抛出一个<literal
>javax.jms.TransactionRolledBackException</literal>异常如果是JMS或者是一
<literal>ActiveMQException</literal>的异常,错误代码为<literal
>ActiveMQException.TRANSACTION_ROLLED_BACK</literal>(如果是核心接口)。</para>
<para>客户端需要自行处理这些异常,进行必要的回滚处理。注意这里不需要人工将会话进行回滚-此时它已经
被回滚了。用户可以通过同一个会话重试该事务操作。</para>
<para>ActiveMQ发布包中包括了一个完整的例子来展示如何处理这种情况。参见
<xref linkend="examples.transaction-failover"/></para>
<para>如果是在提交过程中发生了失效备援,服务器将这个阻塞调用解除。这种情况下客户端很难确定在事故发生
之前事务是否在主服务器中得到了处理。</para>
<para>为了解决这个问题,客户端可以在事务中使用重复检测(<xref linkend="duplicate-detection"/>
,并且在提交的调用被解除后重新尝试事务操作。如果在失效备援前事务确实在主服务器上已经完成提交,那么
当事务进行重试时,重复检测功能可以保证重复发送的消息被丢弃,这样避免了消息的重复。</para>
<note>
<para>通过处理异常和重试适当处理被解除的阻塞调用并配合重复检测功能ActiveMQ可以在故障条件下保证
一次并且只有一次的消息传递,没有消息丢失和消息重复。</para>
</note>
</section>
<section id="ha.automatic.failover.nontransactional">
<title>非事务会话的失效备援处理</title>
<para>如果会话是非事务性的,那么通过它的消息或通知在故障时可能会丢失。</para>
<para>如果你在非事务会话中要保证<emphasis role="italic">一次并且只有一次</emphasis>
的消息传递,你需要使用重复检测功能,并适当处理被解除的阻塞调用。参见 <xref
linkend="ha.automatic.failover.blockingcalls"/>。</para>
</section>
</section>
<section>
<title>连接故障的通知</title>
<para>JMS提供了标准的异步接收连接故障通知的机制<literal>java.jms.ExceptionListener</literal>
请参考JMS的javadoc或者其它JMS教程来进一步了解怎样使用这个接口。</para>
<para>ActiveMQ的核心接口也提供了一个相似的接口
<literal>org.hornet.core.client.SessionFailureListener</literal></para>
<para>任何ExceptionListener或SessionFailureListener的实例在发生连接故障时都会被ActiveMQ
调用,<emphasis role="bold">不管</emphasis>该连接是否得到了失效备援、重新连接还是得到了恢复。</para>
</section>
<section>
<title>应用层的失效备援</title>
<para>在某些情况下你可能不需要自动的客户端失效备援,希望自己来处理连接故障,使用自己的重新连接方案等。
我们把它称之为<emphasis>应用层</emphasis>失效备援,因为它是发生在应用层的程序中。</para>
<para>为了实现应用层的失效备援你可以使用监听器listener的方式。如果使用的是JMS你需要在JMS连接上
设置一个<literal>ExceptionListener</literal>类。这个类在连接发生故障时由ActiveMQ调用。在这个类
中你可以将旧的连接关闭使用JNDI查找新的连接工厂并创建新的连接。这里你可以使用
<ulink url="http://www.jboss.org/community/wiki/JBossHAJNDIImpl">HA-JNDI</ulink>
来保证新的连接工厂来自于另一个服务器。</para>
<para>请参见<xref
linkend="application-level-failover"/>。这是一个完整的应用层失效备援的例子。</para>
<para>如果你使用核心接口,则过程也是很相似的:你在核心的<literal>ClientSession</literal>实例上设置一个
<literal>FailureListener</literal>,然后在这个类中进行相应的处理即可。</para>
</section>
</section>
</chapter>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@ -1,71 +0,0 @@
<?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="intercepting-operations">
<title>拦截操作</title>
<para>ActiveMQ支持<emphasis>拦截器</emphasis>。拦截器可以拦截进入服务器的数据包。每进入服务器
一个数据包,拦截器就被调用一次,允许一些特殊和处理,例如对包的审计、过滤等。拦截器可以对数据包
进行改动。</para>
<section>
<title>实现拦截器</title>
<para>拦截器必须要实现<literal>Interceptor接口</literal></para>
<programlisting>
package org.apache.activemq.api.core.interceptor;
public interface Interceptor
{
boolean intercept(Packet packet, RemotingConnection connection)
throws ActiveMQException;
}
</programlisting>
<para>它的方法的返回值是很重要的:</para>
<itemizedlist>
<listitem>
<para>如果返回<literal>true</literal>,处理正常进行下去。</para>
</listitem>
<listitem>
<para>如果返回<literal>false</literal>,则处理被中断,其它的拦截器将不会被调用,数据包将不会
被服务器所处理。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>配置拦截器</title>
<para>拦截器的配置在<literal>activemq-configuration.xml</literal>文件中:</para>
<programlisting>
&lt;remoting-interceptors&gt;
&lt;class-name&gt;org.apache.activemq.jms.example.LoginInterceptor&lt;/class-name&gt;
&lt;class-name&gt;org.apache.activemq.jms.example.AdditionalPropertyInterceptor&lt;/class-name&gt;
&lt;/remoting-interceptors&gt;
</programlisting>
<para>拦截器的类以及它们依赖的类必须要在服务器的classpath中否则不能被正常初始化及调用。</para>
</section>
<section>
<title>客户端拦截器</title>
<para>在客户端也可以有拦截器来拦截<emphasis>来自服务器</emphasis>的数据包。<code>ClientSessionFactory</code>
<code>addInterceptor()</code>方法可以用来添加拦截器。</para>
<para>同样拦截器的类以及它们依赖的类必须要在客户端的classpath中否则它们不能被正常初始化及调用。</para>
</section>
<section>
<title>例子</title>
<para>参见<xref linkend="examples.interceptor" />。这个例子中展示了如何使用拦截器向发往服务器的消息中
添加属性。</para>
</section>
</chapter>

View File

@ -1,142 +0,0 @@
<?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="interoperability">
<title>互操作性</title>
<section id="stomp">
<title>Stomp</title>
<para><ulink url="http://stomp.codehaus.org/">Stomp</ulink>是一个基于文本的协议。使用Stomp协议的
客户端可以与Stomp的代理broker进行通迅。</para>
<para><ulink url="http://stomp.codehaus.org/Clients">Stomp客户端</ulink>支持多种语言和平台,因此
它有着很好的互操作性。</para>
<section id="stomp.native">
<title>内建Stomp支持</title>
<para>ActiveMQ内建支持Stomp功能。要使用Stomp发送与接收消息必须配置一个<literal>NettyAcceptor</literal>
其中的<literal>protocol</literal>参数值应设为<literal>stomp</literal></para>
<programlisting>
&lt;acceptor name="stomp-acceptor">
&lt;factory-class>org.apache.activemq.core.remoting.impl.netty.NettyAcceptorFactory&lt;/factory-class> &lt;param key="protocol" value="stomp"/>
&lt;param key="port" value="61613"/>
&lt;/acceptor>
</programlisting>
<para>有了上述的配置ActiveMQ就可以在端口<literal>61613</literal>这是Stomp代理的默认端口接受Stomp连接了。</para>
<para><literal>stomp</literal>例子展示了如何在ActiveMQ中配置Stomp。</para>
<section>
<title>限制</title>
<para>消息的通知不是事务性的。ACK信号不能作为事务的一部分来传输如果设置了<literal>transaction</literal>
属性,它将被忽略)。</para>
</section>
</section>
<section>
<title>Stomp目标与ActiveMQ的地址和队列的映射</title>
<para>Stomp客户端在消息发送和订阅中使用的是<emphasis>目标destination</emphasis>。目标名称是简单的字符串,对应的是服务
器端的目的地。不同服务器对这种映射有着不同的实现。</para>
<para>在ActiveMQ中这些目标被映射为<emphasis>地址</emphasis><emphasis>队列</emphasis>
当一个Stomp客户端发送一个消息使用<literal>SEND</literal>信号)到一个目标时,这个目标被映射到一个地址。
如果一个Stomp客户端订阅或解除订阅一个目标时使用<literal>SUBSCRIBE</literal>
<literal>UNSUBSCRIBE</literal>这个目标被映射到一个ActiveMQ的队列。</para>
</section>
<section>
<title>Stomp与JMS的互操作性</title>
<section>
<title>使用JMS目标</title>
<para>正如<xref linkend="jms-core-mapping" />解释的那样JMS的目标同样映射到ActiveMQ的地址与队列。如果你使用
Stomp向JMS的目标发送消息那么Stomp的目标必须要遵照相同的命名规则</para>
<itemizedlist>
<listitem>
<para>如果向JMS<emphasis>队列</emphasis>发送数据或订阅它,则队列的名称前缀必须是<literal>jms.queue.</literal></para>
<para>例如,如果向名为<literal>orders</literal>的JMS队列发送消息Stomp客户端必须发送以下信息</para>
<programlisting>
SEND
destination:jms.queue.orders
hello queue orders
^@
</programlisting>
</listitem>
<listitem>
<para>如果向JMS <emphasis>话题topic</emphasis>发送或订阅消息,话题名称前缀必须是<literal>jms.topic.</literal></para>
<para>例如,如果订阅名为 <literal>stocks</literal>的JMS话题Stomp客户端必须发送以下信息</para>
<programlisting>
SUBSCRIBE
destination:jms.topic.stocks
^@
</programlisting>
</listitem>
</itemizedlist>
</section>
<section>
<title>使用JMS或核心接口发送和接收Stomp消息</title>
<para>Stomp基本上是一个基于文本的协议。为了使用更简单我们的Stomp实现通过检查<literal>content-length</literal>的值
来决定如何将一个Stomp消息映射成一个JMS消息或核心消息。
</para>
<para>如果在Stomp消息中<emphasis>没有</emphasis><literal>content-length</literal>它将被映射为一个JMS的
<emphasis>TextMessage</emphasis>或者是一个核心消息其消息体的缓存是一个SimpleString。</para>
<para>如果Stomp消息中<emphasis></emphasis><literal>content-length</literal>则它被映射为一个JMS的
<emphasis>BytesMessage</emphasis>或者是一个核心消息其消息体缓存中是一个字节数组byte[]。</para>
<para>从一个JMS消息或核心消息映射为Stomp消息时遵从同样的逻辑。一个Stomp客户端可以通过检查
<literal>content-length</literal>来决定消息体的类型(字符串或字节)。</para>
</section>
</section>
<section id="stomp.websockets">
<title>通过Web Sockets使用Stomp</title>
<para>ActiveMQ还支持通过<ulink url="http://dev.w3.org/html5/websockets/">Web Sockets</ulink>使用Stomp。任何支持
Web Socket的浏览器中可以利用ActiveMQ来发送和接收Stomp消息。</para>
<para>要使用些功能,必须配置一个<literal>NettyAcceptor</literal>,并设置<literal>protocol</literal>
的值为<literal>stomp_ws</literal></para>
<programlisting>
&lt;acceptor name="stomp-ws-acceptor">
&lt;factory-class>org.apache.activemq.core.remoting.impl.netty.NettyAcceptorFactory&lt;/factory-class>
&lt;param key="protocol" value="stomp_ws"/>
&lt;param key="port" value="61614"/>
&lt;/acceptor>
</programlisting>
<para>使用上面配置ActiveMQ在URL路径<literal>/stomp</literal>下端口<literal>61614</literal>接收Stomp连接。
浏览器然后就可以连接到<literal>ws://&lt;server&gt;:61614/stomp</literal>使用Web Socket来发送和接收
Stomp消息了。</para>
<para>为了简化客户端的开发,在<ulink url="http://github.com/jmesnil/stomp-websocket">GitHub</ulink>
上提供了一个JavaScript库参见<ulink url="http://jmesnil.net/stomp-websocket/doc/">文档</ulink>)。</para>
<para><literal>stomp-websockets</literal>例子给出一如何配置ActiveMQ服务器以使浏览器和Java应用程序通过一个JMS话题
进行消息的传递。</para>
</section>
<section id="stompconnect">
<title>StompConnect</title>
<para><ulink url="http://stomp.codehaus.org/StompConnect">StompConnect</ulink>是一个Stomp代理服务器
它可以将Stomp协议转换为标准的JMS接口调用。因此通过StompConnect的作用ActiveMQ可以作为一个Stomp代理
与任何一个Stomp客户端通迅。这些客户端可以由C、C++、C#及.net等语言实现。</para>
<para>要运行StompConnect首先要启动ActiveMQ服务以及JNDI服务。</para>
<para>Stomp需要<literal>jndi.properties</literal>文件要在classpath中。该文件
应有如下类似的内容:</para>
<programlisting>java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=jnp://localhost:1099
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces</programlisting>
<para>要确保该文件与StompConnect的jar包以及ActiveMQ的jar文件都在classpath中。最后运行
<literal>java org.codehaus.stomp.jms.Main</literal></para>
</section>
</section>
<section>
<title>REST</title>
<para>ActiveMQ即将支持REST</para>
</section>
<section>
<title>AMQP</title>
<para>ActiveMQ即将支持AMQP</para>
</section>
</chapter>

View File

@ -1,382 +0,0 @@
<?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="jms-bridge">
<title>JMS桥Bridge</title>
<para>ActiveMQ提供了JMS消息桥服务。</para>
<para>桥的作用是从一个消息源队列或话题topic接收消息然后将它们发送到一个目标队列或话题。通常源和
目的不在同一台服务器上。</para>
<para>作为消息源的服务器与目的服务器不必在同一个集群内。通过桥的作用,两台服务器可以通过非可靠的网络连接
起来比如WAN。</para>
<para>桥可以作为单独的服务部署或者部署于ActiveMQ单独服务器内或者部署在JBoss应用服务器中。源或目的可以
在同一个VM中也可以在其它的VM中。</para>
<para>桥还可以在ActiveMQ服务器与其它JMS 1.1 兼容的服务器之间进行消息的传递。
<note><para>还要将JMS桥与核心桥混淆。JMB桥可以连接两个JMS 1.1兼容的服务器它使用的是JMS接口。
而核心桥(在<xref linkend="core-bridges"/>中描述使用核心API将两个ActiveMQ实例连接
起来。核心桥的性能通常要比JMS桥的性能高所以尽可能使用核心桥。另外核心桥可以不用XA
就可以实现<emphasis>一次并只有一次</emphasis>的消息传递保证。</para></note></para>
<para>桥可以适当处理连接故障。当源的连接或目的的连接发生故障时,例如网络故障,桥将不断重试连接直到连接
恢复为止。当连接恢复后,桥会继续正常工作。</para>
<para>桥还可以有一个可选的JMS选择器它可以使桥只接收选择器选择的消息。</para>
<para>可以配置桥从队列还是从话题中接收消息。如果配置成从话题中接收消息,还以设定是以非持久订阅的方式接收,还是
以持久订阅的方式接收。</para>
<para>通常桥是通过一个bean配置文件由JBoss Micro Container部署到JBoss应用服务器中。下面的就是一
个桥的bean文件例子。这个桥将同一服务器上的两个目标连接起来。</para>
<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?>
&lt;deployment xmlns="urn:jboss:bean-deployer:2.0">
&lt;bean name="JMSBridge" class="org.apache.activemq.api.jms.bridge.impl.JMSBridgeImpl">
&lt;!-- ActiveMQ must be started before the bridge -->
&lt;depends>ActiveMQServer&lt;/depends>
&lt;constructor>
&lt;!-- Source ConnectionFactory Factory -->
&lt;parameter>
&lt;inject bean="SourceCFF"/>
&lt;/parameter>
&lt;!-- Target ConnectionFactory Factory -->
&lt;parameter>
&lt;inject bean="TargetCFF"/>
&lt;/parameter>
&lt;!-- Source DestinationFactory -->
&lt;parameter>
&lt;inject bean="SourceDestinationFactory"/>
&lt;/parameter>
&lt;!-- Target DestinationFactory -->
&lt;parameter>
&lt;inject bean="TargetDestinationFactory"/>
&lt;/parameter>
&lt;!-- Source User Name (no username here) -->
&lt;parameter>&lt;null />&lt;/parameter>
&lt;!-- Source Password (no password here)-->
&lt;parameter>&lt;null />&lt;/parameter>
&lt;!-- Target User Name (no username here)-->
&lt;parameter>&lt;null />&lt;/parameter>
&lt;!-- Target Password (no password here)-->
&lt;parameter>&lt;null />&lt;/parameter>
&lt;!-- Selector -->
&lt;parameter>&lt;null />&lt;/parameter>
&lt;!-- Failure Retry Interval (in ms) -->
&lt;parameter>5000&lt;/parameter>
&lt;!-- Max Retries -->
&lt;parameter>10&lt;/parameter>
&lt;!-- Quality Of Service -->
&lt;parameter>ONCE_AND_ONLY_ONCE&lt;/parameter>
&lt;!-- Max Batch Size -->
&lt;parameter>1&lt;/parameter>
&lt;!-- Max Batch Time (-1 means infinite) -->
&lt;parameter>-1&lt;/parameter>
&lt;!-- Subscription name (no subscription name here)-->
&lt;parameter>&lt;null />&lt;/parameter>
&lt;!-- Client ID (no client ID here)-->
&lt;parameter>&lt;null />&lt;/parameter>
&lt;!-- Add MessageID In Header -->
&lt;parameter>true&lt;/parameter>
&lt;!-- register the JMS Bridge in the AS MBeanServer -->
&lt;parameter>
&lt;inject bean="MBeanServer"/>
&lt;/parameter>
&lt;parameter>org.apache.activemq:service=JMSBridge&lt;/parameter>
&lt;/constructor>
&lt;property name="transactionManager">
&lt;inject bean="RealTransactionManager"/>
&lt;/property>
&lt;/bean>
&lt;!-- SourceCFF describes the ConnectionFactory used to connect to the
source destination -->
&lt;bean name="SourceCFF"
class="org.apache.activemq.api.jms.bridge.impl.JNDIConnectionFactoryFactory">
&lt;constructor>
&lt;parameter>
&lt;inject bean="JNDI" />
&lt;/parameter>
&lt;parameter>/ConnectionFactory&lt;/parameter>
&lt;/constructor>
&lt;/bean>
&lt;!-- TargetCFF describes the ConnectionFactory used to connect to the
target destination -->
&lt;bean name="TargetCFF"
class="org.apache.activemq.api.jms.bridge.impl.JNDIConnectionFactoryFactory">
&lt;constructor>
&lt;parameter>
&lt;inject bean="JNDI" />
&lt;/parameter>
&lt;parameter>/ConnectionFactory&lt;/parameter>
&lt;/constructor>
&lt;/bean>
&lt;!-- SourceDestinationFactory describes the Destination used as the source -->
&lt;bean name="SourceDestinationFactory"
class="org.apache.activemq.api.jms.bridge.impl.JNDIDestinationFactory">
&lt;constructor>
&lt;parameter>
&lt;inject bean="JNDI" />
&lt;/parameter>
&lt;parameter>/queue/source&lt;/parameter>
&lt;/constructor>
&lt;/bean>
&lt;!-- TargetDestinationFactory describes the Destination used as the target -->
&lt;bean name="TargetDestinationFactory"
class="org.apache.activemq.api.jms.bridge.impl.JNDIDestinationFactory">
&lt;constructor>
&lt;parameter>
&lt;inject bean="JNDI" />
&lt;/parameter>
&lt;parameter>/queue/target&lt;/parameter>
&lt;/constructor>
&lt;/bean>
&lt;!-- JNDI is a Hashtable containing the JNDI properties required -->
&lt;!-- to connect to the sources and targets JMS resrouces -->
&lt;bean name="JNDI" class="java.util.Hashtable">
&lt;constructor class="java.util.Map">
&lt;map class="java.util.Hashtable" keyClass="String"
valueClass="String">
&lt;entry>
&lt;key>java.naming.factory.initial&lt;/key>
&lt;value>org.jnp.interfaces.NamingContextFactory&lt;/value>
&lt;/entry>
&lt;entry>
&lt;key>java.naming.provider.url&lt;/key>
&lt;value>jnp://localhost:1099&lt;/value>
&lt;/entry>
&lt;entry>
&lt;key>java.naming.factory.url.pkgs&lt;/key>
&lt;value>org.jboss.naming:org.jnp.interfaces"&lt;/value>
&lt;/entry>
&lt;entry>
&lt;key>jnp.timeout&lt;/key>
&lt;value>5000&lt;/value>
&lt;/entry>
&lt;entry>
&lt;key>jnp.sotimeout&lt;/key>
&lt;value>5000&lt;/value>
&lt;/entry>
&lt;/map>
&lt;/constructor>
&lt;/bean>
&lt;bean name="MBeanServer" class="javax.management.MBeanServer">
&lt;constructor factoryClass="org.jboss.mx.util.MBeanServerLocator"
factoryMethod="locateJBoss"/>
&lt;/bean>
&lt;/deployment></programlisting>
<section>
<title>JMS桥的配置参数</title>
<para>桥的主要的bean是<literal>JMSBridge</literal>。所有的配置参数需要传递给这个bean的
构造函数。</para>
<note>
<para>如果不想指定某个参数的值(例如匿名认证或没有选择器),将该参数设为<literal>&lt;null
/&gt;</literal>即可。</para>
</note>
<itemizedlist>
<listitem>
<para>源连接工厂的工厂Source ConnectionFactory Factory</para>
<para>这个参数注入一个<literal>SourceCFF</literal>bean由bean文件定义。它被
用来创建<emphasis></emphasis><literal>ConnectionFactory</literal>
</para>
</listitem>
<listitem>
<para>目标连接工厂的工厂Target ConnectionFactory Factory</para>
<para>这个参数注入一个<literal>TargetCFF</literal>bean由bean文件定义。它被
用来创建<emphasis>目的</emphasis><literal>ConnectionFactory</literal>
</para>
</listitem>
<listitem>
<para>源目标工厂Source DestinationFactory</para>
<para>这个参数注入一个<literal>SourceDestinationFactory</literal>bean
bean文件定义。它用来创建<emphasis></emphasis>
<literal>目标Destination</literal>
</para>
</listitem>
<listitem>
<para>目的目标工厂Target DestinationFactory</para>
<para>这个参数注入一个<literal>TargetDestinationFactory</literal>bean
bean文件定义。它用来创建<emphasis>目的</emphasis>
<literal>目标Destination</literal>
</para>
</listitem>
<listitem>
<para>源用户名Source User Name</para>
<para>用于创建到<emphasis></emphasis>的连接的用户名</para>
</listitem>
<listitem>
<para>源密码Source Password</para>
<para>用于创建<emphasis></emphasis>连接的密码</para>
</listitem>
<listitem>
<para>目的用户名Target User Name</para>
<para>用于创建<emphasis>目的</emphasis>连接的用户名</para>
</listitem>
<listitem>
<para>目的密码Target Password</para>
<para>t用于创建<emphasis>目的</emphasis>连接的密码</para>
</listitem>
<listitem>
<para>选择器Selector</para>
<para>这是一个JMS的选择器表达式它用于从源目标接收消息。只有与选择器相匹配的消息才会被桥
转发到目的目标</para>
<para>选择器必须符合<ulink
url="http://java.sun.com/j2ee/1.4/docs/api/javax/jms/Message.html">JMS
选择器语法</ulink></para>
</listitem>
<listitem>
<para>故障重试间隔Failure Retry Interval</para>
<para>代表当桥发现连接故障时在每两次重试连接之间所要等待的时间间隔,单位毫秒</para>
</listitem>
<listitem>
<para>最大重试次数Max Retries</para>
<para>表示桥在发现连接故障时所进行的最大重试次数。超过这个次数,桥就放弃重试。
<literal
>-1</literal>代表一直重试下去</para>
</listitem>
<listitem>
<para>服务质量Quality Of Service</para>
<para>这个参数代表所需要的服务质量模式</para>
<para>有效的值为:</para>
<itemizedlist>
<listitem>
<para><literal>AT_MOST_ONCE</literal></para>
</listitem>
<listitem>
<para><literal>DUPLICATES_OK</literal></para>
</listitem>
<listitem>
<para><literal>ONCE_AND_ONLY_ONCE</literal></para>
</listitem>
</itemizedlist>
<para>有关这些模式的解释,参见<xref linkend="quality-of-service"/></para>
</listitem>
<listitem>
<para>最大批量Max Batch Size</para>
<para>表示桥一次性从源目标最多接收多少消息,并将它们一次发往目的地。它的值必须是
<literal>>= 1</literal>
</para>
</listitem>
<listitem>
<para>最大批时间Max Batch Time</para>
<para>代表桥在将一批消息发向目的之前等待的最大毫秒数。这个时间过后,即使接收的消息数小于
<literal>MaxBatchSize</literal>,桥也会开始向目的发送消息。它的值必须是
<literal>-1</literal> (代表永远等待)或<literal>>= 1</literal></para>
</listitem>
<listitem>
<para>订阅名Subscription Name</para>
<para>如果源的目标是一个话题topic你想使用持久的订阅来接收消息的话这个参数可以指定
订阅名。</para>
</listitem>
<listitem>
<para>客户IDClient ID</para>
<para>如果源的目标是一个话题topic你想使用持久的订阅来接收消息的话这个参数可以指定
JMS的客户ID。它用于创建查找持久订阅。</para>
</listitem>
<listitem>
<para>在消息头添加MessageIDAdd MessageID In Header</para>
<para>如果值为<literal>true</literal>原始的消息ID在发往目的是回到消息的名为<literal
>HORNETQ_BRIDGE_MSG_ID_LIST</literal>的头中。如果一个消息被桥转发了多次,
则每次转发的消息ID都添加在这个头中。这用于分布式请求回答的消息模式。</para>
<note>
<para>当收到一个消息时通过它的相关IDcoorelation id可以发送一个回答。这样
在消息发送方得到这个回答消息时,它可以与原消息相关联起来。</para>
</note>
</listitem>
<listitem>
<para>MBean服务器MBean Server</para>
<para>要使用JMX管理JMS桥需指定JMS桥所要注册的MBeanServer如JVM Platform MBeanServer 或
JBoss 应用服务器的MBeanServer</para>
</listitem>
<listitem>
<para>ObjectName</para>
<para>设置了MBeanServer后你还需要设置JMS桥MBean注册用的ObjectName必须是唯一的</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>源和目的的连接工厂</title>
<para>源工目的的连接工厂分别用于创建到源和到目的的连接。</para>
<para>上面的配置例子中使用的是ActiveMQ提供的默认实现。它使用JNDI查找连接工厂。对于其它的应用服务器
或JMS提供者需要实现相应的实现即实现<literal
>org.apache.activemq.jms.bridge.ConnectionFactoryFactory</literal>接口。</para>
</section>
<section>
<title>源和目的的目标工厂</title>
<para>它们用来创建或查找相应的目标。</para>
<para>上面例子中我们使用了ActiveMQ的默认实现从JNDI中查找相应的对象。</para>
<para>要提供新的实现,只要实现接口<literal
>org.apache.activemq.jms.bridge.DestinationFactory</literal>即可。</para>
</section>
<section id="quality-of-service">
<title>服务质量</title>
<para>下面给是桥的三种服务质量的详细说明。</para>
<section>
<title>AT_MOST_ONCE</title>
<para>这种QoS模式表示的是消息最多送达目标一次。在消息发往目的之前消息就会被通知。因此
如果在消息被源删除但并未到达目的时发生故障,消息有可能丢失。所以说消息的
发送最多一次。 </para>
<para>这个模式适用于持久或非持久的消息。</para>
</section>
<section>
<title>DUPLICATES_OK</title>
<para>在这个QoS模式下消息从源接收后再发送到目的之后才对源进行消息通知。这样如果在发送成功之后
消息通知前的时间内发生故障的话,在故障恢复时同一个消息可能被再次传递。結果可能是在目的处
该消息收到了两次。</para>
<para>这个模式适用于持久或非持久的消息。</para>
</section>
<section>
<title>ONCE_AND_ONLY_ONCE</title>
<para>这个模式保证消息从源发送到目的一次,并且只有一次。(有时这个模式又称为“只一次”)。若源与目的处于
同一个ActiveMQ服务器中这个模式通过本地事务来保证消息的发送和通知。如果是在不同的服务器上
则会使用一个JTA的事务将发送和接收包括其中。这里使用的JTA事务是JBoss的实现它包含有一个
完整的事务恢复管理器所以能提供高度可靠的持久性。如果使用JTA则桥的所有连接工厂必须是
XAConnectionFactory。这种模式的效率通常是最低的因为它需要额外记录事务的日志。</para>
<para>这个模式只适用于持久性消息。</para>
<note>
<para>某些情况下可以不使用ONCE_AND_ONLY_ONCE模式而同样可以保证“一次且只一次”的效果。
这是通过使用DUPLICATES_OK模式加上在目的端应用程序来检测重复的消息如果有则将其丢弃。
一些JMS服务器本身提供自动重复消息检测的功能这样节省了在应用层实现的工作。在应用层常见
的实现方法是将接收到的消息的ID存放到缓存文件中然后与每个新到的消息进行对比。这种方式
可能在某段时间内有效所以它不如ONCE_AND_ONLY_ONCE那样严格它视具体情况也可能是一个
不错的选择。</para>
</note>
</section>
<section>
<title>JMS bridge中的超时问题</title>
<para>有时候目标服务器或源服务器会连接不上,这里桥就会尝试重新连接。重新连接的次数由<literal>Max Retries</literal>
参数决定,两次重新连接的时间间隔由<literal>Failure Retry Interval</literal>定义。</para>
<para>在重新尝试时会进行JNDI的查找。ActiveMQ的JNDI使用的是JBoss的实现如果在JNDI查找过程中网络出现故障
查找的操作可能挂起。为了避免这种情况的发生我们可以为JNDI设置适当的超时。这是通过定义初始上下文的
<literal>jnp.timeout</literal>参数和<literal>jnp.sotimeout</literal>参数来
实现的。第一个参数定义了初始连接的超时,第二个参数定义的是套接字的读取超时。</para>
<note>
<para>一旦JNDI连接成功所有调用都是通过RMI完成。如果你想要控制RMI连接的超时你需要定义相应的系统变量。
JBoss使用Sun的RMI实现它的控制参数可以在<ulink
url="http://java.sun.com/j2se/1.5.0/docs/guide/rmi/sunrmiproperties.html">这里</ulink>找到。
默认的连接超时是10秒默认的读超时是18秒。</para>
</note>
<para>如果你使用自己的实现来查找JMS资源你需要注意超时问题。</para>
</section>
<section>
<title>例子</title>
<para>参见<xref linkend="examples.javaee.jms-bridge"/>。这个例子展示了如何在JBoss应用服务器中配置并使用
JMS桥从一处目标将消息转发到另一个目标。</para>
<para>关于如何在两个单独ActiveMQ服务器间使用桥的例子请参见<xref linkend="examples.jms.jms-bridge"/></para>
</section>
</section>
</chapter>

View File

@ -1,42 +0,0 @@
<?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="jms-core-mapping">
<title>JMS与内核API之间的映射关系</title>
<para>本意讲述JMS的目标实体destination如何映射到ActiveMQ的地址addresses</para>
<para>ActiveMQ的内核没有JMS的任何实现。在内核中没有topic的概念它是通过在一个地址上相当于topic的名字绑定
零或多个queue来实现JMS topic的功能的。每个绑定的queue相当于该topic的一个订阅subscription
类似地通过在一个地址上相当于queue的名字绑定单一的queue就可以实现JMS queue的功能。</para>
<para>按照惯例所有的JMS queue所对应的内核queue的名字都以<literal>jms.queue.</literal>做为开头。比如
当JMS queue的名字是"orders.europe"时其对应的内核queue的名字应该是"jms.queue.orders.europe"。
那么内核queue所绑定的地址的名字和该内核queue的名字是相同的。</para>
<para>同样所有JMS topic所对应的内核地址的名字都以 "jms.topic."为前缀。比如当一个JMS topic的名字是"news.europe"
时,它对应的内核的地址应该是"jms.topic.news.europe"。</para>
<para>换句话说就是如果你向JMS queue “orders.europe"发送一个消息这个消息就会被路由到绑定在内核地址为“jms.queue.orders.europe”
的同名内核queue中。 如果是向JMS topic “news.europe“发送一个消息它会被路由到绑定到内核地址为
”jms.topic.news.europe“的所有的内核queue中。</para>
<para>具体要配置一个名为“orders.europe"的JMS队列时你需要配置相应的内核queue“jms.queue.orders.europe“</para>
<programlisting>
&lt;!-- expired messages in JMS Queue "orders.europe"
will be sent to the JMS Queue "expiry.europe" --&gt;
&lt;address-setting match="jms.queue.orders.europe"&gt;
&lt;expiry-address&gt;jms.queue.expiry.europe&lt;/expiry-address&gt;
...
&lt;/address-setting&gt;
</programlisting>
</chapter>

View File

@ -1,234 +0,0 @@
<?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="large-messages">
<title>大消息</title>
<para>ActiveMQ支持超大消息的发送和接收。消息的大小不受客户端或服务器端的内存限制。它只受限于你的磁盘空间的大小。
在我们作过的测试中消息最大可达8GiB而客户端和服务器端的内存只有50MiB</para>
<para>要发送一个大消息,用户需要为大消息提供一个<literal>InputStream</literal>,当大消息被发送时,
ActiveMQ从该<literal>InputStream</literal>读取消息。例如,要将一个磁盘中的大文件以消息形式发送,可以
使用<literal>FileInputStream</literal></para>
<para>数据从<literal>InputStream</literal>读出并分解为一个个数据片段向服务器以流的形式发送。服务器在收到
这些片段后将它们保存到磁盘上。当服务器准备向接收者传递消息时,它将这些片段读回,同样以片段流的形式向接收者
一端发送。当接收者开始接收时,最初收到的只是一个空的消息体。它需要为其设置一个<literal>OutputStream</literal>
以便向大消息保存到磁盘上或其它地方。从发送到接收整个过程中不需要整个消息都在内存中。</para>
<section id="large.message.configuring">
<title>服务器端的配置</title>
<para>大消息在服务器端是直接保存在磁盘目录中。这一目录可以在ActiveMQ的配置文件中定义。</para>
<para>这个参数的名字是<literal>large-messages-directory</literal></para>
<programlisting>&lt;configuration xmlns="urn:activemq"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:activemq /schema/activemq-configuration.xsd">
...
&lt;large-messages-directory>/data/large-messages&lt;/large-messages-directory>
...
&lt;/configuration</programlisting>
<para>默认的大消息保存目录是<literal>data/largemessages</literal></para>
<para>为了提高性能我们建议将大消息的保存目录定义到与消息日志journal或分页转存目录分开的物理卷上。</para>
</section>
<section>
<title>设定限制</title>
<para>参数<literal>min-large-message-size</literal>定义了大消息的最小值。
任何消息的大小如果超过了该值就被视为大消息。一旦成为大消息,它将被分成小的
片段来传送。</para>
<para>默认值是100KiB.</para>
<section id="large-messages.core.config">
<title>使用核心的API</title>
<para>如果使用ActiveMQ的核心<literal>ClientSessionFactory.setMinLargeMessageSize</literal>方法
可以设置大消息的最小值。</para>
<programlisting>ClientSessionFactory factory =
ActiveMQClient.createClientSessionFactory(new
TransportConfiguration(NettyConnectorFactory.class.getName()), null);
factory.setMinLargeMessageSize(25 * 1024);</programlisting>
<para><xref linkend="configuring-transports.client.side"/>对于如何实例化一个会话工厂session factory
给出了进一步的说明。</para>
</section>
<section>
<title>使用JMS</title>
<para>如果连接工厂是通过JNDI方式获得的则需要在<literal>activemq-jms.xml</literal>文件中定义:</para>
<programlisting>...
&lt;connection-factory name="ConnectionFactory">
&lt;connectors>
&lt;connector-ref connector-name="netty"/>
&lt;/connectors>
&lt;entries>
&lt;entry name="ConnectionFactory"/>
&lt;entry name="XAConnectionFactory"/>
&lt;/entries>
&lt;min-large-message-size>250000&lt;/min-large-message-size>
&lt;/connection-factory>
...</programlisting>
<para>如果是直接实例化连接工厂,则使用<literal
>ActiveMQConnectionFactory.setMinLargeMessageSize</literal>方法来定义。</para>
</section>
</section>
<section>
<title>大消息与流stream</title>
<para>在ActiveMQ中可以定义大消息所使用的输入和输出流<literal>java.lang.io</literal>)。</para>
<para>ActiveMQ将使用定义的流来发送输入流和接收输出流大消息。</para>
<para>在使用输出流接收大消息时,有两种选择:你可以用<literal>ClientMessage.saveOutputStream</literal>方法
以阻塞的方式保存大消息;或者你可以使用<literal>ClientMessage.setOutputstream</literal>方法
以异步方法保存大消息。在采用后一种方法时必须保证接收者consumer在大消息的接收过程中保持
有效状态。</para>
<para>根据需要选择所适合的流。最常见的情况是将磁盘文件以消息方式发送也有可能是JDBC的Blob数据
或者是一个<literal>SocketInputStream</literal>,或是来自<literal>HTTPRequests</literal>
的数据等等。只要是实现了<literal>java.io.InputStream</literal>
<literal>java.io.OutputStream</literal>的数据源都可以作为大消息传送。</para>
<section>
<title>核心API中流的使用</title>
<para>下表列出了<literal>ClientMessage</literal>上可以使用的方法。
通过相应的对象属性也可以在JMS中应用。</para>
<table frame="topbot" id="large-messages.ClientMessageAPI">
<title>org.apache.activemq.api.core.client.ClientMessage API</title>
<tgroup cols="3">
<colspec colname="Name" colnum="1"/>
<colspec colname="Descr" colnum="2"/>
<colspec colname="JMS" colnum="3"/>
<thead>
<row>
<entry>名称</entry>
<entry>说明</entry>
<entry>JMS相对应的属性</entry>
</row>
</thead>
<tbody>
<row>
<entry>setBodyInputStream(InputStream)</entry>
<entry>设定大消息发送时所使用的输入流。</entry>
<entry>JMS_HQ_InputStream</entry>
</row>
<row>
<entry>setOutputStream(OutputStream)</entry>
<entry>设定异步接收大消息所使用的输出流。</entry>
<entry>JMS_HQ_OutputStream</entry>
</row>
<row>
<entry>saveOutputStream(OutputStream)</entry>
<entry>设定保存大消息所使用的输出流。这个方法将会阻塞直到大消息全部
保存完毕才返回。</entry>
<entry>JMS_HQ_SaveStream</entry>
</row>
</tbody>
</tgroup>
</table>
<para>下面代码中设定了接收核心消息所用的输出流: </para>
<programlisting>
...
ClientMessage msg = consumer.receive(...);
// This will block here until the stream was transferred
msg.saveOutputStream(someOutputStream);
ClientMessage msg2 = consumer.receive(...);
// This will not wait the transfer to finish
msg.setOutputStream(someOtherOutputStream);
...
</programlisting>
<para> 设定发送核心消息所用的输入流: </para>
<programlisting>
...
ClientMessage msg = session.createMessage();
msg.setInputStream(dataInputStream);
...
</programlisting>
</section>
<section id="large-messages.streaming.over.jms">
<title>在JMS中使用流</title>
<para>使用JMS时ActiveMQ根据定义的属性值调用对应的核心接口参见 <xref
linkend="large-messages.ClientMessageAPI"/>)来使用流。你只需要用
<literal>Message.setObjectProperty</literal>方法设置适当的输入/输出流即可。</para>
<para>输入流<literal>InputStream</literal>可以通过JMS属性JMS_HQ_InputStream来定义</para>
<programlisting>
BytesMessage message = session.createBytesMessage();
FileInputStream fileInputStream = new FileInputStream(fileInput);
BufferedInputStream bufferedInput = new BufferedInputStream(fileInputStream);
message.setObjectProperty("JMS_HQ_InputStream", bufferedInput);
someProducer.send(message);</programlisting>
<para>输出流<literal>OutputStream</literal>可以通过JMS属性JMS_HQ_SaveStream来定义。下面是阻塞式方法</para>
<programlisting>
BytesMessage messageReceived = (BytesMessage)messageConsumer.receive(120000);
File outputFile = new File("huge_message_received.dat");
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
BufferedOutputStream bufferedOutput = new BufferedOutputStream(fileOutputStream);
// This will block until the entire content is saved on disk
messageReceived.setObjectProperty("JMS_HQ_SaveStream", bufferedOutput);
</programlisting>
<para>也可以使用JMS_HQ_OutputStream属性以非阻塞式异步方法来定义输出流<literal>OutputStream</literal></para>
<programlisting>
// This won't wait the stream to finish. You need to keep the consumer active.
messageReceived.setObjectProperty("JMS_HQ_OutputStream", bufferedOutput);
</programlisting>
<note>
<para>使用JMS时只有<literal>StreamMessage</literal><literal>BytesMessage</literal>才支持大消息的传送。</para>
</note>
</section>
</section>
<section>
<title>不使用流的方式</title>
<para>如果不想使用输入流与输出流来传送大消息,可以用另外一种方法。</para>
<para>使用核心接口时,可以直接从消息中读字节。</para>
<programlisting>ClientMessage msg = consumer.receive();
byte[] bytes = new byte[1024];
for (int i = 0 ; i &lt; msg.getBodySize(); i += bytes.length)
{
msg.getBody().readBytes(bytes);
// Whatever you want to do with the bytes
}</programlisting>
<para>使用JMS接口时<literal>BytesMessage</literal><literal>StreamMessage</literal>
本身提供这样的支持。</para>
<programlisting>BytesMessage rm = (BytesMessage)cons.receive(10000);
byte data[] = new byte[1024];
for (int i = 0; i &lt; rm.getBodyLength(); i += 1024)
{
int numberOfBytes = rm.readBytes(data);
// Do whatever you want with the data
} </programlisting>
</section>
<section id="large-messages.cache.client">
<title>在客户端缓存大消息</title>
<para>大消息通过流在服务器和客户端之间传输。每个大消息被分割成很多小的数据包传递。因此大消息只能被
读取一次。这样一个大消息在收到后就不能再被再次传送。例如JMS Bridge在发送大消息时如果在出现故障
将不能把它重新发送。</para>
<para>要解决这个问题,可以在连接工厂上设置<literal>cache-large-message-client</literal>属性。
这个属性可以使客户端接收者创建一个临时的文件保存收到的大消息,这样就可以在需要时能够重新发送该消息。</para>
<note>如果JMS Bridge用来发送大消息可以在它使用的连接工厂上使用它。</note>
</section>
<section id="large-messages.example">
<title>大消息例子</title>
<para>我们在<xref linkend="examples.large-message"/>提供了一个在JMS中配置和使用大消息的例子。</para>
</section>
</chapter>

View File

@ -1,66 +0,0 @@
<?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="last-value-queues">
<title>最新值队列Last-Value Queues</title>
<para>最新值队列是一种特殊的队列。当一个新消息到达一个最新值队列时它会将所有与该消息定义的Last-Value相同的旧消息
抛弃。换句话说,只有最新的消息被保留下来。</para>
<para>一个典型的用例是股价信息,通常你只关心一支股票的最新价格。</para>
<section>
<title>最新值队列的配置</title>
<para>最新值队列的配置在address-setting内</para>
<programlisting>
&lt;address-setting match="jms.queue.lastValueQueue"&gt;
&lt;last-value-queue&gt;true&lt;/last-value-queue&gt;
&lt;/address-setting&gt;
</programlisting>
<para>默认的<literal>last-value-queue</literal>值是false。可以使用通配符来匹配地址。
(参见 <xref linkend="wildcard-syntax"/>)。</para>
</section>
<section>
<title>使用Last-Value参数</title>
<para>用来标识最新值的参数名是<literal>"_HQ_LVQ_NAME"</literal>
相当于核心API中定义的常量<literal>Message.HDR_LAST_VALUE_NAME</literal>)。</para>
<para>如果两个消息具有相同的Last-Value值那么较新的消息就会保留另外一个被丢弃</para>
<programlisting>
// send 1st message with Last-Value property set to STOCK_NAME
TextMessage message =
session.createTextMessage("1st message with Last-Value property set");
message.setStringProperty("_HQ_LVQ_NAME", "STOCK_NAME");
producer.send(message);
// send 2nd message with Last-Value property set to STOCK_NAME
message =
session.createTextMessage("2nd message with Last-Value property set");
message.setStringProperty("_HQ_LVQ_NAME", "STOCK_NAME");
producer.send(message);
...
// only the 2nd message will be received: it is the latest with
// the Last-Value property set
TextMessage messageReceived = (TextMessage)messageConsumer.receive(5000);
System.out.format("Received message: %s\n", messageReceived.getText());
</programlisting>
</section>
<section>
<title>例子</title>
<para>参见<xref linkend="examples.last-value-queue"/>。它展示的是在JMS应用中来配置和使用
最新值队列。</para>
</section>
</chapter>

View File

@ -1,114 +0,0 @@
<?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="libaio">
<title>Libaio平台专有库</title>
<para>ActiveMQ发布包中包括一个平台专有的库它可以使ActiveMQ使用Linux操作系统的libaio。</para>
<para><literal>libaio</literal>是Linux项目的一个库。它将用户提交的写操作用异步的方式执行。通过
回调用户的代码来通知写操作的完成。</para>
<para>通过配置ActiveMQ可以使用这个库来访问高性能的日志具体请参见 <xref
linkend="persistence"/>。</para>
<para>下面列出了ActiveMQ所带的平台专有库文件</para>
<itemizedlist>
<listitem>
<para>libActiveMQAIO32.so - x86 32 位平台</para>
</listitem>
<listitem>
<para>libActiveMQAIO64.so - x86 64 位平台</para>
</listitem>
</itemizedlist>
<para>当使用libaio时ActiveMQ会在<link linkend="using-server.library.path">库路径</link>中寻找并装
载这些文件。</para>
<section>
<title>库文件的编译</title>
<para>如果你的Linux平台不是x86_32或x86_64比如Itanium 64或IBM Power你需要自己编译相应的库文件
因为ActiveMQ不提供这些平台的库文件。</para>
<section>
<title>安装要求</title>
<note>
<para>目前libaio只在Linux上有。所以它不可能在其它操作系统上编译。</para>
</note>
<para>编译需要<ulink url="http://en.wikipedia.org/wiki/Autoconf"
>autoconf</ulink>工具,它用来简化编译过程。除此之外还需要一些安装包:</para>
<itemizedlist>
<listitem>
<para>gcc - C 编译器</para>
</listitem>
<listitem>
<para>gcc-c++ or g++ - gcc的c++编译工具扩展</para>
</listitem>
<listitem>
<para>autoconf - 自动编译工具</para>
</listitem>
<listitem>
<para>make - make 工具</para>
</listitem>
<listitem>
<para>automake - make文件自动生成工具</para>
</listitem>
<listitem>
<para>libtool - 库连接工具</para>
</listitem>
<listitem>
<para>libaio - 磁盘异步IO库</para>
</listitem>
<listitem>
<para>libaio-dev - libaio的编译支持</para>
</listitem>
<listitem>
<para>完整的JDKJAVA_HOME要指向正确的位置</para>
</listitem>
</itemizedlist>
<para>如果在RHEL或Fedora上进行安装输入以下命令</para>
<programlisting>sudo yum install automake libtool autoconf gcc-g++ gcc libaio libaio-dev make</programlisting>
<para>如果是 debian系统</para>
<programlisting>sudo apt-get install automake libtool autoconf gcc-g++ gcc libaio libaio-dev make</programlisting>
<note>
<para>在有些Linux的版本中上述的安装包名可能有一些差别。例如Fedora中的gcc-c++在Debian系统中
的名称为g++</para>
</note>
</section>
<section>
<title>开始编译</title>
<para>在ActiveMQ发布包的<literal>native-src</literal>目录下执行shell脚本
<literal>bootstrap</literal>。这个脚本会调用 <literal
>automake</literal>以及<literal>make</literal>来创建所有的make文件和专有库。</para>
<programlisting>someUser@someBox:/messaging-distribution/native-src$ ./bootstrap
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
...
configure: creating ./config.status
config.status: creating Makefile
config.status: creating ./src/Makefile
config.status: creating config.h
config.status: config.h is unchanged
config.status: executing depfiles commands
config.status: executing libtool commands
...</programlisting>
<para>编译好的库文件在<literal
>./native-src/src/.libs/libActiveMQAIO.so</literal>。将该文件移到发布包的
<literal>bin</literal>目录下,或者你的<link linkend="using-server.library.path">库目录</link>
所指向的目录即可。</para>
<para>如果你修改了ActiveMQ的libaio代码只需要在<literal>native-src</literal>目录下直挂运行make即可完成编译。</para>
</section>
</section>
</chapter>

View File

@ -1,46 +0,0 @@
<?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="logging">
<title>日志Logging</title>
<para>ActiveMQ有自己的独立的日志系统不依赖于任何其它的日志框架。在默认情况下所有ActiveMQ的日志将输入到
标准的<ulink
url="http://java.sun.com/j2se/1.4.2/docs/guide/util/logging/">JDK日志系统</ulink>
即JULJava Util Logging。服务器在默认条件下读取config目录下的
<literal>logging.properties</literal>文件做为JUL的配置文件。它配置了使用ActiveMQ自己的格式化
方法将日志输出到屏幕终端Console及文件中。请访问Sun公司的相关网址来进一步了解如何配置使用JUL。</para>
<para>你可以通过编程或定义系统变量的方法来配置不同的日志代理Logging Delegate</para>
<para>采用编程方法,只需要调用方法:
<programlisting>org.apache.activemq.core.logging.Logger.setDelegateFactory(new Log4jLogDelegateFactory())</programlisting></para>
<para>其中<literal>Log4jLogDelegateFactory</literal>实现了<literal
>org.apache.activemq.spi.core.logging.LogDelegateFactory </literal>接口。</para>
<para>如果要使用系统变量方法,则需要设置变量<literal
>org.apache.activemq.logger-delegate-factory-class-name</literal>为相应的代理工厂,即
<programlisting>-Dorg.apache.activemq.logger-delegate-factory-class-name=org.apache.activemq.integration.logging.Log4jLogDelegateFactory</programlisting></para>
<para>上面的例子可以看出ActiveMQ提供了一些代理工厂以方便用户使用它们是<orderedlist
><listitem><para>org.apache.activemq.core.logging.impl.JULLogDelegateFactory - 默认的JUL日志代理工厂。</para>
</listitem><listitem><para>org.apache.activemq.integration.logging.Log4jLogDelegateFactory
- Log4J的日志代理工厂。</para></listitem></orderedlist></para>
<para>如果在客户端使用JUL代理注意要提供<literal>logging.properties</literal>文件,并且在客户端启动之前设置<literal
>java.util.logging.config.file</literal>属性。</para>
<section>
<title>与JBoss应用服务器日志的关系</title>
<para>当ActiveMQ部署到JBoss应用服务器版本5.x或以上时虽然ActiveMQ仍然使用JUL但是所有的日志输出被重定向到
JBoss logger。请参阅相关的JBoss文档来了解更多的信息。如果是以前版本的JBoss则必需指定你所需要的日志代理。</para>
</section>
</chapter>

View File

@ -1,805 +0,0 @@
<?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="management">
<title>管理</title>
<para>ActiveMQ拥有套丰富的管理接口。用户使用这些接口可以修改服务器配置、创建新的资源如队列和
话题)、检查这些资源(如队列中有多少消息)并进行管理(从队列中删除消息)。这样用户可以
<emphasis>管理</emphasis>ActiveMQ。另外客户还可以订阅管理通知。</para>
<para>有三种方式管理ActiveMQ</para>
<itemizedlist>
<listitem>
<para>使用JMX -- JMX是标准的Java应用程序管理方式。</para>
</listitem>
<listitem>
<para>使用核心接口 -- 管理操作通过<emphasis>核心消息</emphasis>的方法发向ActiveMQ服
务。</para>
</listitem>
<listitem>
<para>使用JMS接口 -- 管理操作通过<emphasis>JMS消息</emphasis>的方式发向ActiveMQ服务器。</para>
</listitem>
</itemizedlist>
<para>虽然有三种方式但它们提供相同的功能。使用JMX方法能完成的功能使用核心接口或JMS接口都可以完成。</para>
<para>根据不同的应用环境来选择最适当的方式。</para>
<section>
<title>管理接口API</title>
<para>不管使用哪种方式,管理接口都是一样的。</para>
<para>对于每个<emphasis>被管理的资源</emphasis>都有一个Java的接口提供可使用的操作。</para>
<para>ActiveMQ的管理接口分布在2个包中</para>
<itemizedlist>
<listitem>
<para><emphasis>核心</emphasis>资源的管理接口在 <literal
>org.apache.activemq.api.core.management</literal>包中。</para>
</listitem>
<listitem>
<para><emphasis>JMS</emphasis>资源的管理接口在 <literal
>org.apache.activemq.api.jms.management</literal>包中。</para>
</listitem>
</itemizedlist>
<para>调用<emphasis>管理操作</emphasis>的方法由所使用是方式是JMX、核心消息还是JMS
消息来决定。</para>
<note>
<para>一小部分的管理接口需要一个<literal>过滤器</literal>参数来选择需要的消息。
如果要求<emphasis>所有的消息</emphasis>,传递该参数时使用<literal>
null</literal>或者一个空的字符串即可。</para>
</note>
<section>
<title>核心管理接口</title>
<para>ActiveMQ定义了一套对核心资源的管理接口。关于它们的详细说明请参见相应的javadoc。
下面是对它们的概述:</para>
<section>
<title>核心服务器管理</title>
<itemizedlist>
<listitem>
<para>队列的列表、创建、部署与删除</para>
<para><literal>getQueueNames()</literal> method方法用来列出所有已经部署的队列。</para>
<para><literal>ActiveMQServerControl</literal> ObjectName <literal
>org.apache.activemq:module=Core,type=Server</literal>或资源名<literal
>core.server</literal>)上有队列创建或删除的方法,它们是
<literal>createQueue()</literal><literal>deployQueue()</literal>
<literal>destroyQueue()</literal></para>
<para>如果队列已经存在,那么<literal>createQueue</literal>方法调用会出错,而
<literal>deployQueue</literal>方法调用没有任何作用。</para>
</listitem>
<listitem>
<para>暂停与恢复队列</para>
<para><literal>QueueControl</literal>可用来暂停与恢复队列。如果一个队列被暂停,它
虽然可以继续接收消息但是不传递消息;当被恢复时,队列又会开始传递消息。
</para>
</listitem>
<listitem>
<para>远程连接的列表与关闭</para>
<para><literal>listRemoteAddresses()</literal>方法可以用来列出客户端的远程地址。
还可以使用<literal>closeConnectionsForAddress()</literal>方法来关闭
与该地址相关的远程连接。</para>
<para>另外,使用<literal>listConnectionIDs()</literal>方法可以列出连接ID
使用<literal>listSessions()</literal>方法可以列出与一个连接ID相关的所有
会话session</para>
</listitem>
<listitem>
<para>事务的手动操作heuristic operations</para>
<para>当服务器由于故障而重新启动时,可能造成一些事务没有完成而需要人工干预。
<literal>listPreparedTransactions()</literal>方法可以列出所有处于
准备prepared状态的事务事务是用Base64字符串的形式列出。如果要提交或回滚
可以使用<literal>commitPreparedTransaction()</literal>方法或
<literal>rollbackPreparedTransaction()</literal>方法。采用启发式
heuristic完成的事务可以用<literal>listHeuristicCommittedTransactions()</literal>
方法和<literal>listHeuristicRolledBackTransactions</literal>方法列出。</para>
</listitem>
<listitem>
<para>打开和重置消息计数器</para>
<para>消息计数器可以用<literal>enableMessageCounters()</literal>方法打开,用
<literal>disableMessageCounters()</literal>方法关闭。如果要重置消息计数器,
可以使用<literal>resetAllMessageCounters()</literal>方法和
<literal>resetAllMessageCounterHistories()</literal>方法。</para>
</listitem>
<listitem>
<para>获得服务器的配置和属性</para>
<para><literal>ActiveMQServerControl</literal>提供了访问ActiveMQ服务器所有属性
的方法(例如<literal>getVersion()</literal>方法可以得到服务器的版本,等等)。 </para>
</listitem>
<listitem>
<para>核心桥和转发器的创建,删除与列表</para>
<para>使用<literal>getBridgeNames()</literal>可以列出部署的核心桥。
使用<literal>getDivertNames()</literal>可以列出部署的转发器。</para>
<para>使用<literal>ActiveMQServerControl</literal> (ObjectName <literal
>org.apache.activemq:module=Core,type=Server</literal> 或资源名 <literal
>core.server</literal>)的方法<literal>createBridge()</literal>
             <literal>destroyBridge()</literal>可以创建和删除核心桥。
通过<literal>createDivert()</literal><literal>destroyDivert()</literal>
可以创建和删除转发器。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>核心地址的管理</title>
<para>核心地址可以通过<literal>AddressControl</literal>类进行访问ObjectName
<literal>org.apache.activemq:module=Core,type=Address,name="&lt;the
address name&gt;"</literal>或者资源名 <literal>core.address.&lt;the
address name&gt;</literal>)。</para>
<itemizedlist>
<listitem>
<para>修改地址的角色和权限。</para>
<para>你可以使用<literal>addRole()</literal>方法或<literal>removeRole()</literal>
方法添加或删除地址的角色。用<literal>getRoles()</literal>方法可以列出一个地址的所有角色。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>核心队列的管理</title>
<para>管理接口中的一大部分是管理核心队列的。<literal>QueueControl</literal>类定义了核心队列的管理
接口ObjectName <literal>org.apache.activemq:module=Core,type=Queue,address="&lt;绑定地址
address&gt;",name="&lt;队列名&gt;"</literal> 或资源名 <literal
>core.queue.&lt;队列名&gt;</literal>)。</para>
<para>绝大部分的队列管理方法需要一个消息ID参数如删除一个消息或一个过滤器参数如将具有某个
属性值的所有消息设置为过期)。</para>
<itemizedlist>
<listitem>
<para>消息的过期,发向死信地址及删除</para>
<para><literal>expireMessages()</literal>方法可以使消息过期。如果设置了一个过期地址,
这些消息会被发到过期地址。否则这些消息会被丢弃。<literal>setExpiryAddress()</literal>
方法可以用来设置队列的过期地址。</para>
<para>消息可以用<literal>sendMessagesToDeadLetterAddress()</literal>方法发送到
一个死信地址。它返回发到这个死信地址的消息的数量。如果没有设置死信地址,那么消息就会从队列中
删除。用<literal>setDeadLetterAddress()</literal>方法可以设置队列的死信地址。</para>
<para>消息还可以从一个队列转移到另一个队列。其方法是
<literal>moveMessages()</literal></para>
</listitem>
<listitem>
<para>消息的列表与删除</para>
<para><literal>listMessages()</literal>方法可以列出一个队列中的所有消息。这个方法
返回的是一个<literal>Map</literal>的数组。每一个Map对应一个消息。</para>
<para>消息可以用<literal>removeMessages()</literal>方法删除。如果是使用消息ID
返回的是一个布尔常量;如果是用过滤器,则返回的
是删除的消息数量。在使用过滤器来删除过滤的消息时,如果传入一个空字符串则表示要删除
所有的消息。</para>
</listitem>
<listitem>
<para>消息计数</para>
<para>一个队列中的消息数可以用<literal>getMessageCount()</literal>方法获得。
此外,<literal>countMessages()</literal>方法可以返回队列中与一
<emphasis>过滤器</emphasis>匹配的消息数量。</para>
</listitem>
<listitem>
<para>修改消息的优先级</para>
<para><literal>changeMessagesPriority()</literal>方法可以改变消息的优先级。
该方法如果带一个消息ID参数返回一个布尔常量如果带一个过滤器参数返回优先级
被更新的消息的数量。</para>
</listitem>
<listitem>
<para>消息计数器</para>
<para><literal>listMessageCounter()</literal>方法和<literal>
listMessageCounterHistory()</literal>方法可以列出一个队列的消息计数器。
(参见 <xref linkend="management.message-counters"/>)。消息计数器还可以
<literal>resetMessageCounter()</literal>方法重置。</para>
</listitem>
<listitem>
<para>获得队列的属性</para>
<para>通过<literal>QueueControl</literal>可以获得核心队列的属性(例如用
<literal>getFilter()</literal>方法可以得到队列的
过滤器,<literal>isDurable()</literal>方法可以知道队列是否是持久的队列等等)。</para>
</listitem>
<listitem>
<para>暂停和恢复队列</para>
<para><literal>QueueControl</literal>可用来暂停与恢复队列。如果一个队列被暂停,它
虽然可以继续接收消息但是不传递消息;当被恢复时,队列又会开始传递消息。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>其它核心资源的管理</title>
<para>ActiveMQ允许用户启动或停止其远程资源接收器转发器等等。这样可以使服务器暂停工作
而不需要完全停止服务器(比如可以临时对服务器进行一些离线操作,像对一些事务的处理)。这些资源有:</para>
<itemizedlist>
<listitem>
<para>接收器</para>
<para><literal>AcceptorControl</literal>ObjectName <literal
>org.apache.activemq:module=Core,type=Acceptor,name="&lt;接收器名
&gt;"</literal> 或资源名 <literal>core.acceptor.&lt;地址名
&gt;</literal>)的<literal>start()</literal>方法启动,用<literal>
stop()</literal>方法停止。接收器的参数可以通过<literal>AcceptorControl</literal>
的属性获得。(参见 <xref
linkend="configuring-transports.acceptors"/>)。</para>
</listitem>
<listitem>
<para>转发器</para>
<para><literal>DivertControl</literal>ObjectName是 <literal
>org.apache.activemq:module=Core,type=Divert,name=&lt;转发器名&gt;</literal>
或资源名<literal>core.divert.&lt;转发器&gt;</literal>)类的
<literal>start()</literal>方法可以启动,用<literal>stop()</literal>方法可以停止。
通过<literal>DivertControl</literal>还可以获得转发器的各种属性。(参见 <xref
linkend="diverts"/>)。</para>
</listitem>
<listitem>
<para></para>
<para>桥可以通过<literal>BridgeControl</literal>ObjectName <literal
>org.apache.activemq:module=Core,type=Bridge,name="&lt;桥的名字
&gt;"</literal> 或资源名 <literal>core.bridge.&lt;桥的名字
&gt;</literal>)的<literal>start()</literal>
方法启动,用<literal>stop()</literal>方法停止。它的属性可以通过
<literal>BridgeControl</literal>的属性获得(参见
<xref linkend="core-bridges"/>)。</para>
</listitem>
<listitem>
<para>广播组</para>
<para>广播组可以通过<literal>BroadcastGroupControl</literal>ObjectName <literal
>org.apache.activemq:module=Core,type=BroadcastGroup,name="&lt;广播组名
&gt;"</literal> 或者资源名 <literal
>core.broadcastgroup.&lt;广播组名&gt;</literal>)的<literal>
start()</literal>方法启动,用<literal>stop()</literal>方法停止。
它的属性也可以通过<literal
>BroadcastGroupControl</literal>的属性获得(参见<xref
linkend="clusters.broadcast-groups"/>)。</para>
</listitem>
<listitem>
<para>发现组</para>
<para>发现组可以通过<literal>DiscoveryGroupControl</literal>
ObjectName <literal>org.apache.activemq:module=Core,type=DiscoveryGroup,
name="&lt;发现组名&gt;"</literal> 或资源名<literal>core.discovery.&lt;
发现组名&gt;</literal>)的
<literal>start()</literal>方法启动,用<literal>stop()</literal>方法停止。
它的参数可以通过<literal>DiscoveryGroupControl</literal>的属性获得(参见
<xref linkend="clusters.discovery-groups"/>)。</para>
</listitem>
<listitem>
<para>集群连接</para>
<para>集群连接可以通过<literal>ClusterConnectionControl</literal>类(
ObjectName <literal
>org.apache.activemq:module=Core,type=ClusterConnection,name="&lt;集群连接名
&gt;"</literal> 或资源名 <literal
>core.clusterconnection.&lt;集群连接名&gt;</literal>)的<literal>
start()</literal>方法启动,用<literal>stop()</literal>方法停止。
它的参数可以通过<literal
>ClusterConnectionControl</literal>的属性来获得(参见
<xref linkend="clusters.cluster-connections"/>)。</para>
</listitem>
</itemizedlist>
</section>
</section>
<section>
<title>JMS管理接口</title>
<para>ActiveMQ定义了一套JMS管理接口来管理JMS的<emphasis>可管理的对象</emphasis>
例如JMS队列话题及连接工厂</para>
<section>
<title>JMS服务器管理</title>
<para><literal>JMSServerControl</literal>ObjectName <literal
>org.apache.activemq:module=JMS,type=Server</literal> 或资源名<literal
>jms.server</literal>用来创建JMS资源连接工厂和目标</para>
<itemizedlist>
<listitem>
<para>列表、创建、删除连接工厂</para>
<para>使用<literal>getConnectionFactoryNames()</literal> 方法可以列出部署的连接工厂的
名字。</para>
<para><literal>createConnectionFactory()</literal>方法和<literal
>destroyConnectionFactory()</literal>方法能创建和删除JMS连接工厂。
这些连接工厂都与JNDI绑定以便于客户端来查找。如果是在图形介面下创建连接工厂在广本框内输入
有关的传输参数时可使用一组用逗号隔开的键-值对(例如<literal>key1=10, key2="value", key3=false</literal>)。
如果需要定义多个传输,你需要将每个传输的参数对用大括号括起来,例如<literal>{key=10}, {key=20}</literal>
第一个<literal>key</literal>属于第一个传输配置,第二个<literal>key</literal>属于第二个传输配置。
(有关传输的各种参数参见<xref linkend="configuring-transports"/>)。</para>
</listitem>
<listitem>
<para>列表、创建与删除队列</para>
<para><literal>getQueueNames()</literal>方法可以获得部署的JMS队列的名字列表。</para>
<para>JMS队列可以用<literal>createQueue()</literal>方法创建,用<literal>destroyQueue()</literal>方法删除。
创建的队列都绑定到JNDI以便JMS客户端可以查找。</para>
</listitem>
<listitem>
<para>列表、创建与删除话题topic</para>
<para><literal>getTopicNames()</literal>方法可以获得部署的JMS话题名字。</para>
<para>JMS话题可以用<literal>createTopic()</literal>方法来创建,用<literal>destroyTopic()</literal>方法来删除。
创建的话题都绑定到JNDI以便客户端查找。</para>
</listitem>
<listitem>
<para>远程连接的列表与关闭</para>
<para><literal>listRemoteAddresses()</literal>方法可以获得JMS客户端的远程地址。
还可以用<literal>closeConnectionsForAddress()</literal>方法关闭与某个远程地址相关联的连接。</para>
<para>另外,<literal>listConnectionIDs()</literal>方法可以列出连接的ID
<literal>listSessions()</literal>方法可以列出一个给定的连接ID的所有会话session</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>JMS连接工厂的管理</title>
<para>使用类ObjectName <literal>org.apache.activemq:module=JMS,type=ConnectionFactory,
name="&lt;连接工厂名&gt;"</literal>或者资源名<literal>jms.connectionfactory.&lt;
连接工厂名&gt;</literal>可以管理JMS的连接工厂。</para>
<itemizedlist>
<listitem>
<para>获得连接工厂的属性</para>
<para><literal>ConnectionFactoryControl</literal>类可以用来获得连接工厂的属性(
例如<literal>getConsumerWindowSize()</literal>方法可以获得接收者流控制的窗口大小,
<literal>isBlockOnNonDurableSend()</literal>方法可以知道从这个连接工厂创建的发送
者是否采用阻塞方式发送非持久的消息,等等)。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>JMS队列管理</title>
<para>使用<literal>JMSQueueControl</literal>ObjectName <literal>org.apache.activemq:module=JMS,
type=Queue,name="&lt;队列名&gt;"</literal>或资源名 <literal>jms.queue.&lt;队列名
&gt;</literal>可以管理JMS队列。</para>
<para><emphasis>JMS队列的管理操作与核心队列的管理十分相似。</emphasis></para>
<itemizedlist>
<listitem>
<para>过期,发送到死信地址和移动消息</para>
<para>可以使用<literal>expireMessages()</literal>方法将队列中的消息设成过期消息。
如果配置有过期地址,消息就会被发到过期地址。过期地址可以用
<literal>setExpiryAddress()</literal>方法来设定。</para>
<para>使用<literal>sendMessagesToDeadLetterAddress()</literal>方法可以将消息发送到死信地址。
它返回发送到死信地址消息的数量。如果没有设定死信地址,那么消息会被丢弃。使用
<literal>setDeadLetterAddress()</literal>方法可以设定队列的死信地址。</para>
<para><literal>moveMessages()</literal>方法将消息从一个队列移动到另一个队列。</para>
</listitem>
<listitem>
<para>列表与删除消息</para>
<para>使用<literal>listMessages()</literal>方法可以列出一个队列中的所有消息。它返回的是一个
Map的数组。每一个Map对应一个消息。</para>
<para>使用<literal>removeMessages()</literal>方法可以从队列中删除消息。如果带的参数是消息ID
返回的是一个布尔常是;如果带的参数是一个过滤器,则返回删除的消息数。带有过滤器参数的<literal
>removeMessages()</literal>方法只删除过滤器选择的消息。如果些参数是一个空字符串,那么将
删除所有的消息。</para>
</listitem>
<listitem>
<para>消息计数</para>
<para>使用<literal>getMessageCount()</literal>方法可以得到队列中的消息数。另外,方法
<literal>countMessages()</literal>可以得到队列中所有与<emphasis>过滤器</emphasis>相匹配的消息数。</para>
</listitem>
<listitem>
<para>修改消息的优先级</para>
<para>消息的优先级可以用<literal>changeMessagesPriority()</literal>方法修改。如果是带一个消
息ID参数它返回的是一个布尔常量如果是带一个过滤器参数则它返回的是优先级更新了的消息数。</para>
</listitem>
<listitem>
<para>消息计数器</para>
<para><literal>listMessageCounter()</literal>方法和<literal >listMessageCounterHistory()</literal>
方法可以用来列出队列中的所有消息计数器。(参见 <xref
linkend="management.message-counters"/>)。</para>
</listitem>
<listitem>
<para>获取队列的属性</para>
<para><literal>JMSQueueControl</literal>类可以用来获取JMS队列的设置参数例如方法<literal>isTemporary()</literal>
可以判断队列是否为临时的,方法<literal>isDurable()</literal>可以判断队列是否为持久的等等)。</para>
</listitem>
<listitem>
<para>队列的暂停与恢复</para>
<para><literal>JMSQueueControl</literal>可以暂停一个队列或恢复一个队列。
如果一个队列被暂停,它虽然可以继续接收消息但是不传递消息;
当被恢复时,队列又会开始传递消息。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>JMS话题Topic的管理</title>
<para>JMS话题的管理是通过<literal>TopicControl</literal>类(
the ObjectName <literal>org.apache.activemq:module=JMS,type=Topic,name="&lt;话题名&gt;"</literal>
或资源名 <literal>jms.topic.&lt;话题名&gt;</literal>)。</para>
<itemizedlist>
<listitem>
<para>订阅和消息的列表</para>
<para><literal>listAllSubscriptions()</literal><literal
>listDurableSubscriptions()</literal><literal
>listNonDurableSubscriptions()</literal>方法可以列出话题的不同订阅。
这些方法都返回<literal>Object</literal>数组,表示订阅的细节(如订阅名,
客户ID持久性消息计数等。用<literal
>listMessagesForSubscription()</literal>方法可以列出一个订阅上的JMS消息。</para>
</listitem>
<listitem>
<para>删除订阅</para>
<para>持久性订阅可以使用<literal>dropDurableSubscription()</literal>方法来删除。</para>
</listitem>
<listitem>
<para>订阅消息计数</para>
<para><literal>countMessagesForSubscription()</literal>方法可以得到一个订阅上面所持有
的消息数(还可带一个消息选择器来得出有多少消息与之匹配)。</para>
</listitem>
</itemizedlist>
</section>
</section>
</section>
<section id="management.jmx">
<title>使用JMX</title>
<para>ActiveMQ提供了<ulink
url="http://java.sun.com/javase/technologies/core/mntr-mgmt/javamanagement/"
>JMX</ulink></para>
<para>ActiveMQ通过MBean的接口暴露其JMX管理操作。它将自己的资源注册到<literal>org.apache.activemq</literal>域。</para>
<para>比如,用来管理一个名为<literal>exampleQueue</literal>JMS队列的<literal>ObjectName</literal>是:</para>
<programlisting>
org.apache.activemq:module=JMS,type=Queue,name="exampleQueue"
</programlisting>
<para>MBean为</para>
<programlisting>
org.apache.activemq.api.jms.management.JMSQueueControl
</programlisting>
<para>MBean的<literal>ObjectName</literal><literal
>org.apache.activemq.api.core.management.ObjectNameBuilder</literal>来产生出来的。你也可以使用<literal
>jconsole</literal>来查找你想要的MBean的<literal>ObjectName</literal></para>
<para>使用JMX来管理ActiveMQ与用JMX管理其它Java应用程序没有什么不同。你可以使用反射或者创建MBean代理的方法。</para>
<section id="management.jmx.configuration">
<title>配置JMX</title>
<para>默认情况下ActiveMQ的JMX是打开的。将<literal
>activemq-configuration.xml</literal>文件中的<literal
>jmx-management-enabled</literal>设置为<literal>false</literal>就可以关闭JMX</para>
<programlisting>
&lt;!-- false to disable JMX management for ActiveMQ --&gt;
&lt;jmx-management-enabled&gt;false&lt;/jmx-management-enabled&gt;
</programlisting>
<para>如果JMX功能是打开的则使用<literal>jconsole</literal>可以管理本地的ActiveMQ。</para>
<note>
<para>出于安全考虑默认情况下JMX远程连接是关闭的。参见<ulink url="http://java.sun.com/j2se/1.5.0/docs/guide/management/agent.html#remote"
>Java管理指南</ulink>来配置服务器的远程管理(系统变量必须在<literal>run.sh</literal><literal>run.bat</literal>中定义)。</para>
</note>
<para>ActiveMQ默认使用JMX域名"org.apache.activemq"。如果要用一个MBeanServer管理多个ActiveMQ服务器可以将每个ActiveMQ
服务器配置成不同的JMX域。方法就是在<literal>activemq-configuration.xml</literal>文件中设置<literal>jmx-domain</literal></para>
<programlisting>
&lt;!-- use a specific JMX domain for ActiveMQ MBeans -->
&lt;jmx-domain>my.org.apache.activemq&lt;/jmx-domain>
</programlisting>
<section>
<title>MBeanServer的配置</title>
<para>ActiveMQ在独立运行时使用Java虚拟机的<literal
>Platform MBeanServer</literal>来注册其MBean。这在JBoss Microcontainer微容器的bean
文件中进行配置(参见<xref linkend="server.microcontainer.configuration"/></para>
<programlisting>&lt;!-- MBeanServer --&gt;
&lt;bean name="MBeanServer" class="javax.management.MBeanServer"&gt;
&lt;constructor factoryClass="java.lang.management.ManagementFactory"
factoryMethod="getPlatformMBeanServer" /&gt;
&lt;/bean&gt;
</programlisting>
<para>当与AS 5集成运行时它使用应用服务器自己的MBean服务这样就可以使用它的jmx-console</para>
<programlisting>&lt;!-- MBeanServer --&gt;
&lt;bean name="MBeanServer" class="javax.management.MBeanServer"&gt;
&lt;constructor factoryClass="org.jboss.mx.util.MBeanServerLocator"
factoryMethod="locateJBoss" /&gt;
&lt;/bean&gt;
</programlisting>
</section>
</section>
<section>
<title>例子</title>
<para>参见<xref linkend="examples.jmx"/>这个例子展示了如何使用远程JMX连接或MBean代理来管理ActiveMQ。</para>
</section>
</section>
<section>
<title>使用核心接口</title>
<para>核心管理接口的调用实际上是向一个特殊的地址发送核心消息。这个特殊地址称为<emphasis>管理地址</emphasis></para>
<para><emphasis>管理消息</emphasis>是一些定义了一些固定属性的普通核心消息。服务器通过这些属性来解释管理操作:</para>
<itemizedlist>
<listitem>
<para>管理资源的名称</para>
</listitem>
<listitem>
<para>管理操作的名称</para>
</listitem>
<listitem>
<para>管理操作的参数</para>
</listitem>
</itemizedlist>
<para>当一个管理消息发送到管理地址时ActiveMQ服务器将从中提取出相应的信息再调用相应的管理资源的方法之后向
该管理消息的回答地址reply-to address<literal>ClientMessageImpl.REPLYTO_HEADER_NAME
</literal>定义)发送一个<emphasis>管理回答</emphasis></para>
<para>一个<literal>ClientConsumer</literal>用来接收管理回答并提取出其中的操作的結果(如果有的话)。
考虑到可移植性返回的結果采用的是格式的字符串而没有采用Java的序列化技术
<literal>org.apache.activemq.api.core.management.ManagementHelper</literal>可以用来将JSON字符串
转换成Java对象</para>
<para>使用以下步骤可以简化使用核心消息调用管理操作:</para>
<orderedlist>
<listitem>
<para>创建一个<literal>ClientRequestor</literal>对象,用来发送管理消息并接收回答。</para>
</listitem>
<listitem>
<para>创建一个<literal>ClientMessage</literal></para>
</listitem>
<listitem>
<para>使用<literal
>org.apache.activemq.api.core.management.ManagementHelper</literal>类来帮助设置消息的管理参数。</para>
</listitem>
<listitem>
<para>通过<literal>ClientRequestor</literal>将消息发送</para>
</listitem>
<listitem>
<para>使用 <literal
>org.apache.activemq.api.core.management.ManagementHelper</literal>类从管理操作結果中提取返回值。</para>
</listitem>
</orderedlist>
<para>例如,要得到核心队列<literal>exampleQueue</literal>中消息的数量:</para>
<programlisting>
ClientSession session = ...
ClientRequestor requestor = new ClientRequestor(session, "jms.queue.activemq.management");
ClientMessage message = session.createMessage(false);
ManagementHelper.putAttribute(message, "core.queue.exampleQueue", "messageCount");
ClientMessage reply = requestor.request(m);
int count = (Integer) ManagementHelper.getResult(reply);
System.out.println("There are " + count + " messages in exampleQueue");
</programlisting>
<para>管理操作名及其参数必须和<literal>management</literal>包中定义的Java接口一致。</para>
<para>资源的名称是用<literal>org.apache.activemq.api.core.management.ResourceNames</literal>类来生成的,
命名都非常直观(如核心队列<literal>exampleQueue</literal>的名称为<literal>core.queue.exampleQueue</literal>
JMS Topic <literal>exampleTopic</literal>的名称为<literal>jms.topic.exampleTopic</literal>,等等)。
</para>
<section id="management.core.configuration">
<title>配置核心管理</title>
<para>管理地址的配置在文件<literal
>activemq-configuration.xml</literal>中:</para>
<programlisting>
&lt;management-address&gt;jms.queue.activemq.management&lt;/management-address&gt;
</programlisting>
<para>它的默认地址是<literal>jms.queue.activemq.management</literal> (地址前缀加上
“jms.queue”是为了方便JMS客户端也可以向它发送管理消息。</para>
<para>管理地址需要一个<emphasis>特殊</emphasis>的用户权限
<literal>manage</literal>来接收并处理管理消息。这个权限也在activemq-configuration.xml文件中配置</para>
<programlisting>
&lt;!-- users with the admin role will be allowed to manage --&gt;
&lt;!-- ActiveMQ using management messages --&gt;
&lt;security-setting match="jms.queue.activemq.management"&gt;
&lt;permission type="manage" roles="admin" /&gt;
&lt;/security-setting&gt;
</programlisting>
</section>
</section>
<section id="management.jms">
<title>使用JMS进行管理</title>
<para>使用JMS管理ActiveMQ与使用核心API管理ActiveMQ十分相似。</para>
<para>其中一个重要的不同是JMS需要一个JMS队列来发送消息而核心接口使用的是一个地址</para>
<para><emphasis>管理队列</emphasis>是一个特殊的队列,它需要客户端直接实例化:</para>
<programlisting>
Queue managementQueue = ActiveMQJMSClient.createQueue("activemq.management");
</programlisting>
<para>其余步骤完全和使用核心接口一样,只是相应的对象不同:</para>
<orderedlist>
<listitem>
<para>创建一个<literal>QueueRequestor</literal>来向管理地址发送管理消息并接收回答。</para>
</listitem>
<listitem>
<para>创建一个<literal>消息</literal></para>
</listitem>
<listitem>
<para>使用 <literal>org.apache.activemq.api.jms.management.JMSManagementHelper</literal>类向消息中设置管理参数。</para>
</listitem>
<listitem>
<para>再使用<literal>QueueRequestor</literal>发送消息。</para>
</listitem>
<listitem>
<para>使用<literal>org.apache.activemq.api.jms.management.JMSManagementHelper</literal>来从回答中提取返回結果。</para>
</listitem>
</orderedlist>
<para>例如要得到一个JMS队列<literal>exampleQueue</literal>中有多少消息:</para>
<programlisting>
Queue managementQueue = ActiveMQJMSClient.createQueue("activemq.management");
QueueSession session = ...
QueueRequestor requestor = new QueueRequestor(session, managementQueue);
connection.start();
Message message = session.createMessage();
JMSManagementHelper.putAttribute(message, "jms.queue.exampleQueue", "messageCount");
Message reply = requestor.request(message);
int count = (Integer)JMSManagementHelper.getResult(reply);
System.out.println("There are " + count + " messages in exampleQueue");
</programlisting>
<section>
<title>配置JMS管理</title>
<para>JMS管理的配置与核心接口管理的配置步骤是一样的参见<xref linkend="management.core.configuration"/>)。</para>
</section>
<section>
<title>例子</title>
<para>参见<xref linkend="examples.management"/>它展示了如何使用JMS消息来管理ActiveMQ。</para>
</section>
</section>
<section id="management.notifications">
<title>管理通知</title>
<para>ActiveMQ可以向listener发送各种事件的<emphasis>通知</emphasis>(如资源的创建,安全破坏等)。</para>
<para>有三种方式接收管理通知</para>
<itemizedlist>
<listitem>
<para>JMX通知</para>
</listitem>
<listitem>
<para>核心消息</para>
</listitem>
<listitem>
<para>JMS消息</para>
</listitem>
</itemizedlist>
<section>
<title>JMX通知</title>
<para>如果设置了JMX参见<xref linkend="management.jmx.configuration"/>),就可以通过订阅以下
两个MBean来获得通知</para>
<itemizedlist>
<listitem>
<para><literal>org.apache.activemq:module=Core,type=Server</literal> 可以获得有关
<emphasis>核心</emphasis>资源的通知</para>
</listitem>
<listitem>
<para><literal>org.apache.activemq:module=JMS,type=Server</literal>可以获得有关
<emphasis>JMS</emphasis>资源的通知</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>核心消息通知</title>
<para>ActiveMQ定义了一个特殊的<emphasis>管理通知地址</emphasis>。核心队列绑定到该地址后,客户
端就可以接收以核心消息形式发送的管理信通知了。</para>
<para>一个核心客户端要想接收到管理通知,它必须要创建一个队列并绑定到这个管理通知地址上,然后从这个
队列接收通知。</para>
<para>通知消息就是普通的核心消息加上相关的属性(如通知类型,事件发生时间,资源等)。</para>
<para>由于是标准的核心消息,使用选择器还能够过滤掉一部分通知而只接收感兴趣的通知。</para>
<section id="management.notifications.core.configuration">
<title>配置核心管理通知地址</title>
<para>用来发送管理通知的地址在文件中<literal>activemq-configuration.xml</literal>配置:</para>
<programlisting>
&lt;management-notification-address&gt;activemq.notifications&lt;/management-notification-address&gt;
</programlisting>
<para>默认的地址是<literal>activemq.notifications</literal></para>
</section>
</section>
<section>
<title>JMS消息通知</title>
<para>ActiveMQ还可以通过JMS消息的方式发送通知。</para>
<para>这种方式与核心消息通知相似但是有一个重要的不同JMS消息需要一个JMS的目标通常是一个Topic</para>
<para>要通过一个JMS目标来接收管理通知必须将服务器的管理通知地址修改为以<literal>jms.queue</literal>开头(如果是一个
JMS队列或者<literal>jms.topic</literal>(如果是一个话题):</para>
<programlisting>
&lt;!-- 通知将从JMS话题 "notificationsTopic"上接收 -->
&lt;management-notification-address&gt;jms.topic.notificationsTopic&lt;/management-notification-address&gt;
</programlisting>
<para>这个通知话题一旦被创建,就可以接收消息了(或者使用<literal>MessageListener</literal></para>
<programlisting>
Topic notificationsTopic = ActiveMQJMSClient.createTopic("notificationsTopic");
Session session = ...
MessageConsumer notificationConsumer = session.createConsumer(notificationsTopic);
notificationConsumer.setMessageListener(new MessageListener()
{
public void onMessage(Message notif)
{
System.out.println("------------------------");
System.out.println("Received notification:");
try
{
Enumeration propertyNames = notif.getPropertyNames();
while (propertyNames.hasMoreElements())
{
String propertyName = (String)propertyNames.nextElement();
System.out.format(" %s: %s\n", propertyName, notif.getObjectProperty(propertyName));
}
}
catch (JMSException e)
{
}
System.out.println("------------------------");
}
});
</programlisting>
</section>
<section>
<title>例子</title>
<para>参见<xref linkend="examples.management-notifications"/>。本例采用了JMS的<literal>
MessageListener</literal>方法从ActiveMQ
服务器接收管理通知。</para>
</section>
</section>
<section id="management.message-counters">
<title>消息计数器</title>
<para>ActiveMQ保存着队列的历史数据而消息计数器可以从服务器上获取这些信息。</para>
<para>这些信息可以显示队列的一些<emphasis>趋势</emphasis>。例如,使用管理接口你可以定期来查询一个队列
的消息数量。但这个数量不足以说明这个队列是否在工作--也许这个队列既没有发送者也没有接收者;也许这个队列
在不停地发送与接收,但是发送消息的速度与接收的速度相等。两咱情况下都会造成消息数在队列中不变,但实际队列
的状态确完全不一样。</para>
<para>消息计数器可以提供队列的更多的信息:</para>
<itemizedlist>
<listitem>
<para><literal>count</literal></para>
<para>从服务器启动时加到队列中的<emphasis></emphasis>消息数。</para>
</listitem>
<listitem>
<para><literal>countDelta</literal></para>
<para><emphasis>上次消息计数器更新</emphasis>后加入到队列的消息数。</para>
</listitem>
<listitem>
<para><literal>depth</literal></para>
<para>队列<emphasis>当前</emphasis>的消息数。</para>
</listitem>
<listitem>
<para><literal>depthDelta</literal></para>
<para><emphasis>上次消息计数器更新</emphasis>后被加入/删除的消息<emphasis>总数</emphasis>
例如,如果<literal>depthDelta</literal><literal>-10</literal>就意谓着有10个消息从
队列中删除了有可能是2个消息加入了但有12个消息删除了</para>
</listitem>
<listitem>
<para><literal>lastAddTimestamp</literal></para>
<para>最后一个消息加入到队列的时间戳。</para>
</listitem>
<listitem>
<para><literal>udpateTimestamp</literal></para>
<para>最后一次消息计数器更新的时间戳。</para>
</listitem>
</itemizedlist>
<section id="configuring.message.counters">
<title>配置消息计数器</title>
<para>默认的消息计数器是关闭的,因为它需要占用一些内存。</para>
<para>要打开消息计数器,编辑<literal>activemq-configuration.xml</literal>文件将其设为<literal>true</literal></para>
<programlisting>
&lt;message-counter-enabled&gt;true&lt;/message-counter-enabled&gt;
</programlisting>
<para>消息计数器会保存队列的历史数据默认是10天。它以一定间隔默认10秒一次对每个队列进行扫描。
如果消息计数器打开,这些参数可以在<literal>activemq-configuration.xml</literal>文件中进行调整:</para>
<programlisting>
&lt;!-- keep history for a week --&gt;
&lt;message-counter-max-day-history&gt;7&lt;/message-counter-max-day-history&gt;
&lt;!-- sample the queues every minute (60000ms) --&gt;
&lt;message-counter-sample-period&gt;60000&lt;/message-counter-sample-period&gt;
</programlisting>
<para>使用管理接口可以获得消息计数器。例如要使用JMX得到一个JMS队列的消息计数器</para>
<programlisting>
// retrieve a connection to ActiveMQ's MBeanServer
MBeanServerConnection mbsc = ...
JMSQueueControlMBean queueControl = (JMSQueueControl)MBeanServerInvocationHandler.newProxyInstance(mbsc,
on,
JMSQueueControl.class,
false);
// message counters are retrieved as a JSON String
String counters = queueControl.listMessageCounter();
// use the MessageCounterInfo helper class to manipulate message counters more easily
MessageCounterInfo messageCounter = MessageCounterInfo.fromJSON(counters);
System.out.format("%s message(s) in the queue (since last sample: %s)\n",
counter.getDepth(),
counter.getDepthDelta());
</programlisting>
</section>
<section>
<title>例子</title>
<para>参见<xref linkend="examples.message-counters"/>。这个例子使用消息计数器来获得一个JMS队列的相关数据。</para>
</section>
</section>
<section>
<title>通过JBoss应用服务器的Admin Console来管理ActiveMQ的资源</title>
<para>通过JBoss应用服务器的Admin Console可以创建与配置ActiveMQ的各种资源。</para>
<para>Admin Console允许你创建各种目标JMS话题与队列和JMS的连接工厂。</para>
<para>登录admin console后你在左边的树中会看到JMS Manager节点。所有ActiveMQ的资源都属于这个节点。在它的下面有JMS Queues、
Topics以及Connection Factories。分别点击它们将会看到相应的资源。下面将解释如何创建并配置它们。</para>
<section>
<title>JMS队列</title>
<para>要创建一个新的JMS队列点击JMS Queues将列出当前的队列。在右边的窗口中有一个“add a new resource“按钮点击这个按钮
并选择默认JMS 队列模板。点击“continue“。填入相应的队列名称与JNDI名称。其它的参数值都给出了合理的默认值通常情况下
不用改动它们。在底部可以配置安全角色,如果你不提供将使用默认的配置。当这个队列成功创建后这些配置将会显示出来。除了队列的名字
和JNDI名字外其它参数都可以在contiguration标签页下进行修改。下面就对它们分别解释。</para>
<para>点击 configuration后你将看到如下显示</para>
<para>
<graphic fileref="images/console1.png" align="center"/>
</para>
<para>name和JNDI name是不能改变的。如果你想改变它们必须重新创建队列。其它选项是关于地址设置与安全设置。
默认的地址设置来自于服务器的配置。如果你通过console修改或创建一个队列那么就会增加一条新的地址设置。有关
地址设置的完整说明参见<xref linkend="queue-attributes.address-settings"/></para>
<para>要删除一个队列只要点击队列名称旁边的“delete“按钮即可。与此队列相关的任何地址设置或安全设置也将被删除。</para>
<para>配置的最后一部分是安全角色。如果在创建时没有给出则默认的安全设置将会显示在屏幕上。如果它们被修改并更新则队列的安全设置
将被更新。关于安全设置参见<xref linkend="security"/></para>
<para>在console中还有一个metrics标签页它显示了队列的各项统计数据如消息计数接收者计数等。</para>
<para>在control标签页中可以对队列进行各种操作比如启动和停止队列对队列中的消息进行列表移动变为过期删除等。
要进行一项操作只要点击相应的按钮然后在出现的提示中输入相应的参数再点击ok按钮即可。操作的結果会显示在屏幕的底部。</para>
</section>
<section>
<title>JMS话题</title>
<para>创建及配置JMS话题几乎与队列的操作是一样的。唯一不同的是这些配置应用于一个代表订阅的队列。</para>
</section>
<section>
<title>JMS连接工厂</title>
<para>JMS连接工厂的创建的操作过程与上述队列或话题的操作一致只是配置具体的参数不同而已。关于连接工厂的参数参见配置索引。</para>
</section>
</section>
</chapter>

View File

@ -1,128 +0,0 @@
<?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. -->
<!-- ============================================================================= -->
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
"../../../lib/docbook-support/support/docbook-dtd/docbookx.dtd" [
<!ENTITY appserver-integration SYSTEM "appserver-integration.xml">
<!ENTITY architecture SYSTEM "architecture.xml">
<!ENTITY client-reconnection SYSTEM "client-reconnection.xml">
<!ENTITY client-classpath SYSTEM "client-classpath.xml">
<!ENTITY clusters SYSTEM "clusters.xml">
<!ENTITY configuration-index SYSTEM "configuration-index.xml">
<!ENTITY configuring-transports SYSTEM "configuring-transports.xml">
<!ENTITY connection-ttl SYSTEM "connection-ttl.xml">
<!ENTITY core-bridges SYSTEM "core-bridges.xml">
<!ENTITY diverts SYSTEM "diverts.xml">
<!ENTITY duplicate-detection SYSTEM "duplicate-detection.xml">
<!ENTITY embedding-activemq SYSTEM "embedding-activemq.xml">
<!ENTITY examples SYSTEM "examples.xml">
<!ENTITY filter-expressions SYSTEM "filter-expressions.xml">
<!ENTITY flow-control SYSTEM "flow-control.xml">
<!ENTITY ha SYSTEM "ha.xml">
<!ENTITY transaction-config SYSTEM "transaction-config.xml">
<!ENTITY intercepting-operations SYSTEM "intercepting-operations.xml">
<!ENTITY interoperability SYSTEM "interoperability.xml">
<!ENTITY jms-bridge SYSTEM "jms-bridge.xml">
<!ENTITY jms-core-mapping SYSTEM "jms-core-mapping.xml">
<!ENTITY large-messages SYSTEM "large-messages.xml">
<!ENTITY last-value-queues SYSTEM "last-value-queues.xml">
<!ENTITY logging SYSTEM "logging.xml">
<!ENTITY management SYSTEM "management.xml">
<!ENTITY message-expiry SYSTEM "message-expiry.xml">
<!ENTITY message-grouping SYSTEM "message-grouping.xml">
<!ENTITY messaging-concepts SYSTEM "messaging-concepts.xml">
<!ENTITY notice SYSTEM "notice.xml">
<!ENTITY paging SYSTEM "paging.xml">
<!ENTITY perf-tuning SYSTEM "perf-tuning.xml">
<!ENTITY persistence SYSTEM "persistence.xml">
<!ENTITY pre-acknowledge SYSTEM "pre-acknowledge.xml">
<!ENTITY preface SYSTEM "preface.xml">
<!ENTITY project-info SYSTEM "project-info.xml">
<!ENTITY queue-attributes SYSTEM "queue-attributes.xml">
<!ENTITY scheduled-messages SYSTEM "scheduled-messages.xml">
<!ENTITY security SYSTEM "security.xml">
<!ENTITY send-guarantees SYSTEM "send-guarantees.xml">
<!ENTITY thread-pooling SYSTEM "thread-pooling.xml">
<!ENTITY undelivered-messages SYSTEM "undelivered-messages.xml">
<!ENTITY using-core SYSTEM "using-core.xml">
<!ENTITY using-jms SYSTEM "using-jms.xml">
<!ENTITY using-server SYSTEM "using-server.xml">
<!ENTITY wildcard-syntax SYSTEM "wildcard-syntax.xml">
<!ENTITY wildcard-routing SYSTEM "wildcard-routing.xml">
<!ENTITY libaio SYSTEM "libaio.xml">
]>
<book lang="en">
<bookinfo>
<title>ActiveMQ 2.1用户手册</title>
<subtitle>Putting the buzz in messaging</subtitle>
</bookinfo>
<toc></toc>
&notice;
&preface;
&project-info;
&messaging-concepts;
&architecture;
&using-server;
&using-jms;
&using-core;
&jms-core-mapping;
&client-classpath;
&examples;
&wildcard-routing;
&wildcard-syntax;
&filter-expressions;
&persistence;
&configuring-transports;
&connection-ttl;
&transaction-config;
&flow-control;
&send-guarantees;
&undelivered-messages;
&message-expiry;
&large-messages;
&paging;
&queue-attributes;
&scheduled-messages;
&last-value-queues;
&message-grouping;
&pre-acknowledge;
&management;
&security;
&appserver-integration;
&jms-bridge;
&client-reconnection;
&diverts;
&core-bridges;
&duplicate-detection;
&clusters;
&ha;
&libaio;
&thread-pooling;
&logging;
&embedding-activemq;
&intercepting-operations;
&interoperability;
&perf-tuning;
&configuration-index;
</book>

View File

@ -1,81 +0,0 @@
<?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="message-expiry">
<title>过期的消息</title>
<para>消息在发送时有一个可选的<emphasis>生存时间</emphasis>属性。</para>
<para>如果一个消息已经超过了它的生存时间ActiveMQ不再将它传递给任何接收者。
服务器会将过期的消息抛弃。</para>
<para>ActiveMQ的地址可以配置一个过期地址当消息过期时它们被从队列中删除并被转移到过期地址中。
多个不同的队列可以绑定到一个过期地址上。这些过期的消息过后可以接收下来以供分析用。</para>
<section>
<title>过期消息的配置</title>
<para>如果使用ActiveMQ核心API可以直接在消息上设置过期时间</para>
<programlisting>
// message will expire in 5000ms from now
message.setExpiration(System.currentTimeMillis() + 5000);
</programlisting>
<para>JMS的MessageProducer可以设置一个TimeToLive来控制其发送的消息</para>
<programlisting>
// messages sent by this producer will be retained for 5s (5000ms) before expiration
producer.setTimeToLive(5000);
</programlisting>
<para>从过期地址中接收下来的消息有以下属性:</para>
<itemizedlist>
<listitem>
<para><literal>_HQ_ORIG_ADDRESS</literal></para>
<para>这是一个字符串,它是该消息的<emphasis>原始地址</emphasis></para>
</listitem>
<listitem>
<para><literal>_HQ_ACTUAL_EXPIRY</literal></para>
<para>一个长整型量,代表此消息<emphasis>实际过期时间</emphasis></para>
</listitem>
</itemizedlist>
</section>
<section id="message-expiry.configuring">
<title>配置过期地址</title>
<para>过期地址配置在地址设置address-setting</para>
<programlisting>
&lt;!-- expired messages in exampleQueue will be sent to the expiry address expiryQueue --&gt;
&lt;address-setting match="jms.queue.exampleQueue"&gt;
&lt;expiry-address&gt;jms.queue.expiryQueue&lt;/expiry-address&gt;
&lt;/address-setting&gt;
</programlisting>
<para>如果没有定义过期地址,当一个消息过期时,它将被删除。配置过期地址时可以使用通配符
来给一组地址配置过期地址。(参见<xref linkend="wildcard-syntax"/>)。</para>
</section>
<section id="configuring.expiry.reaper">
<title>配置过期回收线程</title>
<para>ActiveMQ有一个回收线程定期地检查队列中的消息目的是发现是否有消息过期。</para>
<para><literal>activemq-configuration.xml</literal>文件中可以对回收线程进行配置,参数如下:</para>
<itemizedlist>
<listitem>
<para><literal>message-expiry-scan-period</literal></para>
<para>过期消息的扫描间隔单位毫秒默认为30000ms。如要关闭扫描将其设为<literal>-1</literal></para>
</listitem>
<listitem>
<para><literal>message-expiry-thread-priority</literal></para>
<para>回收线程的优先级为0到9的整数9优先级最高。默认是3</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>例子</title>
<para>参见<xref linkend="examples.expiry"/>。这个例子展示了在JMS中如何配置使用消息过期功能。</para>
</section>
</chapter>

View File

@ -1,149 +0,0 @@
<?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="message-grouping">
<title>消息分组</title>
<para>消息组是具有下列特性的消息集合:</para>
<itemizedlist>
<listitem>
<para>在一个消息组中的消息有相同的组标识id即它们的<literal>JMSXGroupID</literal>JMS
<literal>_HQ_GROUP_ID</literal>ActiveMQ核心的值相同。</para>
</listitem>
<listitem>
<para>不管存在多少个接收者consumer一个消息组的所有消息总是被同一个接收者所接收。一个组id总是
与同一个接收者相关联。如果这个接收者被关闭,另外一个接收者就被选中来代替它接收该消息组的消息。</para>
</listitem>
</itemizedlist>
<para>消息组在需要同一个接收者按顺序处理某类消息的时候很有用。</para>
<para>一支股票的订购就是一个例子。某支股票的订单需要同一个接收者按顺序处理。于是可以每支股票有一个接收者
来处理也可以用少一些的接收者然后将每支股票的名字设在消息的_HQ_GROUP_ID参数中。</para>
<para>这样可以保证一支股票的消息只被同一个接收者处理。</para>
<section>
<title>使用核心接口</title>
<para>用来标识一个消息组的参数是 <literal>"_HQ_GROUP_ID""</literal> (或者相应的常量<literal
>MessageImpl.HDR_GROUP_ID</literal>)。另一种方法是在<literal>SessionFactory</literal>
中将<literal>autogroup</literal>设置为true。这样做的话组id是随机给出的。</para>
</section>
<section id="message-grouping.jmsconfigure">
<title>使用JMS</title>
<para>用来标识一个消息组的参数是<literal>JMSXGroupID</literal></para>
<programlisting>
// send 2 messages in the same group to ensure the same
// consumer will receive both
Message message = ...
message.setStringProperty("JMSXGroupID", "Group-0");
producer.send(message);
message = ...
message.setStringProperty("JMSXGroupID", "Group-0");
producer.send(message);
</programlisting>
<para>另一个方法是将<literal>ActiveMQConnectonFactory</literal><literal>autogroup</literal>
属性设为true或者在<literal>activemq-jms.xml</literal>文件中进行配置:</para>
<programlisting>&lt;connection-factory name="ConnectionFactory">
&lt;connectors>
&lt;connector-ref connector-name="netty-connector"/>
&lt;/connectors>
&lt;entries>
&lt;entry name="ConnectionFactory"/>
&lt;/entries>
&lt;autogroup>true&lt;/autogroup>
&lt;/connection-factory></programlisting>
<para>还可以通过连接工厂来设置组id。来自这个连接工厂的所有的发送者producer发送的消息的<literal
>JMSXGroupID</literal>将具有指定的值。这种方法需要在<literal>activemq-jms.xml</literal>
文件中作如下配置:
<programlisting>
&lt;connection-factory name="ConnectionFactory"&gt;
&lt;connectors&gt;
&lt;connector-ref connector-name="netty-connector"/&gt;
&lt;/connectors&gt;
&lt;entries&gt;
&lt;entry name="ConnectionFactory"/&gt;
&lt;/entries>
&lt;group-id>Group-0&lt;/group-id&gt;
&lt;/connection-factory&gt;
</programlisting></para>
</section>
<section>
<title>例子</title>
<para>参见<xref linkend="examples.message-group"/>。这个例子展示的是在JMS中如何配置与使用消息组。</para>
</section>
<section>
<title>例子</title>
<para><xref linkend="examples.message-group2"/>是另外一个消息组的例子,在这个例子中通过配置连接工厂
来使用消息组。</para>
</section>
<section>
<title>集群中的消息组</title>
<para>在集群中使用消息组是相对比较复杂的。这是因在在集群中,一个消息组中的消息有可能被送到集群中的任一全节点,
这就要求每个节点都要知道这个消息是属于哪个节点上的哪个接收者consumer。一个消息组的消息往往会被发送到
集群中的一个节点,而该消息组的接收者在另一个节点上。每个节点都要知道这些细节以便能将消息正确路由到所属接收
者所在的节点上。</para>
<para>为了解决上述问题我们使用了消息组处理器。每个节点都有一个自己的消息组处理器。当一个带有组id的消息收到时
这些节点的消息组处理器就会协同作出决定该如何对这个消息进行路由。</para>
<para>消息组处理器有两种:本地消息组处理器和远程消息组处理器。在一个集群中要选择一个节点作为本地消息组处理器的
节点,集群中所有其它的节点都持有远程消息组处理器。在集群中由本地消息组处理器最終决定消息怎样路由,其它的远程
处理器配合本地处理器完成决策。消息组处理器的配置在<emphasis role="italic">activemq-configuration.xml</emphasis>
文件中,下面就是一个例子:
<programlisting> &lt;grouping-handler name="my-grouping-handler">
&lt;type>LOCAL&lt;/type>
&lt;address>jms&lt;/address>
&lt;timeout>5000&lt;/timeout>
&lt;/grouping-handler>
&lt;grouping-handler name="my-grouping-handler">
&lt;type>REMOTE&lt;/type>
&lt;address>jms&lt;/address>
&lt;timeout>5000&lt;/timeout>
&lt;/grouping-handler></programlisting></para>
<para><emphasis role="italic">address</emphasis>属性表示一个集群的连接以及它使用的地址。有关如何配置集群
参见集群章节。<emphasis role="italic">timeout</emphasis>属性代表做出路由决定所需要等待的时间。如果超过
了这个时间还没有做出决定,则会抛出异常。这可以保证严格的顺序。</para>
<para>收到消息的节点会首先提出一个消息路由的建议。它采用轮询方式来选择一个合适的路由。它首先选择一个本地的队列,之后
再选择一个有接收者的队列。如果这个建议被所有组处理器接受,消息就会被路由到所选的队列。如果被拒绝就提出另一个路
由方案,如此反复直到方案被接受为止。队列选择后所有其它的节点都将消息路由到这个队列。这样所有的消息组的消息在一个
节点上进行处理,也就是该节点上的接收者接收所有的同组的消息。</para>
<para>由于只有一个本地处理器,如果它的节点出现故障则无法做出决定。这时所有的消息将不能被传递,并且会抛出异常。
为了避免这一单点故障,本地处理器可以在备份节点上有一个复本。只要创建备份节点并配置一个相同的本地处理器即可。</para>
<para/>
<section>
<title>集群消息组的最佳使用惯例</title>
<para>下面是一些很好的建议:<orderedlist>
<listitem>
<para>尽可能使接收者均匀分布在不同的节点上。由于消息组的消息总是传递到同一个队列的同一个接收者,
如果你经常性地创建与关闭接收者,就可能出现消息由于没有接收者而传递不出去,造成消息在队列中
不断积累的情况。因此,尽量要避免关闭接收者,或者确保有足够数量的接收者。例如,如果你的集群
有3个节点那么就创建3个接收者。</para>
</listitem>
<listitem>
<para>尽可能使用持久型队列。如果消息组的消息与一个队列绑定,一旦这个队列被删除,其它节点可能仍然
尝试向这个已删除的队列路由消息。为避免这种情况,要确保这个队列由发送消息的会话来删除。这样如果
下一个消息发出后发现原来的队列被删除,新的路由建议就会提出。另外一种方案是你可以重新使用一个不
同的组ID。</para>
</listitem>
<listitem>
<para>一定要确保本地的消息组处理器有备份。这样在有故障时消息组仍然可以正常工作。</para>
</listitem>
</orderedlist></para>
</section>
<section>
<title>集群消息组例子</title>
<para>参见<xref linkend="examples.clustered.grouping"/>这个例子给出了如何在ActiveMQ集群中配置消息组。</para>
</section>
</section>
</chapter>

View File

@ -1,164 +0,0 @@
<?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="messaging-concepts">
<title>消息的相关概念</title>
<para>ActiveMQ是一个异步的 <ulink
url="http://en.wikipedia.org/wiki/Message_oriented_middleware">
面向消息的中间件</ulink>。在本文档中我们简称为消息系统。</para>
<para>首先我们简要介绍消息系统是做什么的,在哪些领域得到应用,以及与消息相关的一些概念。</para>
<para>如果你已经对消息系统的这些方面的知识很熟悉,可以跳过本章内容。</para>
<section>
<title>消息相关的概念</title>
<para>消息系统可以将不同异种的系统松散地耦合在一起,提供基本的可靠性,事务及其它功能的支持。</para>
<para>与基于<ulink
url="http://en.wikipedia.org/wiki/Remote_procedure_call">远程过程调用
</ulink> (RPC) 的系统不同,消息系统主要采用异步传送的方式,请求与响应之间的耦合很松。
大多数的消息系统也支持请求-响应的方式,但这不是消息的主要功能。</para>
<para>端与端之间采用异步通信的好处是可以充分利用硬件资源最大程度减少IO操作引起的线程阻塞并充分利用网络带宽。
而采用RPC方式每一个请求必须要等待响应返回才能继续因而要依赖你的网络的速度
<emphasis role="italic">latency</emphasis>)。异步系统则能将消息以管道的方式传送,
它只受<emphasis role="italic">带宽</emphasis>的限制,并不因网络速度而降低效率。利用异步的方式往往可以创建更高效率的应用。</para>
<para>消息系统将消息的发送方与接收方分开,使消息的发送所接收完全独立于对方,有利于创建灵活的、松耦的系统。</para>
<para>大型的企业应用通常采用消息系统来实现一种消息总线,并基于这个总线将企业的各种不同结构的系统松散地连在一起工作。
消息总线也常常是<ulink url="http://en.wikipedia.org/wiki/Enterprise_service_bus">企业服务总线</ulink>(ESB)的核心。
采用这种方式搭建的松耦合系统可以非常容易地扩展和修改。由于系统各模块之间的信赖关系很弱,所以在需要时可以对系统灵活地添加和减少模块。</para>
</section>
<section>
<title>消息的种类</title>
<para>消息系统通常支持两种异步的传送模式:<ulink url="http://en.wikipedia.org/wiki/Message_queue">消息队列</ulink>
(又称为点对点消息传送)模式和<ulink url="http://en.wikipedia.org/wiki/Publish_subscribe">广播/订阅模式</ulink></para>
<section>
<title>消息队列模式</title>
<para>在这种模式中消息被发送到队列中。通常消息会被持久化以保证可靠的传送。消息系统会将队列中的消息传送给接收者
receiver或consumer。当接收者处理消息完成后它会发出完成的通知给消息系统。得到通知的消息就会从队列
中删除,因此该消息不会被再次传送。如果在收到消息前消息服务器发生故障导致系统崩溃,当系统恢复时,
该消息会被再次传送给接收者。</para>
<para>这种模式允许一个队列有多个接收者。但是一个消息最多只传送给一个接收者。一个队列的消息发送者sender或producer)
与接收者是完全独立的。它们不知道彼此的存在。</para>
<para>图书订单系统是一个典型的消息队列的用例。每一个订单都被包装为一个消息传送到订单队列中。假定有多个图书订购的终
端向订单队列发关订单消息。当一个消息到达队列时它被持久化以防止系统崩溃时订单的丢失。再假定有多个订单处理中心
分布在不同的机器上接收这个订单队列的消息。消息系统将每一个消息发送给其中一个(并且只发送一个)接收者(即一个订单处理模块)。
这样不同的订单可能会被不同的处理模块处理,但一个订单只会被处理一次。</para>
<para>当订单处理模块接收到一个消息,对它进行处理后将订单信息发送给仓库系统并更新订单数据库。处理完成后它会发出通知告诉服务
器可以删除此消息。通常这一系列和处理(接收,发送给仓库系统,更新数据库以及通知)会被作为一个交易来处理以保证它的完整性
<ulink url="http://en.wikipedia.org/wiki/ACID">ACID</ulink>)。</para>
</section>
<section>
<title>消息的广播/订阅模式</title>
<para>这种模式中多个发送者将消息发送到服务器中一个特定的实体JMS中通常称为话题topic)。
一个Topic常常有多个订阅者subscription即消息的接收者</para>
<para>与消息队列的接收者不同,每个订阅者都会收到发送的队列中的每个消息。</para>
<para>订阅者可以选择为固定的方式(<emphasis role="italic">durable</emphasis>)。采用这种方式的订阅者,
其消息会保留直到被接收为止。即使是其间服务器发生过故障或重启也不受影响。非固定的订阅者只在其连接期间有效,
一但连接断开其消息将不会保留。</para>
<para>电子消息订阅是消息广播模式的一个例子。当新闻被世界各地的编辑編好后他们将其发关到新闻topic。
同样对这些新闻兴趣的读者会订阅这个topic。消息系统将保证每个订阅者都能够收到每一篇新闻稿。</para>
</section>
</section>
<section>
<title>传送的可靠性</title>
<para>大多数的消息系统是<emphasis role="italic">可靠的消息传送系统</emphasis>。消息系统可以保证一个消息被传送
给一个并且只传送给一个队列的接收者或每个话题的固定的订阅者。一个消息不会被传送两次。即使在系统出现故障时也是如此。
这一特性对于很多企业来说是非常重要的。比如你想要保证订单不丢失或被处理两次以上,就可以利用该特性。</para>
<para>在某些情况下这种“一次并且只有一次”的传送方式并不是很重要,重复消息和消息的丢失并不影响系统的功能。比如股票价格
的更新消息,它并不需要保证每次都能收到,因为前一次更新很快就会被下一次代替。这样的功能消息系统也可以支持。</para>
</section>
<section>
<title>交易Transactions)</title>
<para>消息系统通常支持在一次本地交易中发送并通知多个消息。ActiveMQ还支持分布式交易。它可以通过Java的XA和JTA接口
将消息的发送与通知做为一个分布式交易的一部分来完成。</para>
</section>
<section>
<title>持久性Durability</title>
<para>消息可以分为持久消息和非持久消息。持久消息被保存到永久的存储介质中,不受服务器故障与重启的影响。非持久消息在服务
器故障与重启时则会丢失。像订单,交易信息属于持久消息,而股票价格更新由于它的即时性则可以做为非持久消息来处理。</para>
</section>
<section>
<title>消息API和协议</title>
<para>客户端的应用程序怎样访问消息系统来进行消息的发送与接收呢?</para>
<para>一些消息系统提供私有的API客户端可以通过这些私有的API与相应的消息系统交互实现消息的收发。</para>
<para>除此之外,还存在着一些标准的交互方式可供使用。另外还有一些标准正在不断完善。下面我们就介绍一下这些标准。</para>
<para>Let's take a brief look at these:</para>
<section>
<title>Java消息服务JMS</title>
<para><ulink url="http://en.wikipedia.org/wiki/Java_Message_Service">JMS</ulink> 属于Sun公司JEE规范的一部分。
它定义了一套标准的API支持消息队列和广播订阅模式。JMS是一套非常精简的通用的标准它将当时已经存在的消息系统的共同功能包括了进去。
</para>
<para>JMS是一个广泛使用的API绝大多数的消息系统都支持它。JMS只有Java的客户端才可以使用。</para>
<para>JMS并没有定义传输的格式wire format。因此不同的JMS消息服务器的和客户端相互之间通常不能交互这是因为每个消息系统都自己的传输格式。</para>
<para>ActiveMQ全面支持JMS 1.1 API。</para>
</section>
<section>
<title>专有的API</title>
<para>很多系统提供自己的一套API来与其消息系统进行通迅其优势是它可以允许客户端使用其全部的功能。
像JMS那样的标准API往往不能提供许多消息系统所支持的额外的功能。</para>
<para>ActiveMQ提供了一套自有的核心API客户端程序可以通过它充分利用ActiveMQ的强大功能。
这对于一些JMS API满足不了的需求是非常有用的。</para>
</section>
<section>
<title>RESTful API</title>
<para>采用<ulink url="http://en.wikipedia.org/wiki/Representational_State_Transfer"
>REST</ulink> REST[http://en.wikipedia.org/wiki/Representational_State_Transfer]方式与消息系统交互越来越被关注。</para>
<para>由于云计算技术的API标准目前倾向于采用REST的方式所以采用REST方式的消息系统很有望成为云计算中消息传送的标准。</para>
<para>REST方式中的各种消息资源以URI的方式来定义。用户通过一套很简单的操作与这些资源相交互如PUT、POST、GET等。HTTP通常用来作为REST方式的通信协议。</para>
<para>采用HTTP的好处是它很简单实用并且internet经过多年的发展已经能很好的支持HTTP协议。</para>
<para>ActiveMQ将会很快地支持REST方式的API。</para>
</section>
<section>
<title>STOMP</title>
<para><ulink
url="http://stomp.codehaus.org/"
>Stomp</ulink> 是为消息系统定义的一套简单的文本传输协议。它定义了一种线上传输的格式,
因此采用Stomp编写的客户端可以与所有支持Stomp的消息系统交互。Stomp的客户端可以用多种编程语言来实现。</para>
<para>有关在ActiveMQ中如何使用Stomp的详细内容请参见<xref linkend="stomp"/></para>
</section>
<section>
<title>AMQP</title>
<para><ulink url="http://en.wikipedia.org/wiki/AMQP">AMQP</ulink> 是一套可支持互操作的消息规范。
它定义了自己的传输格式因些任何AMQP的客户端都可以和支持AMQP的系统进行交互。AMQP的客户端可以用多种编程语言来实现。</para>
<para>ActiveMQ将会很快地支持AMQP。</para>
</section>
</section>
<section>
<title>高可获得性Availability</title>
<para>高可获得性是指在系统中有一个或多个服务器发生故障时仍然能够维持运转的特性。不同的消息系统对高可获得性的支持程度是不同的。</para>
<para>ActiveMQ支持自动失效备援failover也就是当主服务器出现故障时当前的会话会自动连接到备用的服务器上。</para>
<para><xref linkend="ha"/>给出了ActiveMQ的HA特性的详细信息。</para>
</section>
<section>
<title>集群</title>
<para>许多消息系统支持由多个消息服务器组成的集群。集群可以使发送和接收的负荷分散到不同的服务器中。
通过增加集群服务器,可以有效的增加整个集群处理消息的能力。</para>
<para>然而不同的消息系统有着不同的集群架构。有的集群架构十分简单,有的集群中成员间的联系很少。</para>
<para>ActiveMQ提供了非常先进的可配置的集群模型。根据每个节点接收者consumer的多少以及是否具有接收状态消息在集群中可以进行智能化负载均衡。</para>
<para>ActiveMQ还能够在集群中的节点间进行消息的再分发以避免在某个节点出现消息匮乏starvation现象。</para>
<para>有关集群的详细内容参见<xref linkend="clusters"/></para>
</section>
<section>
<title>桥接Bridge)和路由Routing</title>
<para>有些消息系统可以将一些分散在不可靠的网络如广域网或internet上孤立的集群或节点桥接在一起。</para>
<para>通常一个桥的作用是从一台服务器的队列上接收消息然后将消息再转发到另一台服务器的队列中。桥连接可以解决不可靠网络连接的问题。
桥有自动重新连接的功能。一旦网络连接中断,桥可以自动进行重试直到重新连接上为止。</para>
<para>ActiveMQ的桥接功能可以配置过滤表达式以实现有条件的转发。另外它还可以实现消息转换的功能transformation</para>
<para>ActiveMQ还允许配置消息在队列之间进行路由。利用它可以完成复杂的路由网络以便在不同队列间进行消息转发与复制形成一个互连的消息代理broker网络。</para>
<para>有关的详细内容将在<xref linkend="core-bridges"/><xref
linkend="diverts"/>给出。</para>
</section>
</chapter>

View File

@ -1,27 +0,0 @@
<!-- ============================================================================= -->
<!-- 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="notice">
<title>法律声明</title>
<para>Red Hat, Inc. 以及其他公司2010年版权所有。</para>
<para>Red Hat公司依照 CC-BY-SA 3.0 Unported(Creative Commons
Attribution-Share Alike)条款之规定授权用戶是用本手册中的文字和插图。</para>
<para>有关 CC-BY-SA 的解释请访问<ulink url="http://creativecommons.org/licenses/by-sa/3.0/">http://creativecommons.org/licenses/by-sa/3.0/</ulink>。根据CCBYSA的规定如果要发布本文档或任何本文档的修改版本都必须给出原始版本文档的URL。</para>
<para>Red Hat 作为本文档的授权方声明在相关法律允许的最大范围内放弃CC-BY-SA第4d节所规定的权利。</para>
</chapter>

View File

@ -1,198 +0,0 @@
<?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="paging">
<title>分页转存</title>
<para>ActiveMQ可以在有限的内存下支持包含百万消息的超大规模的队列。</para>
<para>当有限的内存无法装得下如此多的消息时ActiveMQ将它们<emphasis>分页转存</emphasis>到磁盘中,在内存
有空闲时再将消息分页装载到内存。通过这样的处理,不需要服务器有很大的内存就可以支持大容量的队列。</para>
<para>通过配置可以给一个地址设置一个最大消息值。当这个地址消息数在内存中超过了这个值时ActiveMQ开始将多余的消息
转存到磁盘中。</para>
<para>默认情况下ActiveMQ不转存任何消息。这一功能必须显式地通过配置来激活。</para>
<section>
<title>分页文件</title>
<para>消息按照所属的地址分别保存在不同的文件中。每一个地址有一个单独的文件夹,每个文件夹内消息被保存在
数个文件中(分页文件)。每个文件保存固定数量的消息(由参数<literal>page-size-bytes</literal>
设定)。当从分页文件中读取消息时,一个文件的所有消息被读到内存并被路由。当所有消息处理后,该文件就
被删除。</para>
</section>
<section id="paging.main.config">
<title>配置</title>
<para>你可以配置分页转存文件夹的位置。</para>
<para>在主配置文件<literal>activemq-configuration.xml</literal>)中
可以定义全局的分页转发参数。</para>
<programlisting>&lt;configuration xmlns="urn:activemq"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:activemq /schema/activemq-configuration.xsd">
...
&lt;paging-directory>/somewhere/paging-directory&lt;/paging-directory>
... </programlisting>
<para>
<table frame="topbot">
<title>分页转存的配置参数</title>
<tgroup cols="3">
<colspec colname="c1" colnum="1"/>
<colspec colname="c2" colnum="2"/>
<colspec colname="c3" colnum="3"/>
<thead>
<row>
<entry>参数名</entry>
<entry>说明</entry>
<entry>默认值</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>paging-directory</literal></entry>
<entry>分页转存文件的位置。ActiveMQ在这个位置下为每个地址建立一个文件夹。</entry>
<entry>data/paging</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
<section id="paging.mode">
<title>分页转存模式</title>
<para>一个地址只要消息的数量超过定义的值,它就转到分页转存的模式。</para>
<note>
<para>分页转存是针对每个地址设置的。如果你为一个地址配置了一个max-size-bytes那么每个匹配的地址
都有一个最大值的限制。但是这并不表示所有匹配的地址的大小总和受这个参数的限制。</para>
</note>
<section>
<title>配置</title>
<para>有关分页转存的配置在主要配置文件(<literal>activemq-configuration.xml</literal>
的地址设置的部分内。</para>
<programlisting> &lt;address-settings>
&lt;address-setting match="jms.someaddress">
&lt;max-size-bytes>104857600&lt;/max-size-bytes>
&lt;page-size-bytes>10485760&lt;/page-size-bytes>
&lt;address-full-policy>PAGE&lt;/address-full-policy>
&lt;/address-setting>
&lt;/address-settings>
</programlisting>
<para>下面列出了可用的参数:</para>
<para>
<table frame="topbot">
<title>分页转存参数设置</title>
<tgroup cols="3">
<colspec colname="c1" colnum="1"/>
<colspec colname="c2" colnum="2"/>
<colspec colname="c3" colnum="3"/>
<thead>
<row>
<entry>参数名称</entry>
<entry>说明</entry>
<entry>默认值</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>max-size-bytes</literal></entry>
<entry>地址的最大内存值。当消息占用内存超过此值时,进入分页转存模式。</entry>
<entry>-1 (关闭分页转存功能)</entry>
</row>
<row>
<entry><literal>page-size-bytes</literal></entry>
<entry>每个分页文件的大小。</entry>
<entry>10MiB (10 * 1024 * 1024 字节)</entry>
</row>
<row>
<entry><literal>address-full-policy</literal></entry>
<entry>要使用分页转存这个参数必须设为PAGE。PAGE表示多余的消息会被保存到磁盘。
如果设为DROP那么多余的消息将会被丢弃。如果设为BLOCK当消息占满设定的最大
内存时,在客户端消息的发送者将被阻塞,不能向服务器发送更多的消息。</entry>
<entry>PAGE</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
</section>
<section>
<title>丢弃消息</title>
<para>一个地址除了可以分页转存多余的消息外,还可以配置为丢弃多余消息。</para>
<para>只要将<literal>address-full-policy</literal>设为<literal>DROP</literal>即可。</para>
</section>
<section>
<title>阻塞发送者producer</title>
<para>一个地址除了可以分页转存多余的消息外,还可以通过配置使得消息的发送者在消息达到最大值时阻塞消息
的发送,以防止服务器由于消息过多而耗尽内存。</para>
<para>随着服务器的内存被释放,发送者自动解除阻塞,继续发送消息。</para>
<para>这种方式需要将<literal>address-full-policy</literal>设为<literal>BLOCK</literal></para>
<para>在默认的配置中所有的地址在消息的量达到10MiB后将阻塞发送者。</para>
</section>
<section>
<title>对于有多个队列绑定的地址的配置注意事项</title>
<para>当一个消息被路由到一个绑定了多个队列queue的地址时比如JMS的订阅在内存中仍然只有一分消息的拷贝。每个
队列所持有的不过是它的一个引用。因此,只有所有队列中的消息都成功地传递出去后,这个消息才会从内存中清除。也就是说
只要有一个队列没有传递这个消息,那么就会造成这个消息处于未被传递的状态。 </para>
<para>例如:</para>
<itemizedlist>
<listitem>
<para>一个地址绑定了10个队列queue</para>
</listitem>
<listitem>
<para>其中一个队列没有传递它的消息(也许因为接收者很慢)。</para>
</listitem>
<listitem>
<para>消息不断增加,触发了分页转存模式。</para>
</listitem>
<listitem>
<para>而其它9个队列尽管发送了消息但由于地址将多余的消息转存到磁盘所以它们都是空的。</para>
</listitem>
</itemizedlist>
<para>在这个例子中,必须要等到最后一个队列传递了一些消息后,那些转存的消息被装载回内存,其它队列才有机会得到更多的消息。</para>
</section>
<section>
<title>分页转存与消息的选择器selector</title>
<note><para>请注意消息选择器只对内存的消息进行操作。如果大量的消息被转存在磁盘中,而其中有些消息与选择器是相匹配的,
那么只有内存的消息被传递,这些消息被重新装载入内存后才有可能被传递出去。
ActiveMQ不会扫描在磁盘中的消息来找出与选择器匹配的消息。这样做的话需要实现并管理一种索引机制才能使扫描有效地进行另外
需要其它额外的工作。所有这些如果去完成的话,相当于实现一个关系型数据库!这并不是消息系统的主要任务。如果你要完成的任务是
从海量的消息中选择少数消息,那么你很可能需要使用的是一个关系型数据库,不是消息系统。因为这相当于表的查询。</para></note>
</section>
<section>
<title>分页转存与浏览器</title>
<note><para>请注意浏览器只对内存中的消息进行操作,它不对转存到磁盘中的消息进行操作。
消息是在被路由到任何队列<emphasis>之前</emphasis>进行转存的,所以在转存时刻,它们还没有进入到任何队列中,
自然也就不可能出现在对某个队列浏览的結果中。
</para></note>
</section>
<section>
<title>分页转存与未通知的消息</title>
<note><para>
请注意如果消息没有被通知,它会一直留在服务器的内存中,占用着内存资源。只要消息在被接收者收到并通知后,它才
会在服务器端被清除,空出内存空间以便转存在磁盘上的消息被装载到内存进行传递。如果没有通知,消息不会被清除,
也就不会空出内存空间,转存到磁盘上的消息也就无法装载到内存进行传递。于是在接收端就会呈现出死机的现象。
如果消息的通知是依靠<literal>ack-batch-size</literal>的设定进行的批量通知,那么一定要注意不要将
分页转存的消息临界值设得小于<literal>ack-batch-size</literal>,否则你的系统可能会发生死机现象!
</para></note>
</section>
<section>
<title>例子</title>
<para><xref linkend="examples.paging"/>是一个说明如何使用ActiveMQ的分页转发功能的例子。</para>
</section>
</chapter>

View File

@ -1,233 +0,0 @@
<?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="perf-tuning">
<title>性能调优</title>
<para>本章讲述如何优化ActiveMQ的性能</para>
<section>
<title>持久层的优化</title>
<itemizedlist>
<listitem>
<para>将消息日志放到单独的物理卷上。如果与其它数据共享,例如事务管理、数据库或其它日志等,那么就会
增加读写的负担,磁头会在多个不同文件之间频繁地移动,极大地降低性能。我们的日志系统采用的是只
添加的模式,目的就是最大程度減少磁头的移动。如果磁盘被共享,那么这一目的将不能达到。另外如果
你使用分页转存或大消息功能时,你最好分别将它们放到各自的独立卷中。</para>
</listitem>
<listitem>
<para>尽量减少日志文件的数量。<literal>journal-min-files</literal>参数的设置应以满足平均
运行需要为准。如果你发现系统中经常有新的日志文件被创建,这说明持久的数据量很大,你需要适当增加
这个参数的值以使ActiveMQ更多时候是在重用文件而不是创建新文件。</para>
</listitem>
<listitem>
<para>日志文件的大小。日志文件的大小最好要与磁盘的一个柱面的容量对齐。默认值是10MiB它在绝大多数
的系统中能够满足需要。</para>
</listitem>
<listitem>
<para>使用AIO日志。在Linux下尽量使用AIO型的日志。AIO的可扩展性要好于Java的NIO。</para>
</listitem>
<listitem>
<para>优化 <literal>journal-buffer-timeout</literal>。如果增加它的值,吞吐量会增加,但是
延迟也会增加。</para>
</listitem>
<listitem>
<para>如果使用AIO适当增加<literal>journal-max-io</literal>可能会提高性能。如果使用的是NIO
请不要改变这个参数。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>优化JMS</title>
<para>如果使用JMS接口有以下几个方面可以改进性能。</para>
<itemizedlist>
<listitem>
<para>关闭消息id。如果你不需要这个id<literal>MessageProducer</literal>
<literal>setDisableMessageID()</literal>方法可以关闭它。这可以减少消息的大小并且
省去了创建唯一ID的时间。</para>
</listitem>
<listitem>
<para>关闭消息的时间戳。如果不需要时间戳,用<literal
>MessageProducer</literal><literal
>setDisableMessageTimeStamp()</literal>方法将其关闭。</para>
</listitem>
<listitem>
<para>尽量避免使用<literal>ObjectMessage</literal><literal>ObjectMessage</literal>会带
来额外的开销。<literal>ObjectMessage</literal>使用Java的序列化将它序列化为字节流。在对小的对象
进行序列化会占用大量的空间使传输的数据量加大。另外Java的序列化与其它定制的技术相比要慢。只有在不得
以的情况下才使用它。比如当你在运行时不知道对象的具体类型时可以用ObjectMessage。</para>
</listitem>
<listitem>
<para>避免使用<literal>AUTO_ACKNOWLEDGE</literal><literal>AUTO_ACKNOWLEDGE</literal>
使得每收到一个消息就要向服务器发送一个通知--这样增加的网络传输的负担。如果可能,尽量使用
<literal>DUPS_OK_ACKNOWLEDGE</literal>或者<literal
>CLIENT_ACKNOWLEDGE</literal>。或者使用事务性会话,将通知在提交时批量完成。</para>
</listitem>
<listitem>
<para>避免持久化消息。默认情况下JMS消息是持久的。如果你不需要持久消息则将其设定为非持久。
持久消息都会被写到磁盘中,这给系统带来了明显的负担。</para>
</listitem>
<listitem>
<para>将多个发送或通知放到一个事务中完成。这样ActiveMQ只需要一次网络的往返来发生事务的提交而不是每次发送
或通知就需要一次网络的往返通迅。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>其它优化</title>
<para>在ActiveMQ中还有其它一些地方可以优化</para>
<itemizedlist>
<listitem>
<para>使用异步发送通知。如果你在非事务条件下发送持久的消息并且要保证在send()返回时持久消息已经到达服
务器,不要使用阻塞式发送的方式,应该使用异步发送通知的方式。参见<xref
linkend="send-guarantees"/>中的说明。</para>
</listitem>
<listitem>
<para>使用预先通知模式。预先通知就是在消息发往客户端<literal>之前</literal>进行通知。它节省了正常
的消息通知所占用的通迅时间。详细的解释请参见
<xref linkend="pre-acknowledge"/></para>
</listitem>
<listitem>
<para>关闭安全。将<literal>activemq-configuration.xml</literal>文件中的<literal>security-enabled</literal>
参数设为<literal>false</literal>以关闭安全。这可以带来一些性能的提高。</para>
</listitem>
<listitem>
<para>关闭持久化。如果不你不需要消息持久化,可以将<literal>activemq-configuration.xml</literal>
文件中的<literal>persistence-enabled</literal>参数设为false来完全关闭持久功能。</para>
</listitem>
<listitem>
<para>采用延迟方式事务同步。将<literal>activemq-configuration.xml</literal>文件中的<literal
>journal-sync-transactional</literal>参数设为<literal>false</literal>可以得到
更好的事务持久化的性能。但是这样做可能会造成在发生故障时事务的丢失。有关详细的说明参见
<xref linkend="send-guarantees"/></para>
</listitem>
<listitem>
<para>采用延迟方式非事务同步。将<literal>activemq-configuration.xml</literal>文件中的<literal
>journal-sync-non-transactional</literal>参数设为<literal>false</literal>可以得到
更好的非事务持久化的性能。但是这样做可能会造成在发生故障时持久消息的丢失。有关详细的说明参见
<xref linkend="send-guarantees"/></para>
</listitem>
<listitem>
<para>采用非阻塞方式发送消息。将文件<literal>activemq-jms.xml</literal>中的参数
<literal>block-on-non-durable-send</literal>设为<literal>false</literal>
使用JMS和JNDI时或者直接在上进行相应的设置可以使
消息发送时不阻塞等待服务器的响应。参见 <xref linkend="send-guarantees"/></para>
</listitem>
<listitem>
<para>如果你的接收者速度很快你可以增加consumer-window-size。这样实际上就关闭了流控制的功能。</para>
</listitem>
<listitem>
<para>套接字NIO与旧的IO对比。默认情况下ActiveMQ在服务器端使用套接字NIO技术而在客户端则使用旧的阻塞
IO参见传输配置一章<xref linkend="configuring-transports"/>。NIO比旧的阻塞式IO有更
强的可扩展性但是也会带来一些延时。如果你的服务器要同时有数千个连接使用NIO效果比较好。但是如果
连接数并没有这么多你可以配置接收器使用旧的IO还提高性能。</para>
</listitem>
<listitem>
<para>尽量使用核心接口而不用JMS。使用JMS接口会稍微比使用核心接口性能要低些。这是因为所有JMS操作
实际上要转化为核心的操作才能为服务器所处理。在使用核心接口时,尽量使用带有
<literal>SimpleString</literal>类型参数的方法。<literal>SimpleString</literal>
java.lang.String不同它在写入传输层时不需要拷贝。所以你如果在调用中重用<literal
>SimpleString</literal>对象可以避免不必要的拷贝。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>传输层的优化</title>
<itemizedlist>
<listitem>
<para>TCP缓存大小。如果你的网络速度很快并且你的主机也很快你可以通过增加TCP的发送和接收缓存
来提高性能。参见<xref linkend="configuring-transports"/>中的详细说明。</para>
<note>
<para>注意某些操作系统如最近的Linux版本中包括了TCP自动优化功能。如果再手工设置TCP缓存
会导致自动优化失效,最終使性能下降!
</para>
</note>
</listitem>
<listitem>
<para>增加服务器中文件句柄数量限制。如果你的服务器将要处理很多并行的连接,或者客户端在快速不停地
打开和关闭连接,你要确保在服务器端有足够的文件句柄以供使用。</para>
<para>这个限制在不同平台有不同的方法。在Linux系统中你可以编辑文件<literal
>/etc/security/limits.conf</literal>,增加以下内容:
<programlisting>
serveruser soft nofile 20000
serveruser hard nofile 20000
</programlisting>
它设置了用户<literal>serveruser</literal>可以最多打开20000个文件句柄。</para>
</listitem>
<listitem>
<para>利用参数<literal>batch-delay</literal>并将参数<literal>direct-deliver</literal>
设为false来提高小消息的处理效率。ActiveMQ在其<literal>activemq-configuration.xml</literal>
中预先配置了一个连接器/接受器对(<literal>netty-throughput</literal>),并且在
<literal>activemq-jms.xml</literal>中配置了一个JMS连接工厂
<literal>ThroughputConnectionFactory</literal>)。它们可以用在小消息的处理应用中以提
供最佳呑吐量。参见<xref
linkend="configuring-transports"/>。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>优化虚拟机</title>
<para>我们强烈建议你使用最新的Java 6虚拟机。它在很多方面对以前Java 5的虚拟机进行了改进特别是在网络功能方面。
这是根据我们内部使用Sun的实现测试的結果可能不适用于其它的Java实现例如IBM或JRockit</para>
<itemizedlist>
<listitem>
<para>拉圾回收。为了使服务器的运行比较平滑我们建议使用并行拉圾回收的算法。例如在Sun的JDK使用
JVM选项<literal>-XX:+UseParallelOldGC</literal>.</para>
</listitem>
<listitem id="perf-tuning.memory">
<para>内存设置。尽量为服务器分配更多的内存。ActiveMQ利用其分页转存技术可以在很少的内存下运行
<xref linkend="paging"/>中有说明)。但是如果所有队列都在内存运行,性能将会很好。具体需要
多少内存要由你的队列的大小和数量以及消息的大小和数量决定。使用JVM参数<literal>-Xms</literal>
<literal>-Xmx</literal>来为你的服务器分配内存。我们建议两个参数的设为相同的值。</para>
</listitem>
<listitem>
<para>主动选项Aggressive options。不同JVM有不同的JVM优化参数。对于Sun的Hotspot JVM<ulink
url="http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp"
>这里</ulink>有一个完整的参数列表。我们建议至少要使用 <literal
>-XX:+AggressiveOpts</literal><literal>
-XX:+UseFastAccessorMethods</literal>选项。根据不同的平台,可能还有其它一些参数供你使用,
以提高JVM的性能。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>避免违背设计模式</title>
<itemizedlist>
<listitem>
<para>重用连接/会话/接收者/发送者。最常见的错误恐怕就是每发送/接收一个消息都要创建一个新的连接
/会话/发送者或接收者。这样非常浪费资源。这些对象的创建要占用时间和网络带宽。它们应该进行重用。</para>
<note>
<para>有些常用的框架如Spring JMS Template在使用JMS时违背了设计模式。如果你在使用了它后性能
受到了影响。这不是ActiveMQ的原因Spring的JMS模板只有与能缓存JMS会话的应用服务器一起使用
才是安全的,并且只能是用于发送消息。使用它在应用服务器中同步接收消息是不安全的。</para>
</note>
</listitem>
<listitem>
<para>避免使用繁锁的消息格式。如XML它会使数据量变大进而降低性能。所以应该尽量避免在消息体中使用XML。</para>
</listitem>
<listitem>
<para>不要为每个请求都创建新的临时队列。临时队列通常用于请求-响应模式的消息应用。在这个模式中消息被发往
一个目的它带有一个reply-to的头属性指向一个本地的临时队列的地址。当消息被收到后接收方将响应做为消息发
往那个reply-to指定的临时的地址。如果每发一个消息都创建一个临时队列那么性能将会受很大影响。正确的
作法是在发送消息时重用临时队列。</para>
</listitem>
<listitem>
<para>尽量不要使用MDB。使用MDB消息的接收过程要比直接接收复杂得多要执行很多应用服务器内部的代码。
在设计应用时要问一下是否真的需要MDB可不可以直接使用消息接收者完成同样的任务</para>
</listitem>
</itemizedlist>
</section>
</chapter>

View File

@ -1,263 +0,0 @@
<?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="persistence">
<title>持久化</title>
<para>本章我们将描述ActiveMQ的持久化技术包括持久化的工作原理和配置方法。</para>
<para>ActiveMQ拥有一个高性能的日志journal模块来处理持久化。因此它并不依赖一个外部的数据库或第三方持久化产品。这个
日志模块针对消息的处理进行了高度的优化。</para>
<para>所谓ActiveMQ日志是一个<emphasis>只添加</emphasis>系统。它由一组磁盘文件构成。每个文件都是预先创建好的并且
大小是固定的。文件在创建时都进行了格式化。随着ActiveMQ不断地处理消息如消息的增加、更新、删除等一个个记录被添加
到日志中。当一个日志文件写满时,新记录就会写到下一个文件。</para>
<para>由于对日志的写入只是对文件的添加,这样有效减少了随机寻道的操作。而随机寻道的操作是磁盘操作中最耗时的操作。
所以这种设计可以使磁头的运动降到最低,效率最高。</para>
<para>而文件的大小是可以配置的。这使我们可以将文件大小配置为刚好占满一个磁盘柱面。不过现代的磁盘技术是复杂多样的,
我们并不能控制文件与磁盘柱面的对应关系。尽管如此,我们通过最大限度地降低文件对磁盘柱面的占用,来降低磁头的运动。
这是因为在同一个柱面的存取只需要盘面的转动而不需要磁头的运动。</para>
<para>当被删除的记录越来越多时有的文件最終会变成一个没有有效记录的文件。这样的文件就可以回收再利用。ActiveMQ有
一套复杂的文件回收算法来判断一个日志文件是否可以被回收。</para>
<para>ActiveMQ还有一套文件整理的算法它用来将日志文件中不用的空隙移除以达到更高的存贮效率。</para>
<para>这个日志系统全面支持事务功能。根据需要它可以支持本地事务或XA型事务。</para>
<para>日志系统的大部分是用Java实现的但是ActiveMQ在其中实现了一层抽象的文件系统这样就使得其它的语言实现能
方便地“插入”到日志模块中。实际上ActiveMQ自带有两种实现</para>
<itemizedlist>
<listitem>
<para>Java <ulink url="http://en.wikipedia.org/wiki/New_I/O">NIO</ulink></para>
<para>第一种采用的是标准的Java NIO接口来进行文件的操作。它可以在任何安装有Java 1.6或以上的系统中运行。
NIO的性能是很高的。</para>
</listitem>
<listitem id="aio-journal">
<para>Linux 异步IO Asynchronous IO</para>
<para>第二种是采用的Linux系统中的异步IO技术AIO。它包括了少量的平台相关的代码native code)来
          调用AIO的接口。当数据被保存到磁盘上后AIO会回调ActiveMQ进行通知。这样ActiveMQ就避免了磁盘写
的同步操作。</para>
<para>使用AIO通常可以有比NIO更高的性能。</para>
<para>采用AIO的日志只能在运行 Linux kernel 2.6 或以上版本的内核的系统中才有。另外你需要安装libaio。
有关如何安装libaio请参见 <xref linkend="installing-aio"/></para>
<para>另外请注意AIO只在以下文件系统上能正确工作ext2, ext3, ext4, jfs, xfs。其他文件系统如NFS虽然
AIO看上去可以工作实际上是以较慢的同步的方式在运行。所以不要在NFS上使用日志。</para>
<para>有关libaio的更多介绍参见 <xref linkend="libaio"/></para>
<para>libaio是Linux内核项目的一部分。</para>
</listitem>
</itemizedlist>
<para>标准的ActiveMQ核心服务器使用了两种日志</para>
<itemizedlist id="persistence.journallist">
<listitem>
<para>绑定日志</para>
<para>这个日志用来保存与绑定有关的数据。其中包括在ActiveMQ上部署的队列及其属性还有ID序列计数器。 </para>
<para>绑定日志是一个NIO型日志。与消息日志相比它的呑吐量是比较低的。</para>
<para>这种日志文件的名字采用<literal>activemq-bindings</literal>作为前缀。每个文件都有
<literal>bindings</literal>这样的扩展。文件大小是<literal
>1048576</literal>它的位置在bindings文件夹下。</para>
</listitem>
<listitem>
<para>JMS日志</para>
<para>这个日志保存所有JMS相关的数据包括JMS队列话题及连接工厂以及它们的JNDI绑定信息。</para>
<para>通过管理接口创建的JMS资源将被保存在这个日志中。但是通过配置文件配置的资源则不保存。只有使用JMS时JMS的日志
才被创建。</para>
<para>这种日志文件的名字采用<literal>activemq-jms</literal>作为前缀。每个文件都有
<literal>jms</literal>这样的扩展。文件大小是<literal
>1048576</literal>它的位置在bindings文件夹下。</para>
</listitem>
<listitem>
<para>消息日志</para>
<para>这个日志用来存贮所有消息相关的数据包括消息本身和重复ID缓存。</para>
<para>默认情况下ActiveMQ总是优先使用AIO型日志。如果AIO型日志不可用比如在非Linux平台上运行或系统内核版本不同
它将自动使用NIO型日志。</para>
<para>这种日志文件的名字采用<literal>activemq-data</literal>。作为前缀。每个文件都有
 <literal>hq</literal>作为扩展名。默认的文件大小是 <literal
>10485760</literal> (可配置)。文件保存在journal文件夹下。</para>
</listitem>
</itemizedlist>
<para>对于超大消息Hornet将它们保存在消息日志之外的地方。详见<xref linkend="large-messages"/>.</para>
<para>ActiveMQ还可以在内存不够用时将消息暂存到磁盘上。相关的配置和说明参见<xref linkend="paging"/></para>
<para>如果不需要持久功能ActiveMQ还可以配置成非持久的消息系统。参见<xref linkend="persistence.enabled"/></para>
<section id="configuring.bindings.journal">
<title>配置绑定日志</title>
<para>绑定日志的配置参数在 <literal
>activemq-configuration.xml</literal>文件中。</para>
<itemizedlist>
<listitem>
<para><literal>bindings-directory</literal></para>
<para>这是绑定日志的位置。默认值是<literal>data/bindings</literal></para>
</listitem>
<listitem>
<para><literal>create-bindings-dir</literal></para>
<para>如果设置为<literal>true</literal>,那么在 <literal
>bindings-directory</literal> 所设定的位置不存在的情况下会自动创建它。默认值是<literal>true</literal></para>
</listitem>
</itemizedlist>
</section>
<section id="configuring.bindings.jms">
<title>配置JMS日志</title>
<para>JMS日志的配置与绑定日志共用配置。</para>
</section>
<section id="configuring.message.journal">
<title>配置消息日志</title>
<para>消息日志的配置在<literal
>activemq-configuration.xml文件中。</literal></para>
<itemizedlist>
<listitem id="configuring.message.journal.journal-directory">
<para><literal>journal-directory</literal></para>
<para>这是消息日志文件所在的目录。默认值是
<literal>data/journal</literal></para>
<para>为以达到最佳性能,我们建议将日志设定到属于它自己的物理卷中以减少磁头运动。如果日志的位置与
其它进程共用(如数据库,绑定日志或事务的日志等)则磁头的运动显然要增加很多。性能也就没有保证了。</para>
<para>如果消息日志是贮存在SAN中我们建议每个日志都拥有自己的LUN逻辑单元</para>
</listitem>
<listitem id="configuring.message.journal.create-journal-dir">
<para><literal>create-journal-dir</literal></para>
<para>如果设为<literal>true</literal>,则当<literal
>journal-directory</literal>所指定的日志目录不存在时,会自动创建它。默认值是<literal>true</literal></para>
</listitem>
<listitem id="configuring.message.journal.journal-type">
<para><literal>journal-type</literal></para>
<para>有效值是<literal>NIO</literal> 或者 <literal>ASYNCIO</literal></para>
<para>Choosing <literal>NIO</literal> chooses the Java NIO journal. Choosing
<literal>AIO</literal> 选择作用异步IO型日志。如果你的平台不是Linux或者你没有安装
libaioActiveMQ会自动检测到并使用<literal>NIO</literal></para>
</listitem>
<listitem id="configuring.message.journal.journal-sync-transactional">
<para><literal>journal-sync-transactional</literal></para>
<para>如果设为trueActiveMQ会保证在事务的边界操作时commit, prepare和rollback将事务数据
写到磁盘上。默认的值是 <literal>true</literal></para>
</listitem>
<listitem id="configuring.message.journal.journal-sync-non-transactional">
<para><literal>journal-sync-non-transactional</literal></para>
<para>如果设为true ActiveMQ将保证每次都将非事务性消息数据发送和通知保存到磁盘上。默认值是 <literal>true</literal></para>
</listitem>
<listitem id="configuring.message.journal.journal-file-size">
<para><literal>journal-file-size</literal></para>
<para>每个日志文件的大于。单位为字节。默认值是 <literal
>10485760</literal> bytes (10MiB)。</para>
</listitem>
<listitem id="configuring.message.journal.journal-min-files">
<para><literal>journal-min-files</literal></para>
<para>最少日志文件数。当ActiveMQ启动时会创建这一数量的文件。</para>
<para>创建并初始化日志文件是一项费时的操作,通常不希望这些操作在服务运行时执行。预先创建并初始化这些
日志文件将会使ActiveMQ在工作时避免浪费不必要的时间。</para>
<para>根据你的应用中队列中消息量的实际要求可以适当调节这一参数。</para>
</listitem>
<listitem id="configuring.message.journal.journal-max-io">
<para><literal>journal-max-io</literal></para>
<para>写请求被放到一个队列中,然后再被发送到系统中执行。这个参数限制了在任一时间队列中可以存放的最大数量
的写请求。如果队列达到这个限制,任何新的写请求都将被阻塞,直到队列中有空位为止。</para>
<para>当使用NIO时这个参数必须为 <literal
>1</literal></para>
<para>当使用AIO时它的默认值是<literal>500</literal></para>
<para>系统根据不同类型的日志提供不同的默认值。(NIO 为 1, AIO 为 500)。</para>
<para>如果是AIO这个参数的上限不能超过操作系统的限制(/proc/sys/fs/aio-max-nr)这个值通常为65536.</para>
</listitem>
<listitem id="configuring.message.journal.journal-buffer-timeout">
<para><literal>journal-buffer-timeout</literal></para>
<para>日志模块中有一个内部缓冲。每次写的内容并不是都立即写到磁盘上,而是先放到这个内部缓存中。当这个缓存已满时,或
者超过了一定的时间timeout才将缓存的数据存到硬盘上。NIO和AIO都有这一特点。采用缓存的方式可以很好地满足
大量并发写数据的需要。</para>
<para>这一参数规定了缓存的失效时间如果过了这个时间即使缓存还没有满也将数据写入磁盘中。AIO的写入
能力通常要比NIO强。因此系统对于不同类型的日志有着不同的默认值。 NIO的默认值是 3333333 纳秒即每秒300次。
而AIO则是500000纳秒即每秒2000次。</para>
<note>
<para>加在这个参数有可能会增加系统的呑吐量,但可能会降低系统的响应能力。通常情况下默认值应该是比较理想的折中选择。</para>
</note>
</listitem>
<listitem id="configuring.message.journal.journal-buffer-size">
<para><literal>journal-buffer-size</literal></para>
<para>AIO的定时缓冲的大小默认值为<literal
>490KiB</literal></para>
</listitem>
<listitem id="configuring.message.journal.journal-compact-min-files">
<para><literal>journal-compact-min-files</literal></para>
<para>进行整理压缩日志操作的最少文件数。当日志文件少于这个数时,系统不会进行文件的整理压缩。</para>
<para>默认值是 <literal>10</literal></para>
</listitem>
<listitem id="configuring.message.journal.journal-compact-percentage">
<para><literal>journal-compact-percentage</literal></para>
<para>开始整理压缩的界限值。当有效数据的比例少于这个值时系统开始整理压缩日志。注意是否进行压缩还要
受到、<literal>journal-compact-min-files</literal>参数的控制。</para>
<para>这一参数的默认值是 <literal>30</literal></para>
</listitem>
</itemizedlist>
</section>
<section id="disk-write-cache">
<title>关于关闭磁盘写缓冲的重要说明</title>
<warning>
<para>大多数磁盘产品都有硬件的写缓冲。写缓冲可以明显提高写的效率。</para>
<para>这样的写缓冲与调用fsync()这样的系统函数无关也与在Java程序中进行的同步调用无关</para>
<para>默认情况下许多磁盘的写缓冲是打开的。这样的情况下,即使你在程序中调用了同步操作也不能保证你的数据
就真正写到磁盘介质中了。因此如果故障发生时,关键的数据是有可能丢失的。</para>
<para>有些昂贵的磁盘采用非挥发性的介质或有电源的缓冲来保证故障情况下不丢失数据。但是你仍需要对这些硬盘进行测试!</para>
<para>如果你的磁盘没有非挥发性或有电源的缓存也不是某种冗余盘阵如RAID。要想保证关键数据不丢失你需要
关闭磁盘的写缓冲。</para>
<para>需要知道的是关闭磁盘的写缓冲会显著降低磁盘的性能。如果平时你在使用磁盘时都打开写缓冲,那么当你为了
保护你的数据而关闭它时,你可能感到两种情况下的明显差异。</para>
<para>Linux可以用<literal>hdparm</literal> (IDE硬盘) 或 <literal>sdparm</literal>
<literal>sginfo</literal> (SDSI/SATA 硬盘)工具来查看并修改磁盘的写缓冲。</para>
<para>在Windows平台上你可以右键点击硬盘图标并选择“属性”菜单项来操作。</para>
</warning>
</section>
<section id="installing-aio">
<title>安装AIO</title>
<para>Java NIO日志的性能是很好的。但是如果你是在Linux 内核2.6版本以上的系统中运行ActiveMQ我们强烈建议
你使用 <literal>AIO</literal>日志,以获得更佳的性能。</para>
<para>在早期的Linux版本中或其它操作系统中不可以使用 AIO日志。</para>
<para>如果你的Linux内核是2.6版本或以上但没有安装 <literal
>libaio</literal>,按照下列步骤可以很容易地安装它:</para>
<para>使用 yum(如 Fedora 或 Red Hat Enterprise Linux):
<programlisting>yum install libaio</programlisting></para>
<para>使用 aptitude, (如 Ubuntu 或 Debian):
<programlisting>apt-get install libaio</programlisting></para>
</section>
<section id="persistence.enabled">
<title>配置ActiveMQ不使用持久化</title>
<para>在一些情况下消息系统并不需要持久化。这时可以配置ActiveMQ不使用持久层。只要将<literal
>activemq-configuration.xml</literal>文件中的<literal>persistence-enabled</literal>
参数设为<literal>false</literal>即可。 </para>
<para>注意如果你将该参数设为 false来关闭持久化就意味着所有的绑定数据、消息数据、超大消息数据、重复ID缓冲以及转移paging数据都将不会被持久。</para>
</section>
<section id="persistence.importexport">
<title>导入入/导出日志数据</title>
<para>有时你需要使用导入导出工具来查看日志文件的记录。这个导入导出工具类在activemq-core.jar文件中。
    使用以下命令可以将日志文件导出为文本文件:</para>
<para><literal>java -cp activemq-core.jar org.apache.activemq.core.journal.impl.ExportJournal
&lt;JournalDirectory> &lt;JournalPrefix> &lt;FileExtension> &lt;FileSize>
&lt;FileOutput></literal></para>
<para>要将日志文件导入使用下面的命令注意你需要netty.jar</para>
<para><literal>java -cp activemq-core.jar:netty.jar org.apache.activemq.core.journal.impl.ImportJournal
&lt;JournalDirectory> &lt;JournalPrefix> &lt;FileExtension> &lt;FileSize>
&lt;FileInput></literal></para>
<itemizedlist>
<listitem>
<para>JournalDirectory文件的位置如./activemq/data/journal</para>
</listitem>
<listitem>
<para>JournalPrefix 日志文件的前缀。<link linkend="persistence.journallist">这里</link>有关于前缀的详细描述。</para>
</listitem>
<listitem>
<para>FileExtension 文件的扩展名。详细讨论参见<link linkend="persistence.journallist">这里</link>
</para>
</listitem>
<listitem>
<para>FileSize日志文件的大小。详细讨论参见<link linkend="persistence.journallist">这里</link></para>
</listitem>
<listitem>
<para>FileOutput输出的文本文件名。</para>
</listitem>
</itemizedlist>
</section>
</chapter>

View File

@ -1,72 +0,0 @@
<?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="pre-acknowledge">
<title>预先通知模式pre-acknowledge</title>
<para>JMS 规定了三种消息通知方式</para>
<itemizedlist>
<listitem>
<para><literal>AUTO_ACKNOWLEDGE</literal></para>
</listitem>
<listitem>
<para><literal>CLIENT_ACKNOWLEDGE</literal></para>
</listitem>
<listitem>
<para><literal>DUPS_OK_ACKNOWLEDGE</literal></para>
</listitem>
</itemizedlist>
<para>还有一种情况JMS不支持应用程序在出现故障时可以容忍消息丢失这样可以在消息在传递给客户
<emphasis>之前</emphasis>就通知服务器。</para>
<para>ActiveMQ支持这种模式称为<emphasis>pre-acknowledge</emphasis></para>
<para>这种模式的缺点是消息在通知后,如果系统出现故障时,消息可能丢失。并且在系统重启后该消息
不能恢复。</para>
<para>使用<literal>pre-acknowledgement</literal>模式可以节省网络传输和CPU处理资源。</para>
<para>股票价格更新是一个适用于此模式的例子。如果因为服务器故障丢失了一些消息,等服务器重启后新的
股票更新消息很快到达,以前丢失的过时的股票消息即使丢失也无关紧要。 </para>
<note>
<para>注意如果你使用pre-acknowledge模式在接收消息端不能支持事务。因为这个模式不是在提交时
通知消息,是在消息在传递之前就通知了。</para>
</note>
<section id="pre-acknowledge.configure">
<title>使用PRE_ACKNOWLEDGE</title>
<para>这个模式在<literal>activemq-jms.xml</literal>文件中
<literal>connection factory</literal>下配置:</para>
<programlisting>&lt;connection-factory name="ConnectionFactory">
&lt;connectors>
&lt;connector-ref connector-name="netty-connector"/>
&lt;/connectors>
&lt;entries>
&lt;entry name="ConnectionFactory"/>
&lt;/entries>
&lt;pre-acknowledge>true&lt;/pre-acknowledge>
&lt;/connection-factory></programlisting>
<para>另一个选择是使用JMS接口来设置pre-acknowledgement模式。只需要在创建JMS会话session
时使用<literal>ActiveMQSession.PRE_ACKNOWLEDGE</literal>常数即可。</para>
<programlisting>
// messages will be acknowledge on the server *before* being delivered to the client
Session session = connection.createSession(false, ActiveMQSession.PRE_ACKNOWLEDGE);
</programlisting>
<para>你还可以直接在<literal>ActiveMQConnectionFactory</literal>实例上设置该模式。</para>
<para>另外,如果使用核心接口,则在<literal>ClientSessionFactory</literal>实例上直接
设置该模式。</para>
</section>
<section>
<title>例子</title>
<para>参见<xref linkend="examples.pre-acknowledge"/>。这是一个使用JMS的例子。</para>
</section>
</chapter>

View File

@ -1,68 +0,0 @@
<?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="preface">
<title>前言</title>
<para>什么是ActiveMQ</para>
<itemizedlist>
<listitem>
<para>ActiveMQ 是一个开源的软件项目。它的目标是一个多协议、可嵌入、高性能、可集群的异步消息系统。</para>
</listitem>
<listitem>
<para>ActiveMQ 是一个消息中间件MoM。有关MoM和其它消息相关的概念解释请参见 <xref linkend="messaging-concepts"
/>。</para>
</listitem>
<listitem>
<para>要了解有关ActiveMQ的更多信息请访问 <ulink url="http://www.jboss.org/community/wiki/ActiveMQGeneralFAQs"></ulink></para>
</listitem>
</itemizedlist>
<para>为什么要使用ActiveMQ 以下给出了几个理由:</para>
<itemizedlist>
<listitem>
<para>ActiveMQ是100%的开源软件。 ActiveMQ 采用 Apache v 2.0开源协议,对用户的限制最小。</para>
</listitem>
<listitem>
<para>ActiveMQ的设计强调可用性。</para>
</listitem>
<listitem>
<para>采用Java语言编写。可以在任何Java 6+ 的平台上运行。这几乎包括了从Windows到IBM mainframes的每个平台。</para>
</listitem>
<listitem>
<para>性能出众。不但对非持久化消息的处理性能达到了非常高的性能。独特高效的日志journal使持久消息处理接近非持久消息的性能。</para>
</listitem>
<listitem>
<para>功能全面。不仅拥有其它成熟消息产品所具有的全部功能,而且还有很多独特的功能。</para>
</listitem>
<listitem>
<para>ActiveMQ的设计遵从了简约的原则。对第三方软件的依赖极少。根据不同的需要
ActiveMQ可以单独运行也可以运行于JEE应用服务器中。它还可以嵌入到你自己的应用程序中。</para>
</listitem>
<listitem>
<para>完美的可获得性。ActiveMQ提供自动客户端失效备援automatic client failover功能能保证在服务器故障时没有消息丢失或消息重复。</para>
</listitem>
<listitem>
<para>超级灵活的集群方案。可以控制集群进行消息负载均衡的方式。分布在不同地理位置的各个集群间可以通过非可靠的网络连接形成一个全球网络。
还可以非常灵活地配置消息路由。</para>
</listitem>
<listitem>
<para>请访问 <ulink
url="http://www.jboss.org/community/wiki/ActiveMQFeatures">wiki
</ulink>来全面了解ActiveMQ的所有功能介绍。</para>
</listitem>
</itemizedlist>
</chapter>

View File

@ -1,89 +0,0 @@
<?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="project-info">
<title>项目信息</title>
<para>ActiveMQ的官方网址是 <ulink url="http://activemq.org/"
>http://activemq.org/</ulink>.</para>
<section id="download.software">
<title>软件下载</title>
<para>ActiveMQ的下载地址为<ulink
url="http://activemq.org/downloads.html">http://activemq.org/downloads.html</ulink></para>
</section>
<section id="download.git">
<title>其它相关信息</title>
<para>
<itemizedlist>
<listitem>
<para>ActiveMQ的 <ulink
url="http://www.jboss.org/community/wiki/ActiveMQ">wiki</ulink></para>
</listitem>
<listitem>
<para>如果在使用ActiveMQ中发生任何问题可以去我们的 <ulink
url="http://www.jboss.org/index.html?module=bb&amp;op=viewforum&amp;f=312">用户论坛
</ulink></para>
</listitem>
<listitem>
<para>如果你有开发方面的问题与想法,请访问我们的 <ulink
url="http://www.jboss.org/index.html?module=bb&amp;op=viewforum&amp;f=313"
>开发论坛</ulink></para>
</listitem>
<listitem>
<para>请加入我们的<ulink url="irc://irc.freenode.net:6667/activemq"
>IRC频道</ulink>与我们的核心开发工程师交流。</para>
</listitem>
<listitem>
<para>我们项目有自己的 <ulink url="http://activemq.blogspot.com/">博客</ulink></para>
</listitem>
<listitem>
<para>还可以跟踪我们的<ulink url="http://twitter.com/activemq">twitter</ulink></para>
</listitem>
<listitem>
<para>ActiveMQ的Git代码库地址 <ulink
url="https://github.com/activemq/activemq"
>https://github.com/activemq/activemq</ulink></para>
</listitem>
<listitem>
<para>每次发布的版本标签都在<ulink
url="https://github.com/activemq/activemq/tags"
>https://github.com/activemq/activemq/tags</ulink>下能找到。</para>
</listitem>
</itemizedlist>
</para>
<para>Red Hat 公司聘请全职工程师进行ActiveMQ项目的开发工作他们是 <itemizedlist>
<listitem>
<para>
<ulink url="http://jbossfox.blogspot.com">Tim Fox</ulink> (项目主管)</para>
</listitem>
<listitem>
<para>Howard Gao</para>
</listitem>
<listitem>
<para><ulink url="http://jmesnil.net/weblog/">Jeff Mesnil</ulink></para>
</listitem>
<listitem>
<para>Clebert Suconic</para>
</listitem>
<listitem>
<para>Andy Taylor</para>
</listitem>
</itemizedlist></para>
<para> 另外我们还有长期的和刚加入的一些贡献者,非常感謝他们的帮助。参见<ulink
url="http://jboss.org.apache.activemq/community/team.html">完整的贡献者名单</ulink></para>
</section>
</chapter>

View File

@ -1,120 +0,0 @@
<?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="queue-attributes">
<title>队列属性</title>
<para>有两种方法可以设置队列的属性。一种使用配置文件另一种使用核心接口core API
本章讲述这些属性的配置以及这些属性的作用。</para>
<section id="predefined.queues">
<title>预定义的队列</title>
<para>通过配置可以定义队列。队列的定义可以在核心层定义也可以在JMS层来定义。首先我们看一下JMS层。</para>
<para>下面就是一个在<literal>activemq-jms.xml</literal>中定义的一个队列的例子:</para>
<programlisting>&lt;queue name="selectorQueue">
&lt;entry name="/queue/selectorQueue"/>
&lt;selector string="color='red'"/>
&lt;durable>true&lt;/durable>
&lt;/queue></programlisting>
<para>这个队列的name属性定义了队列的名字。例子中我们采用了一种命名的惯例因些对应的核心队列的名字是
<literal>jms.queue.selectorQueue</literal></para>
<para>在entry单元内定义的名字用来将队列绑定于JNDI。这是必不可少的。一个队列可以有多个entry定义每个
定义中的名字都绑定到同一个队列。</para>
<para>selector单元定义的是队列的选择器。定义了选择器后只有与选择器相匹配的消息才能被加到队列中。
这是一个可选项。如果没有定义选择器,队列将默认没有选择器。</para>
<para>durable定义了队列是否是一个可持久的队列。这也是一个可选项默认值是true。</para>
<para>如果在核心层定义队列,则使用<literal>activemq-configuration.xml</literal>文件。
下面是一个例子:</para>
<programlisting>&lt;queues>
&lt;queue name="jms.queue.selectorQueue">
&lt;address>jms.queue.selectorQueue&lt;/address>
&lt;filter string="color='red'"/>
&lt;durable>true&lt;/durable>
&lt;/queue>
&lt;/queues></programlisting>
<para>它的配置与JMS的配置很相似但有三个不同之处</para>
<orderedlist>
<listitem>
<para>队列的name属性是队列的真正名字不是JMS中的名字。</para>
</listitem>
<listitem>
<para>address一项定义了消息路由的地址。</para>
</listitem>
<listitem>
<para>没有entry单元。</para>
</listitem>
<listitem>
<para>filter的定义使用<emphasis>核心过滤器语法</emphasis> (在
<xref linkend="filter-expressions"/>中描述不是JMS的选择器语法。</para>
</listitem>
</orderedlist>
</section>
<section>
<title>使用接口API创建队列</title>
<para>队列还可以使用核心接口或管理接口来创建。</para>
<para>核心接口的<literal>org.apache.activemq.api.core.client.ClientSession</literal>接口可以用来
创建队列。它有几个<literal>createQueue</literal>方法,可以在创建队列时对上述的属性进行设置。
除此之外,还有一个额外的属性<literal>temporary</literal>可以设置。如果将其设为true
那么队列在会话断开时将被删除。</para>
<para><xref linkend="management"/>中讲述了如何用管理接口来创建队列。</para>
</section>
<section id="queue-attributes.address-settings">
<title>通过地址设置来配置队列属性</title>
<para>有些属性的定义中地址可以使用通配符。下面是<literal>activemq-configuration.xml</literal>
文件中的一个<literal>address-setting</literal>的配置例子。</para>
<programlisting>&lt;address-settings>
&lt;address-setting match="jms.queue.exampleQueue">
&lt;dead-letter-address>jms.queue.deadLetterQueue&lt;/dead-letter-address>
&lt;max-delivery-attempts>3&lt;/max-delivery-attempts>
&lt;redelivery-delay>5000&lt;/redelivery-delay>
&lt;expiry-address>jms.queue.expiryQueue&lt;/expiry-address>
&lt;last-value-queue>true&lt;/last-value-queue>
&lt;max-size-bytes>100000&lt;/max-size-bytes>
&lt;page-size-bytes>20000&lt;/page-size-bytes>
&lt;redistribution-delay>0&lt;/redistribution-delay>
&lt;send-to-dla-on-no-route>true&lt;/send-to-dla-on-no-route>
&lt;address-full-policy>PAGE&lt;/address-full-policy>
&lt;/address-setting>
&lt;/address-settings></programlisting>
<para>通过上述的地址设定可以将多个属性应用于所有与<literal>match</literal>属性相匹配的地址。
上面例子中所定义的属性应用于<literal>jms.queue.exampleQueue</literal>的地址。如果使用
通配符,就可以将这些属性应用于一组匹配的地址。通配符的详细说明在<link linkend="wildcard-syntax">这里</link></para>
<para>例如在<literal>match</literal>中定义字符串<literal>jms.queue.#</literal>,那么
定义的属性就会应用于所有以<literal>jms.queue.</literal>开头的地址即所有的JMS队列。</para>
<para>这些属性在本手册的各个地方有相应的介绍。在此处给出了简单的解释各它所在章的连接。</para>
<para><literal>max-delivery-attempts</literal>定义了最大重传递的次数。一个消息如果反复传递超过
了这个值将会被发往死信地址<literal>dead-letter-address</literal>。相关的完整的解释在
<link linkend="undelivered-messages.configuring">这里</link></para>
<para><literal>redelivery-delay</literal>定义了重新传递的延迟。它控制ActiveMQ在重新
传递一个被取消的消息时要等待的时间。参见<link linkend="undelivered-messages.delay"
>这里</link></para>
<para><literal>expiry-address</literal>定义了过期消息的发送地址。参见<link linkend="message-expiry.configuring">这里</link></para>
<para><literal>last-value-queue</literal> 定义一个队列是否使用最新值。参见<link linkend="last-value-queues">这里</link></para>
<para><literal>max-size-bytes</literal><literal>page-size-bytes</literal>用来设置地址的分页转存功能。
它们在<link linkend="paging">这里</link>有详细的解释。</para>
<para><literal>redistribution-delay</literal>定义了当最后一个接收者关闭时重新分配队列消息前所等待的时间。
参见<link linkend="clusters.message-redistribution">这里</link></para>
<para><literal>send-to-dla-on-no-route</literal>。当一个消息被送到某个地址时,可能不会被路由到任何一个队列。
例如该地址没有绑定任何队列的情况,或者它所有的队列的选择器与该消息不匹配时。这样的消息通常情况下会被丢弃。这时
如果将这个参数设为true则如果这个地址配置了死信地址的话这样的消息就会被发送到该地址的死信地址DLA</para>
<para><literal>address-full-policy</literal>。这个属性有三个可能的值PAGE、 DROP 或 BLOCK。它决定了
如果地址的消息所占用的内存达到了<literal>max-size-bytes</literal>所定义的值时,如何处理后继到来的消息。
默认值是PAGE就是将后续的消息分页转存到磁盘上。DROP则表示丢弃后续的消息。BLOCK表示阻塞消息的发送方发送后续
的消息。参见<xref linkend="flow-control"/><xref linkend="paging"/>
</para>
</section>
</chapter>

View File

@ -1,47 +0,0 @@
<?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="scheduled-messages">
<title>定期消息</title>
<para>与普通消息不同,定期消息是在未来某个指定时间发送的消息。</para>
<para>为了创建定期消息,需要设定一个特殊的参数.</para>
<section>
<title>定期传递参数</title>
<para>用来标识一个定期消息的参数是<literal
>"_HQ_SCHED_DELIVERY"</literal> (相当于常量<literal
>Message.HDR_SCHEDULED_DELIVERY_TIME</literal>)。</para>
<para>这个参数的值必须是一个大于零的长整型单位是毫秒。下面例子给出了使用JMS接口创建定期消息的方法</para>
<programlisting>
TextMessage message =
session.createTextMessage("This is a scheduled message message which will be delivered
in 5 sec.");
message.setLongProperty("_HQ_SCHED_DELIVERY", System.currentTimeMillis() + 5000);
producer.send(message);
...
// message will not be received immediately but 5 seconds later
TextMessage messageReceived = (TextMessage) consumer.receive();
</programlisting>
<para>也可以使用核心接口来发送定期消息。它只需要将同样的参数设定到核心消息上即可。</para>
</section>
<section>
<title>例子</title>
<para>参见<xref linkend="examples.scheduled-message"/>它是一个JMS使用定期消息的例子。</para>
</section>
</chapter>

View File

@ -1,235 +0,0 @@
<?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="security">
<title>安全</title>
<para>本章讲述ActiveMQ的安全机制以及如何配置它。要完全关闭安全只要将<literal>activemq-configuration.xml</literal>
文件中的<literal>security-enabled</literal>参数设为false即可。</para>
<para>出于性能的考虑安全在ActiveMQ中被缓存一定的时间。要改变这个时间需要设置参数
<literal>security-invalidation-interval</literal>,单位是毫秒。默认值是
<literal>10000</literal>毫秒。</para>
<section id="security.settings.roles">
<title>基于角色的地址安全</title>
<para>ActiveMQ采用了基于角色的安全模型来配置地址的安全以及其队列的安全。</para>
<para>正如在<xref linkend="using-core"/>解释的那样ActiveMQ核心主要由绑定到地址上的队列组成。
消息被发送到地址后,服务器查找与之绑定的队列,并将消息路由到这些队列中。</para>
<para>ActiveMQ可以基于地址来给队列定义权限。在定义权限时可以使用通配符'<literal>#</literal>'和
'<literal>*</literal>'。</para>
<para>队列的权限有7种它们是</para>
<itemizedlist>
<listitem>
<para><literal>createDurableQueue</literal>。允许用户在相应的地址上创建持久的队列。</para>
</listitem>
<listitem>
<para><literal>deleteDurableQueue</literal>。允许用户在相应的地址上删除相应的持久的队列。</para>
</listitem>
<listitem>
<para><literal>createNonDurableQueue</literal>。允许用户在相应地址上创建非持久的队列。</para>
</listitem>
<listitem>
<para><literal>deleteNonDurableQueue</literal>。允许用户在相应地址上删除非持久队列。</para>
</listitem>
<listitem>
<para><literal>send</literal>。允许用户向相应地址发送消息。</para>
</listitem>
<listitem>
<para><literal>consume</literal>。允许用户从相应地址上的队列接收消息。</para>
</listitem>
<listitem>
<para><literal>manage</literal>。允许用户调用管理操作,即向管理地址发关管理消息。</para>
</listitem>
</itemizedlist>
<para>每个权限有一个角色表。如果用户的角色在这个表中,那么它将拥有这个权限。</para>
<para>让我们看个简单的例子。下面是从<literal>activemq-configuration.xml</literal>文件或
<literal>activemq-queues.xml</literal>文件中提取的安全设置:</para>
<programlisting>
&lt;security-setting match="globalqueues.europe.#"&gt;
&lt;permission type="createDurableQueue" roles="admin"/&gt;
&lt;permission type="deleteDurableQueue" roles="admin"/&gt;
&lt;permission type="createNonDurableQueue" roles="admin, guest, europe-users"/&gt;
&lt;permission type="deleteNonDurableQueue" roles="admin, guest, europe-users"/&gt;
&lt;permission type="send" roles="admin, europe-users"/&gt;
&lt;permission type="consume" roles="admin, europe-users"/&gt;
&lt;/security-setting&gt;
</programlisting>
<para>在配置中字符'<literal>#</literal>'代表"任何单词序列“。单词由'<literal>.</literal>'字符分隔。
有关通配符的语法的完整说明请参见<xref linkend="wildcard-syntax"/>。上面的安全配置对以
"globalqueues.europe."开始的地址有效:</para>
<para>只有具有<literal>admin</literal>角色的用户才可以创建和删除绑定到以"globalqueues.europe."开始的地址的持久化队列。</para>
<para>具有<literal>admin</literal><literal>guest</literal><literal>europe-users</literal>
角色的用户可以在以开头的地址上创建临时的队列。</para>
<para>任何具有<literal>admin</literal><literal>europe-users</literal>角色的用户可以向以"globalqueues.europe."开头的地址
发送消息,并从绑定到相同地址上的队列接收消息。</para>
<para>安全管理器处理一个用户和它的角色的对应关系。ActiveMQ本身自带一个用户管理器能从文件中读取用户的身份信息。
另外ActiveMQ还可以使用JAAS或JBoss应用服务器的安全管理机制。</para>
<para>有关安全管理器的配置信息,请参见<xref linkend="change-security-manager"/></para>
<para>在每个xml文件中可以有零个或多个 <literal>security-setting</literal>。当一组地址有多个这样的设置时,
ActiveMQ总是选取<emphasis>更具体的</emphasis>匹配。</para>
<para>让我们来看一个实例,下面是另一个<literal>security-setting</literal></para>
<programlisting>
&lt;security-setting match="globalqueues.europe.orders.#"&gt;
&lt;permission type="send" roles="europe-users"/&gt;
&lt;permission type="consume" roles="europe-users"/&gt;
&lt;/security-setting&gt;
</programlisting>
<para>在这个<literal>security-setting</literal>块中,字符串
'globalqueues.europe.orders.#' 要比它之前的字符串'globalqueues.europe.#'更具体。
因此当一个地址与'globalqueues.europe.orders.#'匹配时,它<emphasis></emphasis>选择这个安全配置。</para>
<para>注意安全设置没有继承性。对于像'globalqueues.europe.orders.plastics'的地址,只要上面的设置
能被采用。即角色europe-users有<literal
>send</literal><literal>consume</literal>权限。权限
<literal>createDurableQueue</literal><literal
>deleteDurableQueue</literal><literal>createNonDurableQueue</literal><literal
>deleteNonDurableQueue</literal>不会从先前的设置中继承。</para>
<para>由于权限的不可继承如果我们不在更具体的security-setting设置中给出一个权限这个权限就是没有的不会因为继承而带来
麻烦。否则就不可能对一组地址中的部分地址进行如此的设置。</para>
</section>
<section>
<title>安全套接字层(SSL)传输</title>
<para>当消息客户端与服务器端或服务器之间比如使用桥的情况通过一个不信任的网络相互通信时ActiveMQ
支持使用加密的安全套接字SSL传输数据。</para>
<para>关于SSL的详细配置信息请参见<xref linkend="configuring-transports"/></para>
</section>
<section>
<title>基本用户身份信息Credentials</title>
<para>ActiveMQ自带一个安全管理器security manager可以从xml文件中读取用户身份信息即用户名、
密码、角色信息。该xml文件名为<literal>activemq-users.xml</literal>它必须要在classpath中。</para>
<para>如果你要使用这个安全管理器,就将用户名,密码,角色等信息加入到这个文件中。</para>
<para>让我们看一个例子:</para>
<programlisting>
&lt;configuration xmlns="urn:activemq"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:activemq ../schemas/activemq-users.xsd "&gt;
&lt;defaultuser name="guest" password="guest"&gt;
&lt;role name="guest"/&gt;
&lt;/defaultuser&gt;
&lt;user name="tim" password="marmite"&gt;
&lt;role name="admin"/&gt;
&lt;/user&gt;
&lt;user name="andy" password="doner_kebab"&gt;
&lt;role name="admin"/&gt;
&lt;role name="guest"/&gt;
&lt;/user&gt;
&lt;user name="jeff" password="camembert"&gt;
&lt;role name="europe-users"/&gt;
&lt;role name="guest"/&gt;
&lt;/user&gt;
&lt;/configuration&gt;
</programlisting>
<para>首先要注意的是<literal>defaultuser</literal>,它定义的是默认的用户。当客户端创建会话时
没有提供用户名/密码时,就会使用这个用户。根据上述配置,这个默认用户是<literal>guest</literal>
并且他的角色是<literal>guest</literal>。一个默认用户可以有多个角色。</para>
<para>另外三个用户中,用户<literal>tim</literal>具有角色<literal
>admin</literal>。用户<literal>andy</literal>具有角色<literal
>admin</literal><literal>guest</literal>,用户<literal>jeff</literal>
具有角色<literal>europe-users</literal><literal>guest</literal></para>
</section>
<section id="change-security-manager">
<title>更换安全管理器</title>
<para>如果你不想用默认的安全管理器,可以通过修改配置文件<literal>activemq-beans.xml</literal>
或者在运行JBoss应用服务器情况下<literal
>activemq-jboss-beans.xml</literal>文件)来更换。同时要更换
<literal>ActiveMQSecurityManager</literal> bean 的类。</para>
<para>让我们看一段默认bean文件的内容</para>
<programlisting>
&lt;bean name="ActiveMQSecurityManager"
class="org.apache.activemq.spi.core.security.ActiveMQSecurityManagerImpl"&gt;
&lt;start ignored="true"/&gt;
&lt;stop ignored="true"/&gt;
&lt;/bean&gt;
</programlisting>
<para><literal>org.apache.activemq.spi.core.security.ActiveMQSecurityManagerImpl</literal>
类就是ActiveMQ服务器的在独立运行时的默认的安全管理器。</para>
<para>ActiveMQ自带有另外两个安全管理器可供使用。一个是JAAS安全管理器另一个是用来与JBoss应用服务
器集成的安全管理器。此外,你还可以编写实现你自己的安全管理器。首先要实现
<literal>org.apache.activemq.core.security.SecurityManager</literal>接口,再将你的实现
类定义到<literal>activemq-beans.xml</literal>文件中即可或者在JBoss应用服务器中
使用<literal>activemq-jboss-beans.xml</literal>文件)。</para>
<para>以下分别介绍这两咱安全管理器</para>
</section>
<section>
<title>JAAS安全管理器</title>
<para>JAAS表示“Java认证与授权服务“。它是Java平台标准的一部分。它提供了进行安全认证与授权的通用接口。
它允许你插入自己的安全管理模块。</para>
<para>要配置使用你自己的JAAS安全实现需要在bean文件中定义<literal>JAASSecurityManager</literal>
下面是一个例子:</para>
<programlisting><![CDATA[
&lt;bean name="ActiveMQSecurityManager"
class="org.apache.activemq.integration.jboss.security.JAASSecurityManager"&gt;
&lt;start ignored="true"/&gt;
&lt;stop ignored="true"/&gt;
&lt;property name="ConfigurationName"&gt;org.apache.activemq.jms.example.ExampleLoginModule&lt;/property&gt;
&lt;property name="Configuration"&gt;
&lt;inject bean="ExampleConfiguration"/&gt;
&lt;/property&gt;
&lt;property name="CallbackHandler"&gt;
&lt;inject bean="ExampleCallbackHandler"/&gt;
&lt;/property&gt;
&lt;/bean&gt;
]]></programlisting>
<para>注意你需要为JAAS安全管理器提供三个参数</para>
<itemizedlist>
<listitem>
<para>ConfigurationName: <literal>LoginModule</literal>的名字。</para>
</listitem>
<listitem>
<para>Configuration: <literal>Configuration</literal>的实现。</para>
</listitem>
<listitem>
<para>CallbackHandler: <literal>CallbackHandler</literal>实现,用于用户交互。</para>
</listitem>
</itemizedlist>
<section>
<title>例子</title>
<para>参见<xref linkend="examples.jaas"/>。这个例子展示了怎样在ActiveMQ中配置使用JAAS。</para>
</section>
</section>
<section>
<title>JBoss 应用服务器安全管理器</title>
<para>JBoss 应用服务器安全管理器适用于当ActiveMQ运行于JBoss应用服务器内时。它可以与JBoss应用服务器
的安全模型紧密集成。</para>
<para>此安全管理器的类是 <literal
>org.apache.activemq.integration.jboss.security.JBossASSecurityManager</literal></para>
<para>要了解如何配置JBoss安全管理器可以看一眼ActiveMQ发布包中相关例子中的
<literal>activemq-jboss-beans.xml</literal>文件。</para>
<section>
<title>配置客户端登录</title>
<para>JBoss可以配置使用客户登录。JEE的模块如servlet或EJB可以将安全认证信息设置到安全上下文security context
用于整个调用过程。如果想在ActiveMQ在发送和接收消息时使用这些认证credential信息需要将参数
<literal>allowClientLogin</literal>设为true。它会越过ActiveMQ的身份验证过程并会传播安全上下文security
context。如果你想要ActiveMQ使用传播的安全信息进行身份验证需要同时将参数<literal>authoriseOnClientLogin</literal>
设为true。</para>
<para>关于客户端登录的详细信息请访问<ulink
url="http://community.jboss.org/wiki/ClientLoginModule">这里</ulink></para>
<note><para>如果消息是以非阻塞方式发送的,那么有可能在消息到达服务器时,调用线程已经结束,安全上下文也被清除了。
所以如果使用安全上下文,需要采用阻塞方式发送消息。</para></note>
</section>
</section>
<section>
<title>集群用户名/密码的配置</title>
<para>为了使集群连接正常工作,每个节点都必须与其它节点相连接。它们连接所使用的默认用户名和密码在正式使用时
一定要做相应的更改,以防止安全隐患。</para>
<para>请参见<xref linkend="management"/>了解怎样去做。</para>
</section>
</chapter>

View File

@ -1,102 +0,0 @@
<?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="send-guarantees">
<title>发送与提交的保证</title>
<section>
<title>事务保证</title>
<para>在提交或回滚事务时ActiveMQ将提交或回滚的请求发送到服务器客户端阻塞等待服务器的响应。</para>
<para>当服务器端收到提交或回滚的请求时它将事务信息记录到日志journal中。然后向客户端发回
响应。参数<literal>journal-sync-transactional</literal>控制着如何向客户端发回响应。
如果它的值是<literal>false</literal>,服务器向客户端发回响应时事务的处理結果不一定已经被
保存到磁盘中。可能会在之后的某个时间保存。如果期间服务器发生故障那么事务的处理信息可能丢失。
当它的值是<literal>true</literal>时,服务器将保证在向客户端发回响应时,事务的处理信息
已经被保存到了磁盘中。默认值是<literal>true</literal></para>
<para>显然将这个参数设为<literal>false</literal>可以提高性能,但是要以牺牲事务的持久性为代价。</para>
<para>这个参数在 <literal>activemq-configuration.xml</literal>文件中。</para>
</section>
<section id="non-transactional-sends">
<title>非事务性消息发送的保证</title>
<para>使用非事务性会话发送消息时经过适当配置ActiveMQ客户端在发送后以阻塞的方式等待直到确认发出
的消息已经到达服务器后再返回。可以对持久化或非持久化的消息分别配置,具体参数如下:</para>
<itemizedlist>
<listitem>
<para><literal>BlockOnDurableSend</literal>。如果设为<literal>true</literal>则通过
非事务性会话发送持久消息时,每次发送都将阻塞直到消息到达服务器并返回通知为止。默认值是
<literal>true</literal>
</para>
</listitem>
<listitem>
<para><literal>BlockOnNonDurableSend</literal>。如果设为<literal>true</literal>
则通过非事务性会话发送非持久消息时,每次发送都将阻塞直到消息到达服务器并返回通知为止。默认值是
<literal>false</literal></para>
</listitem>
</itemizedlist>
<para>将发送设置为阻塞方式会降低程序的效率。因为每次发送都需要一次网络往返的过程,然后才可以进行下次发送。
这样发送消息的速度将受网络往返时间RTT的限制。这样你的网络带宽就可能没有被充分利用。为了提高效率我们
建议采用事务来批量发送消息。因为在事务中只有在提交或回滚时阻塞。另外你还可以利用ActiveMQ高级的
<emphasis>异步发送通知功能</emphasis>。这一功能在<xref linkend="asynchronous-send-acknowledgements"/>
进行了描述。</para>
<para>使用JMS时如果JMS的连接工厂是在服务器端被注册到JNDI服务你需要配置
<literal>activemq-jms.xml</literal>文件中的<literal>block-on-durable-send</literal>
<literal>block-on-non-durable-send</literal>。如果不使用JNDI可以调用
<literal>ActiveMQConnectionFactory</literal>相应的设置方法进行配置。</para>
<para>如果你使用的是内核服务,你可以直接在<literal
>ClientSessionFactory</literal>上用相关的方法设置相应的参数。</para>
<para>当服务器从一个非事务性的会话收到一个消息时,如果这个消息是持久的并且此消息被路由到至少一个持久的队列中,
则该消息会被持久化到永久存贮介质中。如果日志journal的参数<literal
>journal-sync-non-transactional</literal>设为<literal>true</literal>,服务器在向客户
发送响应时,它能保证消息已经被持久化到磁盘中。默认值是<literal>true</literal></para>
</section>
<section id="send-guarantees.nontrans.acks">
<title>非事务性通知的保证</title>
<para>当客户端使用非事务性会话向服务器通知消息收到时可以配置ActiveMQ使得客户端的通知阻塞直到服务器收到
了通知并返回为止。其相应的配置参数是<literal>BlockOnAcknowledge</literal>。如果该参数设为
<literal>true</literal>则所有的通过非事务会话的消息通知都是阻塞式的。如果你想要的消息传递策略是
<emphasis>最多一次</emphasis>的话,那么你需要将此参数设为。默认值是<literal>false</literal></para>
</section>
<section id="asynchronous-send-acknowledgements">
<title>异步发送通知</title>
<para>如果你使用的是非事务会话来发送消息并且希望保证每个发送出去的消息都到达服务器的话你可以将ActiveMQ配置
成阻塞的方式,如<xref linkend="non-transactional-sends"/>讨论的那样。这样做的一个缺点是性能的降低。
因为这样每发送一个消息就需要一次网络的往返通信。如果网络时延越长,消息发送的效率就越低。同时网络的带宽对消息
的发送没有影响。</para>
<para>我们来做一个简单的计算。假设有一个1Gib的网络客户端与服务器间往返时间为0.25ms。</para>
<para>这样,在阻塞方式的情况下,客户端<emphasis>最大</emphasis>的消息发送速度为 1000/ 0.25 =
4000 消息每秒。</para>
<para>如果每个消息的大小&lt; 1500字节而且网络的最大传输单元MTU是1500字节。那么理论上1GiB的网络
最大的传输速率是 (1024 * 1024 * 1024 / 8) / 1500 = 89478 消息每秒!尽管这不是一个精确的工程计算但
你可以看出阻塞式的发送对性能的影响会有多大。</para>
<para>为了解决这个问题ActiveMQ提供了一种新的功能称为<emphasis>异步发送通知</emphasis>
它允许消息以非阻塞的方式发送,同时从另一个连接流中异步地接收服务器的通知。这样就使得消息的发送与通知分开来,
避免了阻塞方式带来的缺点。在保证消息可行发送到服务器的同时提高了呑吐量。</para>
<para>参数用来定义消息发送通知的窗口大小。它属于连接工厂或客户会话工厂。参见<xref linkend="client-reconnection"/>
以获取更多的相关信息。</para>
<section>
<title>异步发送通知</title>
<para>如果使用核心API你需要实现<literal
>org.apache.activemq.api.core.client.SendAcknowledgementHandler</literal>接口并将一个实例设置到
<literal>ClientSession</literal>中。</para>
<para>然后使用这个<literal>ClientSession</literal>发送消息。当消息到达服务器后,服务器向客户端异步地发送通知,
并在客户端调用你的SendAcknowledgementHandler实例的<literal
>sendAcknowledged(ClientMessage message)</literal>方法。其中传入的参数就是发送的消息的引用。</para>
<para>为了使异步发送通知正常工作你必须确保<literal>confirmation-window-size</literal>的值为一个正整数,例如 10MiB</para>
<para>相关的例子请参见 <xref linkend="asynchronous-send-acknowledgements-example"/></para>
</section>
</section>
</chapter>

View File

@ -1,108 +0,0 @@
<?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="thread-pooling">
<title>线程管理</title>
<para>本章讲述ActiveMQ如何使用线程池以及如何管理线程。</para>
<para>首先我们讨论在服务器端线程是如何被管理的,然后我们再讨论客户端的情况。</para>
<section>
<title>服务器端线程的管理</title>
<para>每个ActiveMQ服务器都有一个线程池作为一般线程使用另外还有一个可计划线程池。Java的可计划线程池不能作为
标准的线程池使用,因此我们采用了两个单独的线程池。</para>
<para>当使用旧的阻塞IO时使用了一个单独的线程池来处理连接。但是旧的IO要求一个线程配一个连接所以如果你
的应用有很多并发的连接,这个线程池会很快用光所有的线程,造成服务器出现“挂起”现象。因此,对于大量并发连接
的应用一定要使用NIO。</para>
<para>如果使用NIO默认情况下ActiveMQ会使用系统中处理器内核或超线程数量三倍的线程来处理接收的数据包。
内核的数量是通过调用<literal>Runtime.getRuntime().availableProcessors()</literal>来得到
的。如果你想改变这个数量,可以设置传输层配置参数<literal>nio-remoting-threads</literal>
参见<xref linkend="configuring-transports"/></para>
<para>另外在其它一些地方直接使用了线程,没有用线程池。我们将对这些线程作出解释。</para>
<section id="server.scheduled.thread.pool">
<title>服务器端可计划线程池</title>
<para>服务器可计划线程池可以定期地或延迟地执行所交给的任务它用来完成ActiveMQ中绝大部分这样的任务。
它内部使用的是一个 <literal
>java.util.concurrent.ScheduledThreadPoolExecutor</literal>实例。</para>
<para>最大线程数可以在<literal
>activemq-configuration.xml</literal>文件中进行配置,参数名是<literal
>scheduled-thread-pool-max-size</literal>。默认值是<literal>5</literal>
通常这个线程池不需要很大数量的线程。</para>
</section>
<section>
<title>服务器通用线程池</title>
<para>服务器端绝大部分的异步操作都是由这个线程池来完成的。在它的内部使用了一个<literal
>java.util.concurrent.ThreadPoolExecutor</literal>的实例。</para>
<para>这个线程池的最大线程数在<literal>activemq-configuration.xml</literal>文件中配置,相应的参数名为<literal
>thread-pool-max-size</literal></para>
<para>如果将参数设为<literal>-1</literal>则表示该线程池没有线程限制。也就是说当线程不够用时,线程池就
会创建新的线程。当任务不多时,空闲的线程将会超时并被关闭。</para>
<para>如果这个参数的值是一个大于零的整数<literal>n</literal>,则该线程池的线程数是有限的。当所有线程都
处于忙的状态并且线程数已经达到n时任何新的请求都将被阻塞直到有线程空闲为止。在设置线程上限时我们建议
要非常谨慎。因为如何线程数量过低会造成死锁情况的发生。</para>
<para><literal>thread-pool-max-size</literal>的默认值是<literal
>30</literal></para>
<para>参见<ulink
url="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ThreadPoolExecutor.html"
>J2SE javadoc</ulink>有关无边界(缓存)和有边界(固定)线程池的解释。</para>
</section>
<section>
<title>过期回收线程</title>
<para>ActiveMQ使用一个单独的线程来扫描队列中过期的消息。由于这个线程需要自己的优先级配置所以不能使用上述的
任何一个线程池。</para>
<para>关于回收线程的配置请参阅<xref linkend="message-expiry"/></para>
</section>
<section>
<title>异步IO</title>
<para>ActiveMQ使用一个线程池来进行异步IO的操作包括事件的接收和发送。这些线程的名字都是以
ActiveMQ-AIO-poller-pool为开头。每个打开的日志文件都对应有一个线程为其服务通常只有
一个)。</para>
<para>还有一个单独的线程用于向libaio发送写请求。这样做是为了避免上下文转换带来的性能下降。该
线程的名字以ActiveMQ-AIO-writer-pool开头。</para>
</section>
</section>
<section id="thread-pooling.client.side">
<title>客户端线程管理</title>
<para>在客户端ActiveMQ有一个静态的可计划线程池和一个静态的通用线程池它们在一个JVM中由同一个classloader装载的所有客户端
共同使用。</para>
<para>静态的可计划的线程池的最大线程数为 <literal>5</literal>,通用线程池则没有线程数限制。</para>
<para>如果需要还可以配置一个<literal
>ClientSessionFactory</literal>实例以使它拥有自己的可计划与通用线程池。通过这个工厂创建的会话都
将使用这些线程池。</para>
<para>要想配置<literal>ClientSessionFactory</literal>使用自己的线程池,只要调用它相应的方法取出可,如:</para>
<programlisting>ClientSessionFactory myFactory = ActiveMQClient.createClientSessionFactory(...);
myFactory.setUseGlobalPools(false);
myFactory.setScheduledThreadPoolMaxSize(10);
myFactory.setThreadPoolMaxSize(-1); </programlisting>
<para>如果使用JMS你可以先用同样的参数设置ClientSessionFactory然后再用这样工厂创建<literal
>ConnectionFactory</literal>的实例。如:</para>
<programlisting>ConnectionFactory myConnectionFactory = ActiveMQJMSClient.createConnectionFactory(myFactory); </programlisting>
<para>如果你使用JNDI来创建<literal>ActiveMQConnectionFactory</literal>
实例,你还可以在<literal>activemq-jms.xml</literal>文件中进行配置。如:</para>
<programlisting>&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;entry name="XAConnectionFactory"/&gt;
&lt;/entries&gt;
&lt;use-global-pools&gt;false&lt;/use-global-pools&gt;
&lt;scheduled-thread-pool-max-size&gt;10&lt;/scheduled-thread-pool-max-size&gt;
&lt;thread-pool-max-size&gt;-1&lt;/thread-pool-max-size&gt;
&lt;/connection-factory&gt;</programlisting>
</section>
</chapter>

View File

@ -1,29 +0,0 @@
<?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="transaction-config">
<title>配置资源管理器Resource Manager</title>
<para>ActiveMQ有自己的资源管理器来管理JTA事务。当一个事务开始时资源管理器就得到通知并记录下该事务和它的状态。
有的时候一个事务开始后,最終被忘记。有时客户端崩溃并且再也不能恢复,这样的话该事务就一直存在下去。</para>
<para>为了解决这个问题可以配置ActiveMQ来扫描过期的事务并且将它们回滚。默认值是3000000毫秒5分钟
它表示任何超过5分钟的事务都将被删除。这个超时对应的参数是<literal
>transaction-timeout</literal>,它在配置文件<literal>activemq-configuration.xml</literal>中(单位毫秒)。
参数<literal>transaction-timeout-scan-period</literal>定义了ActiveMQ扫描过期事务的间隔。</para>
<para>注意ActiveMQ不会单方面回滚一个已经处于准备状态的XA事务。如果你认为这些事务永远不会被事务管理器transaction manager
来处理的话,你必须通过管理接口来进行回滚。</para>
</chapter>

View File

@ -1,118 +0,0 @@
<?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="undelivered-messages">
<title>消息再传递及未传递的消息</title>
<para>消息有可能传递失败(比如相关的事务发生回滚)。失败的消息将退回到队列中准备重新传递。这样就会出现
一种情况,就是同一个消息会被反复的传递而总不成功,以至于使系统处于忙的状态。</para>
<para>对于这样的消息我们有两种处理方法:</para>
<itemizedlist>
<listitem>
<para>延迟再传递</para>
<para>这种方法是让消息再次传递时有一定的时间延迟这样客户端就有机会从故障中恢复同时网络连接和CPU资源
也不致于被过度占用。</para>
</listitem>
<listitem>
<para>死信Dead Letter地址</para>
<para>这种方法是规定一个死信地址,如果消息再被反复传递达到一定次数时,就会从原有队列中删除,转到这个
死信地址中。这样消息就不会永远地重复传递了。</para>
</listitem>
</itemizedlist>
<para>以上两种方法可以合理搭配使用,使解决方案更加灵活。</para>
<section>
<title>延迟再传递</title>
<para>延迟再传递对于时常出现故障或回滚的客户端十分有用。如果没有延迟,整个系统可能会处于一种”疯狂“的状态。
就是消息被传递、回滚、再传递这样反复不间断地进行着将宝贵的CPU和网络资源占用。</para>
<section id="undelivered-messages.delay">
<title>延迟再传递的配置</title>
<para>延迟再传递的配置在地址设定内address-setting</para>
<programlisting>
&lt;!-- delay redelivery of messages for 5s --&gt;
&lt;address-setting match="jms.queue.exampleQueue"&gt;
&lt;redelivery-delay&gt;5000&lt;/redelivery-delay&gt;
&lt;/address-setting&gt;
</programlisting>
<para>如果定义了<literal>redelivery-delay</literal>ActiveMQ在再传递之前等待所定义的时间。</para>
<para>默认是没有延时的(即<literal>redelivery-delay</literal>的值是0</para>
<para>可以使用通配符为一组地址定义再传递的延迟(参见<xref linkend="wildcard-syntax"/>)。
</para>
</section>
<section>
<title>例子</title>
<para>参见 <xref linkend="examples.delayed-redelivery"/>。这是一个JMS应用中配置延迟再传递的例子。</para>
</section>
</section>
<section>
<title>死信地址</title>
<para>通过定义一个<emphasis role="italic">死信地址</emphasis>也可以防止同一个消息被无休止地传递:
当一个消息被重复传递一定次数后,就会从队列中删除并传递到定义好的死信地址中。</para>
<para>这些死信中的消息之后可以转发到某个队列中,以供系统管理员分析处理。</para>
<para>每个ActiveMQ的地址可以有一个死信地址。当一个消息被反复传递达一定次数时它就会被从队列中删除并送到
死信地址。这些<emphasis>死信</emphasis>消息可以被接收进行分析处理。</para>
<section id="undelivered-messages.configuring">
<title>配置死信地址</title>
<para>死信地址定义在地址设定中address-setting</para>
<programlisting>
&lt;!-- undelivered messages in exampleQueue will be sent to the dead letter address
deadLetterQueue after 3 unsuccessful delivery attempts
--&gt;
&lt;address-setting match="jms.queue.exampleQueue"&gt;
&lt;dead-letter-address&gt;jms.queue.deadLetterQueue&lt;/dead-letter-address&gt;
&lt;max-delivery-attempts&gt;3&lt;/max-delivery-attempts&gt;
&lt;/address-setting&gt;
</programlisting>
<para>如果没有定义<literal>dead-letter-address</literal>,消息在经过
<literal>max-delivery-attempts</literal>次重复传递后被删除。</para>
<para>默认的重复传递次数为10。将<literal>max-delivery-attempts</literal>设定为-1
表示无限次重复传递。</para>
<para>例如,对一组地址设置了一个通用的死信地址后,再将其中某个地址的<literal>max-delivery-attempts</literal>
设定为-1时那么只有这个地址的再传递次数是无限的。</para>
<para>可以使用通配符对一组地址设定死信地址(参见<xref linkend="wildcard-syntax"/>)。</para>
</section>
<section>
<title>死信的属性</title>
<para>从死信地址接收到的消息有以下属性:</para>
<itemizedlist>
<listitem>
<para><literal>_HQ_ORIG_ADDRESS</literal></para>
<para>这是一个字符串属性,它是该死信消息的<emphasis>原始地址</emphasis></para>
</listitem>
</itemizedlist>
</section>
<section>
<title>例子</title>
<para>参见<xref linkend="examples.dead-letter"/>。这个例子给出了在JMS应用中死信的配置与使用。</para>
</section>
</section>
<section id="configuring.delivery.count.persistence">
<title>传递计数的持久化</title>
<para>通常情况下ActiveMQ在一个消息被回滚之前并不更新持久的传递计数即在消息传递到接收者之前不会更新传递计数
大多数情况下消息被接收、通知、然后被忘掉。这样对<emphasis>每一个消息</emphasis>的传递都要更新一次持久的
传递计数,会显著降低系统的性能。</para>
<para>介是如果在消息传递之前不进行持久传递计数的更新,服务器一旦发生故障而崩溃,就会造成消息可能被传递出去而传递
计数却没有正确反映出传递的結果。在恢复阶段,服务器将错误地将该消息的<literal>redelivered</literal>设为
<literal>false</literal>而不是<literal>true</literal></para>
<para>这样是不符合严格的JMS要求的。因此ActiveMQ允许在消息传递前更新传递计数。但是默认不这样做目的是优先考虑
了它对性能的影响。</para>
<para>要想打开传递计数更新功能,将<literal>activemq-configuration.xml</literal>文件中的
<literal>persist-delivery-count-before-delivery</literal>设为<literal>true</literal>即可:</para>
<programlisting>
&lt;persist-delivery-count-before-delivery&gt;true&lt;/persist-delivery-count-before-delivery&gt;
</programlisting>
</section>
</chapter>

View File

@ -1,163 +0,0 @@
<?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-core">
<title>使用ActiveMQ内核</title>
<para>ActiveMQ内核是一个与JMS无关的消息系统它有一套自己的API。我们称它为<emphasis>内核API</emphasis>.</para>
<para>你可以直接使用内核API。使用内核API可以完成JMS同样的功能只是比起JMS API使用更加简单方便。另外内核API
还提供了JMS不具有的额外的功能。</para>
<section>
<title>内核消息系统的相关概念</title>
<para>内核消息系统中有许多概念是与JMS相似的但有些方面是不同的。总的来说内核消息系统的接口相对简单。这是因为
在内核中没有队列queue、话题topic和订阅subscription的概念。下面我们就内核消息中的概念作逐一介绍。
但是每个API的详细说明还是要参见相应的javadoc。</para>
<section>
<title>消息</title>
<itemizedlist>
<listitem>
<para>一个消息就是客户端与服务器传递信息的单位数据。</para>
</listitem>
<listitem>
<para>一个消息有一个消息体body即一个缓存用以写入数据或从中读取数据。</para>
</listitem>
<listitem>
<para>一个消息有一个属性集,这个属性集实际上包含的是主键-值的集合。每个属性的主键是一个字符串,值可
以是一个整数integer、长整数long、短整数short、字节byte、字节数组byte[])、
字符串String双精度值double、浮点数float或是布尔值boolean</para>
</listitem>
<listitem>
<para>每个消息都有一个<emphasis>地址address</emphasis>做为它的目的地。当一个消息被发到
服务器上时它会被路由到与该地址绑定的所有队列中queue。如果queue配置了过滤器filter
那么只有与过滤器相匹配的消息才会被路由到该queue。一个地址可以绑定多个queue也可以一个也不
绑定。注意这里所说的queue是内核的概念不是JMS的queue。除了queue之外还有其它一些实体可以
绑定到某一地址上。比如<emphasis role="italic">divert转发器</emphasis></para>
</listitem>
<listitem>
<para>消息可以是持久的durable或非持久的non-durable。持久的消息不会因为服务器故障或重启而丢失。
非持久消息则会因为服务器的故障或重启而丢失。</para>
</listitem>
<listitem>
<para>消息具有优先级。优先级的值为从0到9的整数。0代表最低优先级9代表最高优先级。ActiveMQ总
会尝试先传送优先级高的消息。</para>
</listitem>
<listitem>
<para>消息还有一个可选的失效时间。如果一个消息过了失效时间ActiveMQ将不再传送它。</para>
</listitem>
<listitem>
<para>消息还有一个可选的时间戳timestamp。这个时间戳表示的是消息被发送的时间。</para>
</listitem>
<listitem>
<para>ActiveMQ还支持大消息的发送。它可以处理大到内存装不下的超大消息。</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>地址Address</title>
<para>ActiveMQ服务器保存有地址和queue的映射集。一个地址对应零个或多个queue。每个queue还可以拥有消息
过滤器filter。当一个消息在服务器内进行路由时它将会被送往与其地址相绑定的所有的queue中。但是
如果其中某个queue有过滤器那么只有与其过滤器相匹配的消息才会被发到这个queue中。</para>
<para>其它的实体如<emphasis role="italic">diverts</emphasis>也可以与一地址进行绑定,消息也会被同样
路由到相应的实体中。</para>
<note>
<para>在内核中没有Topic的概念。只有<emphasis>地址address</emphasis>
<emphasis>queue</emphasis></para>
<para>假如想在内核实现JMS topic的功能只要将一地址绑定到多个queue即可。其中的每一个queue就相当
于一个订阅subscription。类似地一个JMS queue则可以通过一个地址与一个queue的绑定来实现。</para>
</note>
</section>
<section>
<title>Queue</title>
<para>Queue可以的持久的。意思是如果queue中的消息是持久的那么当发生服务器故障或重启时这些消息不会丢失。
Queue也可是非持久的这意谓着如果服务器发的故障或重启queue中的消息将会丢失不管消息是不是持久的。</para>
<para>Queue也可以是临时的意思是临时的queue在客户端断开连接时它将会被删除。</para>
<para>Queue可以有一个过滤器表达式。服务器在向这样的queue路由消息时先判定消息是否与过滤器表达式相匹配
只有匹配的消息才会被发到该queue。</para>
<para>一个地址可以绑定多个queue、。但是一个queue只能被绑定到一个地址上。</para>
</section>
<section>
<title>ClientSessionFactory</title>
<para>客户端使用 <literal>ClientSessionFactory</literal> 类的实例创建 <literal
>ClientSession</literal> 实例。 <literal>ClientSessionFactory</literal>
知道如何连接到服务器并创建会话session。它是可以根据不同需要灵活配置的。</para>
<para><literal>ClientSessionFactory</literal>实例是通过 <literal
>ActiveMQClient</literal> 工厂类创建的。</para>
</section>
<section>
<title>ClientSession</title>
<para>客户端使用ClientSession来发送和接收消息并控制事务的使用。ClientSession可以支持事务性
和非事务性的应用。它还提供了一个 <literal
>XAResource</literal> 接口,因些它可以加入到一个<ulink url="http://java.sun.com/javaee/technologies/jta/index.jsp">JTA</ulink>
交易中。</para>
<para>ClientSession 管理着ClientConsumers和ClientProducers。</para>
<para>ClientSession 实例可以注册一个可选的 <literal
>SendAcknowledgementHandler</literal>。每当消息被送达ActiveMQ服务器中时
ActiveMQ就用它来异步地发出通知。有了这个独特的功能客户可以不必阻塞在每次消息的发送操作上来保证
消息安全到达服务器。如果采用阻塞的方法,那么每一个消息的发送都要包括往返两次的网络传递操作,开销
是很大的。有了这个异步方式就可以避免这种开销建立真正的异步的端到端间的系统。这是标准的JMS接口
无法做到的。参见 <xref linkend="send-guarantees"/>了解相关的更详细的信息。</para>
</section>
<section>
<title>ClientConsumer</title>
<para>客户端使用 <literal>ClientConsumer</literal> 实例来接收来自queue的消息。ActiveMQ的内核同时支持
同步与异步的消息接收。<literal>ClientConsumer</literal> 实例可以配置有可选的过滤器。它只接收与过滤
器相匹配的消息。</para>
</section>
<section>
<title>ClientProducer</title>
<para>客户端使用<literal>ClientSession</literal>创建 <literal>ClientProducer</literal> 实例
来向服务器发送消息。ClientProducer可以指定一个地址用来向其发送消息。或者不指定地址消息在发送时
再指定地址。</para>
</section>
<warning>
<para>请注意 ClientSession、 ClientProducer和ClientConsumer 实例是可以被
<emphasis>重用</emphasis>的。</para>
<para>在每次发送或接收一个消息时都创建新的 ClientSession, ClientProducer 和 ClientConsumer是不符合
设计模式的要求的。这样做会导致性能低下。在<xref linkend="perf-tuning"/>中我们会做进一步的讨论。</para>
</warning>
</section>
<section>
<title>一个内核的应用实例</title>
<para>下面是一个非常简单的使用内核API来发送的接收消息的实例</para>
<programlisting>
ClientSessionFactory factory = ActiveMQClient.createClientSessionFactory(
new TransportConfiguration(
InVMConnectorFactory.class.getName()));
ClientSession session = factory.createSession();
session.createQueue("example", "example", true);
ClientProducer producer = session.createProducer("example");
ClientMessage message = session.createMessage(true);
message.getBodyBuffer().writeString("Hello");
producer.send(message);
session.start();
ClientConsumer consumer = session.createConsumer("example");
ClientMessage msgReceived = consumer.receive();
System.out.println("message = " + msgReceived.getBodyBuffer().readString());
session.close();</programlisting>
</section>
</chapter>

View File

@ -1,273 +0,0 @@
<?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>

View File

@ -1,312 +0,0 @@
<?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-server">
<title>使用ActiveMQ服务</title>
<para>本章将介绍如何使用ActiveMQ服务。</para>
<para>其中的内容包括服务器的位置如何启动和停止ActiveMQ服务器。本章还将解释ActiveMQ的目录结构其中的文件及其用途。</para>
<para>本章中所提到的ActiveMQ服务器是指ActiveMQ默认配置的独立服务器包含JMS服务和JNDI服务。</para>
<para>对于运行于JBoss应用服务器中的ActiveMQ其基本结构是一样的只是有一些小的差别。</para>
<section>
<title>服务的启动和停止</title>
<para>在ActiveMQ的安装目录下<literal>bin</literal>子目录中包含有一个unit/linux脚本run.sh和对应的Windows批处理文件run.bat。</para>
<para>如果你是在Unix/Linux环境在bin目录下运行<literal>./run.sh</literal></para>
<para>如果是在Windows环境则在bin目录下运行 <literal>run.bat</literal></para>
<para>这个脚本文件会设置classpath以及各种JVM参数并启动JBoss Microcontainer。JBoss Microcontainer是一个轻量级的容器。
它被用来部署ActiveMQ的POJO对象。</para>
<para>要停止服务运行其中的相应脚本在Unix/Linux环境下运行 <literal>stop.sh</literal>
在Windows环境运行 <literal>run.bat</literal></para>
<para>注意ActiveMQ需要在Java 6及以上版本才能正常运行。</para>
<para>启动和停止脚本在默认条件下读取<literal>config/stand-alone/non-clustered</literal>目录下的配置文件。
如果要指向其他目录,可以在命令行实现,例如: <literal>./run.sh ../config/stand-alone/clustered</literal>
这一方法同样适用于Windows批处理文件。</para>
</section>
<section>
<title>服务器端JVM参数的设置</title>
<para>在启动脚本<literal>run.sh</literal><literal>run.bat</literal>中设置了一些JVM参数
这些参数主要是调整Java 6的运行环境及拉圾回收的策略。我们建议采用并行拉圾回收的方法。
这种方法可以将拉圾回收所造成的延时进行平均分配,有效减少由于拉圾回收引起的长时间暂停的情况。</para>
<para>默认条件下ActiveMQ需要最大1GB的内存空间。通过<literal>-Xms</literal><literal>-Xmx</literal>可以调整Java程序内存的使用。</para>
<para>你可以向启动脚本中添加其它的参数或修改已有的参数,已满足你的需要。</para>
</section>
<section>
<title>服务器端的classpath</title>
<para>ActiveMQ在其classpath中寻找配置文件。</para>
<para>classpath被定义在<literal>run.sh</literal><literal>run.bat</literal>脚本中。在ActiveMQ的发布中启动脚本将非集群的配置文件目录加进了classpath中。该目录包括了一组配置文件可以让ActiveMQ以基本的非集群方式运行。它的具体位置是在ActiveMQ发布根目录下 config/stand-along/non-clustered/ 子目录。</para>
<para>在ActiveMQ的发布包中包括了一组标准的配置目录它们是</para>
<itemizedlist>
<listitem>
<para>非集群方式的独立服务器配置</para>
</listitem>
<listitem>
<para>集群方式的独立服务器配置</para>
</listitem>
<listitem>
<para>非集群方式运行于JBoss应用服务器</para>
</listitem>
<listitem>
<para>集群方式运行于JBoss应用服务器</para>
</listitem>
</itemizedlist>
<para>当然你可以创建自己定义的配置文件目录。要注意的是将你的目录加到classpath中以便ActiveMQ能正确找到并加载。</para>
</section>
<section id="using-server.library.path">
<title>Library Path</title>
<para>如果要在Linux上使用异步IO的日志<link linkend="aio-journal">Asynchronous IO Journal</link>
你需要在java选项中指定<literal>java.library.path</literal><literal>run.sh</literal>脚本可以自动完成这一步。</para>
<para>如果没有指定<literal>java.library.path</literal>JVM将使用<literal>LD_LIBRARY_PATH</literal>环境变量。</para>
</section>
<section>
<title>系统变量</title>
<para>ActiveMQ命令行可以接受系统变量来配置日志logging。有关logging配置的具体信息参见<xref linkend="logging"/></para>
</section>
<section id="using-server.configuration">
<title>配置文件</title>
<para>配置文件的路径定义在 <literal>run.sh</literal><literal>run.bat</literal> 脚本中的classpath里。该路径下可以包含以下文件</para>
<itemizedlist>
<listitem>
<para><literal>activemq-beans.xml</literal> 如果是运行在JBoss应用服务器内则为 <literal
>activemq-jboss-beans.xml</literal>。这是JBoss Microcontainer的bean配置文件。其中定义了ActiveMQ的
各种bean以及它们之间的依赖关系。ActiveMQ的bean都是一些POJO。是JBoss Microcontainer保证了这些bean的正确装载和运行。</para>
</listitem>
<listitem>
<para><literal>activemq-configuration.xml</literal>。这个是ActiveMQ的主要的配置文件。
其中的所有参数在<xref linkend="configuration-index"/>中给出了解释. 在 <xref
linkend="usingserver.mainconfig"/> 也有更多的相关信息。</para>
</listitem>
<listitem>
<para><literal>activemq-queues.xml</literal>。这个文件里包含了预定义的queue以及它们的配置包括安全设置。
这是一个可选的文件,里面所有的内容都可以放在 <literal>activemq-configuration.xml</literal>文件中。
在默认的配置文件目录下并没有这个文件。ActiveMQ之所以提供这个文件是为了用户便于管理他们的queue。在classpath中
允许包含多个 <literal>activemq-queues.xml</literal> 文件。所有的这些文件都会被加载。</para>
</listitem>
<listitem>
<para><literal>activemq-users.xml</literal>。用户信息文件。ActiveMQ本身实现了一个基本的
安全管理器security manager它从这个文件内读取用户的安全信息如用户名密码和角色。
想了解更多关于安全的信息,参见 <xref linkend="security"/></para>
</listitem>
<listitem>
<para><literal>activemq-jms.xml</literal>。这个文件包含有JMS对象。ActiveMQ的默认配置中包含有JMS服务
它从这个文件中读取JMS QueueTopic和ConnectionFactory并将它们部署到JNDI服务中。如果你不使用JMS
或者你不需要部署这些JMS对象那么你就不需要这个文件。有关JMS的使用详见<xref linkend="using-jms"
/>。</para>
</listitem>
<listitem>
<para><literal>logging.properties</literal> 这个文件用于配置logging
handlers。详见 <xref linkend="logging"/></para>
</listitem>
<listitem>
<para><literal>log4j.xml</literal>。 这是 Log4j handler的配置文件。</para>
</listitem>
</itemizedlist>
<note>
<para>如果在<literal>activemq-configuration.xml</literal>文件中将<literal>file-deployment-enabled</literal> 参数
定义为false则ActiveMQ将不会加载其它的配置文件。这个参数的默认值是true。</para>
</note>
<para>所有配置文件中的参数都可以用系统变量来定义其值。以下用一个connector的配置来说明</para>
<programlisting>&lt;connector name="netty">
&lt;factory-class>org.apache.activemq.integration.transports.netty.NettyConnectorFactory
&lt;/factory-class>
&lt;param key="host" value="${activemq.remoting.netty.host:localhost}" type="String"/>
&lt;param key="port" value="${activemq.remoting.netty.port:5445}" type="Integer"/>
&lt;/connector></programlisting>
<para>在上面的配置中我们定义了两个系统变量 <literal
>activemq.remoting.netty.host</literal><literal
>activemq.remoting.netty.port</literal>。它们的值会被相应的系统变量的值(如果定义了的话)所替代。
如果没有定义这些系统变量,它们的默认值将分别为 localhost 及 5445。也可以不给出默认值但如果这样就
<emphasis>必须</emphasis>要定义相应的系统变量。</para>
</section>
<section id="server.microcontainer.configuration">
<title>JBoss Microcontainer Beans 文件</title>
<para>ActiveMQ的POJO对象是由<ulink url="http://www.jboss.org/jbossmc/"> JBoss Microcontainer
</ulink>进行加载和运行的。JBoss Microcontainer是一个轻量级的加载工具。</para>
<note>
<para>如果是在JBoss应用服务器内运行ActiveMQ同样需要一个bean的配置文件来将其部署到JBoss中。但是这与单独运行时的配置文件略有不同。
这是因为应用服务器内已经部署了一些服务如安全服务等。所以在ActiveMQ中这些服务就不需要再部署了。</para>
</note>
<para>让我们看一个ActiveMQ作为单独服务器时的一个配置文件例子</para>
<para>
<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;deployment xmlns="urn:jboss:bean-deployer:2.0"&gt;
&lt;bean name="Naming" class="org.jnp.server.NamingBeanImpl"/&gt;
&lt;!-- JNDI server. Disable this if you don't want JNDI --&gt;
&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;
&lt;!-- MBean server --&gt;
&lt;bean name="MBeanServer" class="javax.management.MBeanServer"&gt;
&lt;constructor factoryClass="java.lang.management.ManagementFactory"
factoryMethod="getPlatformMBeanServer"/&gt;
&lt;/bean&gt;
&lt;!-- The core configuration --&gt;
&lt;bean name="Configuration" class="org.apache.activemq.core.config.impl.FileConfiguration"&gt;
&lt;/bean&gt;
&lt;!-- The security manager --&gt;
&lt;bean name="ActiveMQSecurityManager"
class="org.apache.activemq.spi.core.security.ActiveMQSecurityManagerImpl"&gt;
&lt;start ignored="true"/&gt;
&lt;stop ignored="true"/&gt;
&lt;/bean&gt;
&lt;!-- The core server --&gt;
&lt;bean name="ActiveMQServer" class="org.apache.activemq.core.server.impl.ActiveMQServerImpl"&gt;
&lt;start ignored="true"/&gt;
&lt;stop ignored="true"/&gt;
&lt;constructor&gt;
&lt;parameter&gt;
&lt;inject bean="Configuration"/&gt;
&lt;/parameter&gt;
&lt;parameter&gt;
&lt;inject bean="MBeanServer"/&gt;
&lt;/parameter&gt;
&lt;parameter&gt;
&lt;inject bean="ActiveMQSecurityManager"/&gt;
&lt;/parameter&gt;
&lt;/constructor&gt;
&lt;/bean&gt;
&lt;!-- The JMS server --&gt;
&lt;bean name="JMSServerManager"
class="org.apache.activemq.jms.server.impl.JMSServerManagerImpl"&gt;
&lt;constructor&gt;
&lt;parameter&gt;
&lt;inject bean="ActiveMQServer"/&gt;
&lt;/parameter&gt;
&lt;/constructor&gt;
&lt;/bean&gt;
&lt;/deployment&gt;</programlisting>
</para>
<para>我们从上可以看出ActiveMQ的单独服务器以及核心服务器包括了一些POJO对象</para>
<itemizedlist>
<listitem>
<para>JNDIServer</para>
<para>很多客户端需要JNDI来获取JMS的对象因此我们提供了一个JNDI服务来满足它们。如果不需要JNDI可以在配置
文件中将它们注释掉。</para>
</listitem>
<listitem>
<para>MBeanServer</para>
<para>这个对象提供了JMX管理接口。它是一个MBean服务器可管理的对象可以注册到这个服务器上。
通常这就是一个JVM内部的默认的平台MBean服务器。如果不需要些服务可以在配置文件中将其注释或删除。</para>
</listitem>
<listitem>
<para>Configuration</para>
<para>这是ActiveMQ的Configuration对象。默认时它是一个FileConfiguration对象。它可以从文件系统中读取
配置信息。有些情况下如嵌入式ActiveMQ你可以将它定义为其它对象以便用其它方法获得配置信息。</para>
</listitem>
<listitem>
<para>Security Manager. 可配置的安全管理器。默认的安全管理器使用 <literal>activemq-users.xml</literal> 文件中的配置信息。
它也可以配置为一个JAAS的安全管理器。当ActiveMQ运行于JBoss应用服务器中时它还可以配置为JBoss的安全管理器以达到更紧密的集成。
如果不需要安全管理,你也可以将它删除。</para>
</listitem>
<listitem>
<para>ActiveMQServer</para>
<para>这是ActiveMQ的核心服务对象几乎所有的核心功能都在这里。</para>
</listitem>
<listitem id="bean-jmsservermanager">
<para>JMSServerManager</para>
<para>这个对象将<literal>activemq-jms.xml</literal>文件中定义的JMS的对象进行部署比如JMS Queues, Topics
以及ConnectionFactory。它还提供一套简单的管理接口以方便地对这些JMS对象进行管理。通常它只是将工作代理给
核心服务器。如果你不需要在服务器端进行JMS对象的部署与管理可以将它从配置中去掉。</para>
</listitem>
</itemizedlist>
</section>
<section id="server.microkernel.configuration">
<title>JBoss AS4 MBean 服务</title>
<note>
<para>本节只讨论在JBoss AS 4上配置ActiveMQ。其与JBoss Microcontainer的配置很相似。</para>
</note>
<para>
<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;server&gt;
&lt;mbean code="org.apache.activemq.service.ActiveMQFileConfigurationService"
name="org.apache.activemq:service=ActiveMQFileConfigurationService"&gt;
&lt;/mbean&gt;
&lt;mbean code="org.apache.activemq.service.JBossASSecurityManagerService"
name="org.apache.activemq:service=JBossASSecurityManagerService"&gt;
&lt;/mbean&gt;
&lt;mbean code="org.apache.activemq.service.ActiveMQStarterService"
name="org.apache.activemq:service=ActiveMQStarterService"&gt;
&lt;!--let's let the JMS Server start us--&gt;
&lt;attribute name="Start"&gt;false&lt;/attribute&gt;
&lt;depends optional-attribute-name="SecurityManagerService"
proxy-type="attribute"&gt;org.apache.activemq:service=JBossASSecurityManagerService&lt;/depends&gt;
&lt;depends optional-attribute-name="ConfigurationService"
proxy-type="attribute"&gt;org.apache.activemq:service=ActiveMQFileConfigurationService&lt;/depends&gt;
&lt;/mbean&gt;
&lt;mbean code="org.apache.activemq.service.ActiveMQJMSStarterService"
name="org.apache.activemq:service=ActiveMQJMSStarterService"&gt;
&lt;depends optional-attribute-name="ActiveMQServer"
proxy-type="attribute"&gt;org.apache.activemq:service=ActiveMQStarterService&lt;/depends&gt;
&lt;/mbean&gt;
&lt;/server&gt;
</programlisting>
</para>
<para>这个jboss-service.xml包含在activemq-server.sar文件中它用来配置AS4中嵌入运行的ActiveMQ。
在这个配置文件中我们启动了以下几个服务:</para>
<itemizedlist>
<listitem>
<para>ActiveMQFileConfigurationService</para>
<para>这个MBean服务的任务是管理 <literal>FileConfiguration POJO</literal>的生命周期。</para>
</listitem>
<listitem>
<para>JBossASSecurityManagerService</para>
<para>这个MBean服务管理着 <literal>JBossASSecurityManager</literal> POJO的生命周期。</para>
</listitem>
<listitem>
<para>ActiveMQStarterService</para>
<para>这个MBean服务管理着<literal>ActiveMQServer</literal> POJO。它依赖于 JBossASSecurityManagerService 和
ActiveMQFileConfigurationService 这两个MBean。</para>
</listitem>
<listitem>
<para>ActiveMQJMSStarterService</para>
<para>这个MBean服务管理着 <literal>JMSServerManagerImpl</literal> POJO对象。如果不需要JMS可以去掉这个服务。</para>
</listitem>
<listitem>
<para>JMSServerManager</para>
<para>用于启动JMSServerManager。</para>
</listitem>
</itemizedlist>
</section>
<section id="usingserver.mainconfig">
<title>主配置文件</title>
<para>ActiveMQ 核心服务的配置保存在 <literal>activemq-configuration.xml</literal>文件中。
         FileConfiguration bean 读取这个文件来对消息服务器进行配置。</para>
<para>ActiveMQ有很多的配置参数。采用默认的配置在绝大多数情况下可以很好的运行。事实上每一个参数都有默认的处理因此一个只包含有一个空
<literal>configuration</literal>的配置文件是一个有效的文件。对各个参数的解释贯穿于本手册。你还可参参照
<link linkend="configuration-index">这里</link>来查找你想看的参数。</para>
</section>
</chapter>

View File

@ -1,37 +0,0 @@
<?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="wildcard-routing">
<title>使用通配符实现消息路由</title>
<para>ActiveMQ支持使用带通配符的地址对消息路由。</para>
<para>例如,当创建一个队列时使用了地址<literal>queue.news.#</literal>,那么它就能接收
所有和这个地址通配符相配的每一个地址的消息。这样的地址如 <literal
>queue.news.europe</literal><literal>queue.news.usa</literal><literal
>queue.news.usa.sport</literal>等。这样一个消息接收者可以接收<literal>一组</literal>相关
的地址的消息,而不是只能指定一个具体的地址。</para>
<note>
<para>用JMS的术语来说这个功能就是允许创建“话题组”topic hierarchy</para>
</note>
<para>要使用本功能需要将<literal>wild-card-routing-enabled</literal>属性设置为<literal>true</literal>
这个属性在 <literal>activemq-configuration.xml</literal> 文件中。默认值是<literal>true</literal></para>
<para>关于通配符的语法参见<xref
linkend="wildcard-syntax" /> 章及 <xref
linkend="topic-hierarchy-example" />。</para>
</chapter>

View File

@ -1,37 +0,0 @@
<?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="wildcard-syntax">
<title>了解 ActiveMQ 通配符的语法</title>
<para>ActiveMQ使用了一种专门的通配符语法来配置安全、地址及接收者consumer的创建。</para>
<para>这种语法与 <ulink url="http://www.amqp.org">AMQP</ulink>所用的语法相似。</para>
<para>一个ActiveMQ的通配符表达式是由一些由“<literal
>.</literal>”分隔的单词组成。</para>
<para>特殊字符“<literal>#</literal>”和“<literal>*</literal>”在表达式中可作为一个单词,它们代表
特殊的意义。</para>
<para>字符“<literal>#</literal>”表示“零或多个单词的任意排列”。</para>
<para>字符“<literal>*</literal>”表示“一个单词”。</para>
<para>因此通配符表达式“news.europe.#”可以匹配“news.europe”、“news.europe.sport”、
“news.europe.politics”以及“news.europe.politics.regional”但是与“news.usa”、
“news.usa.sport” 及 “entertainment”不相匹配。</para>
<para>通配符“news.*”与“news.europe”匹配但不与“news.europe.sport”匹配。</para>
<para>通配符“news.*.sport”与“news.europe.sport”及“news.usa.sport”匹配但与
“news.europe.politics”不匹配。</para>
</chapter>