383 lines
24 KiB
XML
383 lines
24 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!-- ============================================================================= -->
|
||
<!-- Copyright © 2009 Red Hat, Inc. and others. -->
|
||
<!-- -->
|
||
<!-- The text of and illustrations in this document are licensed by Red Hat under -->
|
||
<!-- a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). -->
|
||
<!-- -->
|
||
<!-- An explanation of CC-BY-SA is available at -->
|
||
<!-- -->
|
||
<!-- http://creativecommons.org/licenses/by-sa/3.0/. -->
|
||
<!-- -->
|
||
<!-- In accordance with CC-BY-SA, if you distribute this document or an adaptation -->
|
||
<!-- of it, you must provide the URL for the original version. -->
|
||
<!-- -->
|
||
<!-- Red Hat, as the licensor of this document, waives the right to enforce, -->
|
||
<!-- and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent -->
|
||
<!-- permitted by applicable law. -->
|
||
<!-- ============================================================================= -->
|
||
<chapter id="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><?xml version="1.0" encoding="UTF-8"?>
|
||
|
||
<deployment xmlns="urn:jboss:bean-deployer:2.0">
|
||
|
||
<bean name="JMSBridge" class="org.apache.activemq.api.jms.bridge.impl.JMSBridgeImpl">
|
||
<!-- ActiveMQ must be started before the bridge -->
|
||
<depends>ActiveMQServer</depends>
|
||
<constructor>
|
||
<!-- Source ConnectionFactory Factory -->
|
||
<parameter>
|
||
<inject bean="SourceCFF"/>
|
||
</parameter>
|
||
<!-- Target ConnectionFactory Factory -->
|
||
<parameter>
|
||
<inject bean="TargetCFF"/>
|
||
</parameter>
|
||
<!-- Source DestinationFactory -->
|
||
<parameter>
|
||
<inject bean="SourceDestinationFactory"/>
|
||
</parameter>
|
||
<!-- Target DestinationFactory -->
|
||
<parameter>
|
||
<inject bean="TargetDestinationFactory"/>
|
||
</parameter>
|
||
<!-- Source User Name (no username here) -->
|
||
<parameter><null /></parameter>
|
||
<!-- Source Password (no password here)-->
|
||
<parameter><null /></parameter>
|
||
<!-- Target User Name (no username here)-->
|
||
<parameter><null /></parameter>
|
||
<!-- Target Password (no password here)-->
|
||
<parameter><null /></parameter>
|
||
<!-- Selector -->
|
||
<parameter><null /></parameter>
|
||
<!-- Failure Retry Interval (in ms) -->
|
||
<parameter>5000</parameter>
|
||
<!-- Max Retries -->
|
||
<parameter>10</parameter>
|
||
<!-- Quality Of Service -->
|
||
<parameter>ONCE_AND_ONLY_ONCE</parameter>
|
||
<!-- Max Batch Size -->
|
||
<parameter>1</parameter>
|
||
<!-- Max Batch Time (-1 means infinite) -->
|
||
<parameter>-1</parameter>
|
||
<!-- Subscription name (no subscription name here)-->
|
||
<parameter><null /></parameter>
|
||
<!-- Client ID (no client ID here)-->
|
||
<parameter><null /></parameter>
|
||
<!-- Add MessageID In Header -->
|
||
<parameter>true</parameter>
|
||
<!-- register the JMS Bridge in the AS MBeanServer -->
|
||
<parameter>
|
||
<inject bean="MBeanServer"/>
|
||
</parameter>
|
||
<parameter>org.apache.activemq:service=JMSBridge</parameter>
|
||
</constructor>
|
||
<property name="transactionManager">
|
||
<inject bean="RealTransactionManager"/>
|
||
</property>
|
||
</bean>
|
||
|
||
<!-- SourceCFF describes the ConnectionFactory used to connect to the
|
||
source destination -->
|
||
<bean name="SourceCFF"
|
||
class="org.apache.activemq.api.jms.bridge.impl.JNDIConnectionFactoryFactory">
|
||
<constructor>
|
||
<parameter>
|
||
<inject bean="JNDI" />
|
||
</parameter>
|
||
<parameter>/ConnectionFactory</parameter>
|
||
</constructor>
|
||
</bean>
|
||
|
||
<!-- TargetCFF describes the ConnectionFactory used to connect to the
|
||
target destination -->
|
||
<bean name="TargetCFF"
|
||
class="org.apache.activemq.api.jms.bridge.impl.JNDIConnectionFactoryFactory">
|
||
<constructor>
|
||
<parameter>
|
||
<inject bean="JNDI" />
|
||
</parameter>
|
||
<parameter>/ConnectionFactory</parameter>
|
||
</constructor>
|
||
</bean>
|
||
|
||
<!-- SourceDestinationFactory describes the Destination used as the source -->
|
||
<bean name="SourceDestinationFactory"
|
||
class="org.apache.activemq.api.jms.bridge.impl.JNDIDestinationFactory">
|
||
<constructor>
|
||
<parameter>
|
||
<inject bean="JNDI" />
|
||
</parameter>
|
||
<parameter>/queue/source</parameter>
|
||
</constructor>
|
||
</bean>
|
||
|
||
<!-- TargetDestinationFactory describes the Destination used as the target -->
|
||
<bean name="TargetDestinationFactory"
|
||
class="org.apache.activemq.api.jms.bridge.impl.JNDIDestinationFactory">
|
||
<constructor>
|
||
<parameter>
|
||
<inject bean="JNDI" />
|
||
</parameter>
|
||
<parameter>/queue/target</parameter>
|
||
</constructor>
|
||
</bean>
|
||
|
||
<!-- JNDI is a Hashtable containing the JNDI properties required -->
|
||
<!-- to connect to the sources and targets JMS resrouces -->
|
||
<bean name="JNDI" class="java.util.Hashtable">
|
||
<constructor class="java.util.Map">
|
||
<map class="java.util.Hashtable" keyClass="String"
|
||
valueClass="String">
|
||
<entry>
|
||
<key>java.naming.factory.initial</key>
|
||
<value>org.jnp.interfaces.NamingContextFactory</value>
|
||
</entry>
|
||
<entry>
|
||
<key>java.naming.provider.url</key>
|
||
<value>jnp://localhost:1099</value>
|
||
</entry>
|
||
<entry>
|
||
<key>java.naming.factory.url.pkgs</key>
|
||
<value>org.jboss.naming:org.jnp.interfaces"</value>
|
||
</entry>
|
||
<entry>
|
||
<key>jnp.timeout</key>
|
||
<value>5000</value>
|
||
</entry>
|
||
<entry>
|
||
<key>jnp.sotimeout</key>
|
||
<value>5000</value>
|
||
</entry>
|
||
</map>
|
||
</constructor>
|
||
</bean>
|
||
|
||
<bean name="MBeanServer" class="javax.management.MBeanServer">
|
||
<constructor factoryClass="org.jboss.mx.util.MBeanServerLocator"
|
||
factoryMethod="locateJBoss"/>
|
||
</bean>
|
||
</deployment></programlisting>
|
||
<section>
|
||
<title>JMS桥的配置参数</title>
|
||
<para>桥的主要的bean是<literal>JMSBridge</literal>。所有的配置参数需要传递给这个bean的
|
||
构造函数。</para>
|
||
<note>
|
||
<para>如果不想指定某个参数的值(例如匿名认证或没有选择器),将该参数设为<literal><null
|
||
/></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>客户ID(Client ID)</para>
|
||
<para>如果源的目标是一个话题(topic),你想使用持久的订阅来接收消息的话,这个参数可以指定
|
||
JMS的客户ID。它用于创建/查找持久订阅。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>在消息头添加MessageID(Add MessageID In Header)</para>
|
||
<para>如果值为<literal>true</literal>,原始的消息ID在发往目的是回到消息的名为<literal
|
||
>HORNETQ_BRIDGE_MSG_ID_LIST</literal>的头中。如果一个消息被桥转发了多次,
|
||
则每次转发的消息ID都添加在这个头中。这用于分布式请求/回答的消息模式。</para>
|
||
<note>
|
||
<para>当收到一个消息时,通过它的相关ID(coorelation 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>
|