551 lines
43 KiB
XML
551 lines
43 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="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><broadcast-groups>
|
||
<broadcast-group name="my-broadcast-group">
|
||
<local-bind-address>172.16.9.3</local-bind-address>
|
||
<local-bind-port>54321</local-bind-port>
|
||
<group-address>231.7.7.7</group-address>
|
||
<group-port>9876</group-port>
|
||
<broadcast-period>2000</broadcast-period>
|
||
<connector-ref connector-name="netty-connector"
|
||
backup-connector="backup-connector"/>
|
||
</broadcast-group>
|
||
</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><discovery-groups>
|
||
<discovery-group name="my-discovery-group">
|
||
<local-bind-address>172.16.9.7</local-bind-address>
|
||
<group-address>231.7.7.7</group-address>
|
||
<group-port>9876</group-port>
|
||
<refresh-timeout>10000</refresh-timeout>
|
||
</discovery-group>
|
||
</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>hornetq-jms.xml</literal>中指定连接工厂所用的发现组。如下面所示:</para>
|
||
<programlisting><connection-factory name="ConnectionFactory">
|
||
<discovery-group-ref discovery-group-name="my-discovery-group"/>
|
||
<entries>
|
||
<entry name="ConnectionFactory"/>
|
||
</entries>
|
||
</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>
|
||
<cluster-connections>
|
||
<cluster-connection name="my-cluster">
|
||
<address>jms</address>
|
||
<retry-interval>500</retry-interval>
|
||
<use-duplicate-detection>true</use-duplicate-detection>
|
||
<forward-when-no-consumers>false</forward-when-no-consumers>
|
||
<max-hops>1</max-hops>
|
||
<discovery-group-ref discovery-group-name="my-discovery-group"/>
|
||
</cluster-connection>
|
||
</cluster-connections>
|
||
</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>
|
||
<cluster-user>HORNETQ.CLUSTER.ADMIN.USER</cluster-user>
|
||
<cluster-password>CHANGE ME!!</cluster-password>
|
||
</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, D,A, 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>hornetq-jms.xml</literal>文件中定义策略,如:
|
||
<programlisting>
|
||
<connection-factory name="ConnectionFactory">
|
||
<discovery-group-ref discovery-group-name="my-discovery-group"/>
|
||
<entries>
|
||
<entry name="ConnectionFactory"/>
|
||
</entries>
|
||
<connection-load-balancing-policy-class-name>
|
||
org.apache.activemq.api.core.client.loadbalance.RandomConnectionLoadBalancingPolicy
|
||
</connection-load-balancing-policy-class-name>
|
||
</connection-factory>
|
||
</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>hornetq-jms.xml</literal>中来指定,如下面的例子:</para>
|
||
<programlisting><connection-factory name="ConnectionFactory">
|
||
<connectors>
|
||
<connector-ref connector-name="my-connector1"
|
||
backup-connector-name="my-backup-connector1"/>
|
||
<connector-ref connector-name="my-connector2"
|
||
backup-connector-name="my-backup-connector2"/>
|
||
<connector-ref connector-name="my-connector3"
|
||
backup-connector-name="my-backup-connector3"/>
|
||
</connectors>
|
||
<entries>
|
||
<entry name="ConnectionFactory"/>
|
||
</entries>
|
||
</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<Pair<TransportConfiguration, TransportConfiguration>> serverList =
|
||
new ArrayList<Pair<TransportConfiguration, TransportConfiguration>>();
|
||
|
||
serverList.add(new Pair<TransportConfiguration,
|
||
TransportConfiguration>(liveTC0, backupTC0));
|
||
serverList.add(new Pair<TransportConfiguration,
|
||
TransportConfiguration>(liveTC1, backupTC1));
|
||
serverList.add(new Pair<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<Pair<TransportConfiguration, TransportConfiguration>> serverList =
|
||
new ArrayList<Pair<TransportConfiguration, TransportConfiguration>>();
|
||
|
||
serverList.add(new Pair<TransportConfiguration,
|
||
TransportConfiguration>(liveTC0, backupTC0));
|
||
serverList.add(new Pair<TransportConfiguration,
|
||
TransportConfiguration>(liveTC1, backupTC1));
|
||
serverList.add(new Pair<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><cluster-connections>
|
||
<cluster-connection name="my-explicit-cluster">
|
||
<address>jms</address>
|
||
<connector-ref connector-name="my-connector1"
|
||
backup-connector-name="my-backup-connector1"/>
|
||
<connector-ref connector-name="my-connector2"
|
||
backup-connector-name="my-backup-connector2"/>
|
||
<connector-ref connector-name="my-connector3"
|
||
backup-connector-name="my-backup-connector3"/>
|
||
</cluster-connection>
|
||
</cluster-connections></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><address-settings>
|
||
<address-setting match="jms.#">
|
||
<redistribution-delay>0</redistribution-delay>
|
||
</address-setting>
|
||
</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>
|