1. 概述

Apache ActiveMQ Artemis 集群允许将一组 Apache ActiveMQ Artemis 服务器组合在一起,以共享消息处理负载。集群中的每个活动节点都是一个活动的 Apache ActiveMQ Artemis 服务器,它管理自己的消息并处理自己的连接。

集群的形成是通过每个节点在核心配置文件 broker.xml 中声明对集群中其他节点的集群连接 来实现的。当一个节点形成到另一个节点的集群连接时,它在内部会在它和另一个节点之间创建一个核心桥接(如 核心桥接 中所述)连接,这是在幕后透明地完成的 - 你不必为每个节点声明一个显式的桥接。这些集群连接允许消息在集群的节点之间流动以平衡负载。

节点可以连接在一起以形成多种不同的拓扑结构的集群,我们将在本章稍后讨论几个更常见的拓扑结构。

我们还将讨论客户端负载均衡,我们可以在集群的节点之间平衡客户端连接,并将考虑消息重新分配,其中 Apache ActiveMQ Artemis 将在节点之间重新分配消息以避免饥饿。

集群的另一个重要部分是服务器发现,其中服务器可以广播其连接详细信息,以便客户端或其他服务器可以以最少的配置连接到它们。

一旦集群节点被配置,通常只需将该配置复制到其他节点以生成一个对称集群。但是,复制 Apache ActiveMQ Artemis 文件时必须小心。不要将 Apache ActiveMQ Artemis 数据(即 bindingsjournalpaginglarge-messages 目录)从一个节点复制到另一个节点。当一个节点第一次启动并初始化其日志文件时,它还会将一个特殊的标识符持久化到 journal 目录。此 ID 必须在集群中的节点之间唯一,否则集群将无法正常形成。

2. 性能注意事项

重要的是要注意,虽然集群的目标是通过横向扩展来提高整体消息吞吐量,但它并不是“万能药”。在某些情况下,集群实际上会降低消息吞吐量,因此在选择集群配置时必须小心。以下是一些一般准则

  1. 建立一个清晰具体的性能目标。 性能测试和调整通常是困难且乏味的操作。小的相对收益会让你继续进行下去,而没有目标,你永远不会知道何时停止。你需要一个目标才能知道“足够好到底有多好”。

  2. 从简单开始。 首先使用单个代理对你的用例进行基准测试。单个代理可以在某些用例中每秒处理数百万条消息。如果你无法使用单个代理满足你的性能目标,那么才使用集群配置。只有在有明显益处时才增加复杂性。

集群降低整体消息吞吐量的主要方式是,如果每个节点上的生产者和消费者数量不足,导致某些节点上的消息堆积,而其他节点上的消费者出现饥饿。集群具有机制来处理这种情况(即消息负载均衡和重新分配,我们将在后面讨论),但你真的不希望代理在绝对必要的情况下介入并在节点之间移动消息,因为这会增加延迟

因此,在性能方面考虑问题时,选择集群配置时必须回答的主要问题是:我是否有足够的客户端,以便集群中的每个节点都有足够的消费者来接收该节点上产生的所有消息?如果答案是“是”,那么集群实际上可能会提高你的整体消息吞吐量。如果答案是“否”,那么你可能从较小的集群或单个代理获得更好的性能。

还要记住,连接路由器 可以通过将相关的消费者和生产者分组到同一个节点上,来提高集群的性能。

3. 服务器发现

服务器发现是一种机制,通过这种机制,服务器可以将其连接详细信息传播到

  • 消息客户端。消息客户端希望能够连接到集群的服务器,而无需具体知道集群中的哪些服务器在任何特定时间都处于活动状态。

  • 其他服务器。集群中的服务器希望能够相互创建集群连接,而无需事先了解集群中的所有其他服务器。

此信息(我们称之为集群拓扑结构)实际上是通过正常 Apache ActiveMQ Artemis 连接到客户端以及通过集群连接到其他服务器发送的。因此,我们需要一种方法来建立最初的第一个连接。这可以使用动态发现技术(如 UDPJGroups)来完成,也可以通过提供一个初始连接器的列表来完成。

3.1. 动态发现

服务器发现使用 UDP 多播或 JGroups 来广播服务器连接设置。

3.1.1. 广播组

广播组是服务器通过网络广播连接器的方式。连接器定义了客户端(或其他服务器)如何与服务器建立连接的方式。有关连接器是什么的更多信息,请参见 配置传输

广播组接受连接器并将其广播到网络上。根据你配置的广播技术,它使用 UDP 或 JGroups 来广播连接器信息。

广播组在服务器配置文件 broker.xml 中定义。每个 Apache ActiveMQ Artemis 服务器可以有多个广播组。所有广播组都必须定义在 broadcast-groups 元素中。

让我们看一个来自 broker.xml 的示例广播组,它定义了一个 UDP 广播组

<broadcast-groups>
   <broadcast-group name="my-broadcast-group">
    <local-bind-address>172.16.9.3</local-bind-address>
    <local-bind-port>5432</local-bind-port>
    <group-address>231.7.7.7</group-address>
    <group-port>9876</group-port>
    <broadcast-period>2000</broadcast-period>
    <connector-ref>netty-connector</connector-ref>
   </broadcast-group>
</broadcast-groups>

一些广播组参数是可选的,你通常会使用默认值,但我们在上面的示例中为了清晰起见指定了所有参数。让我们逐一讨论每个参数

name

属性。服务器中的每个广播组都必须具有唯一的名称。

local-bind-address

这是数据报套接字绑定的本地绑定地址。如果你在服务器上有多个网络接口,你可以通过设置此属性来指定要用于广播的接口。如果未指定此属性,则套接字将绑定到通配符地址,即内核选择的 IP 地址。这是一个 UDP 特定属性。

local-bind-port

如果你想指定数据报套接字绑定的本地端口,你可以在此处指定它。通常你只需要使用默认值 -1,它表示应使用匿名端口。此参数始终与 local-bind-address 结合使用。这是一个 UDP 特定属性。

group-address

这是数据将广播到的多播地址。它是一个位于 224.0.0.0239.255.255.255(含)范围内的 D 类 IP 地址。地址 224.0.0.0 是保留的,不可用。此参数是必需的。这是一个 UDP 特定属性。

group-port

这是用于广播的 UDP 端口号。此参数是必需的。这是一个 UDP 特定属性。

broadcast-period

这是连续广播之间的周期,单位为毫秒。此参数是可选的,默认值为 2000 毫秒。

connector-ref

这指定了将要广播的连接器和可选的备份连接器(有关连接器的更多信息,请参见 配置传输)。

以下是用 JGroups 广播组定义的另一个示例广播组

<broadcast-groups>
   <broadcast-group name="my-broadcast-group">
      <broadcast-period>2000</broadcast-period>
      <jgroups-file>test-jgroups-file_ping.xml</jgroups-file>
      <jgroups-channel>activemq_broadcast_channel</jgroups-channel>
      <connector-ref>netty-connector</connector-ref>
   </broadcast-group>
</broadcast-groups>

为了能够使用 JGroups 进行广播,必须指定两个属性,即 jgroups-filejgroups-channel,如以下详细讨论的那样

jgroups-file

属性。这是 JGroups 配置文件的名称。它将用于初始化 JGroups 通道。确保该文件位于 java 资源路径中,以便 Apache ActiveMQ Artemis 可以加载它。该文件的典型位置是代理实例的 etc 目录。

jgroups-channel

属性。JGroups 通道为广播连接到的名称。

JGroups 属性(jgroups-filejgroups-channel)以及上面描述的 UDP 特定属性是互斥的。在广播组配置中只能指定一组。不要混用它们!

以下是一个 JGroups 文件的示例

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="urn:org:jgroups"
        xsi:schemaLocation="urn:org:jgroups http://www.jgroups.org/schema/jgroups.xsd">
  <TCP bind_addr="${jgroups.bind_addr:site_local}"
       bind_port="${jgroups.bind_port:7800}"
       external_addr="${jgroups.external_addr}"
       external_port="${jgroups.external_port}"
       thread_pool.min_threads="0"
       thread_pool.max_threads="200"
       thread_pool.keep_alive_time="30000"/>
  <RED/>

  <!-- a location that can be found by both server's running -->
  <FILE_PING location="../file.ping.dir"/>
  <MERGE3  min_interval="10000"
           max_interval="30000"/>
  <FD_SOCK2/>
  <FD_ALL3 timeout="40000" interval="5000" />
  <VERIFY_SUSPECT2 timeout="1500"  />
  <BARRIER />
  <pbcast.NAKACK2 use_mcast_xmit="false" />
  <UNICAST3 />
  <pbcast.STABLE desired_avg_gossip="50000"
                 max_bytes="4M"/>
  <pbcast.GMS print_local_addr="true" join_timeout="2000"/>
  <UFC max_credits="2M"
       min_threshold="0.4"/>
  <MFC max_credits="2M"
       min_threshold="0.4"/>
  <FRAG2 frag_size="60K"  />
  <!--RSVP resend_interval="2000" timeout="10000"/-->
  <pbcast.STATE_TRANSFER/>
</config>

如它所示,文件内容定义了 jgroups 协议堆栈。如果你希望 Apache ActiveMQ Artemis 使用此堆栈创建通道,你必须确保你的 broadcast-group/discovery-group 配置中 jgroups-file 的值与此 jgroups 配置文件的名称相同。例如,如果上面的堆栈配置存储在一个名为“jgroups-stacks.xml”的文件中,那么你的 jgroups-file 应该类似于

<jgroups-file>jgroups-stacks.xml</jgroups-file>

3.1.2. 发现组

广播组定义了如何从服务器广播连接器信息,而发现组则定义了如何从广播端点(UDP 多播地址或 JGroup 通道)接收连接器信息。

发现组维护一个连接器对列表 - 每个连接器对对应于不同服务器的每个广播。当它从特定服务器的广播端点接收到广播时,它会更新其列表中该服务器的条目。

如果它在一段时间内没有从特定服务器接收到广播,它将从其列表中删除该服务器的条目。

发现组在 Apache ActiveMQ Artemis 中的两个地方使用

  • 由集群连接使用,以便它们知道如何获取初始连接以下载拓扑

  • 由消息客户端使用,以便它们知道如何获取初始连接以下载拓扑

虽然发现组始终接受广播,但其当前的可用主服务器和备份服务器列表仅在建立初始连接时使用,从那时起,服务器发现通过正常的 Apache ActiveMQ Artemis 连接完成。

每个发现组必须配置一个广播端点(UDP 或 JGroups),该端点与其广播组对应端点匹配。例如,如果使用 UDP 配置广播,则发现组也必须使用 UDP,并且使用相同的组播地址。

3.1.3. 在服务器上定义发现组

对于集群连接,发现组在服务器端配置文件 broker.xml 中定义。所有发现组都必须定义在 discovery-groups 元素内。Apache ActiveMQ Artemis 服务器可以定义多个发现组。让我们看一个例子

<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>

我们将考虑发现组的每个参数

name

属性。每个发现组在每个服务器上必须具有唯一的名称。

local-bind-address

如果您在同一台机器上运行多个网络接口,您可能需要指定发现组仅监听特定接口。为此,您可以使用此参数指定接口地址。此参数是可选的。这是一个 UDP 特定属性。

group-address

这是要监听的组的多播 IP 地址。它应该与您要从中监听的广播组中的 group-address 相匹配。此参数是必需的。这是一个 UDP 特定属性。

group-port

这是多播组的 UDP 端口。它应该与您要从中监听的广播组中的 group-port 相匹配。此参数是必需的。这是一个 UDP 特定属性。

refresh-timeout

这是发现组在从特定服务器收到最后一次广播后等待的周期,在此期间,发现组将在其列表中删除该服务器的连接器对条目。通常,您会将此值设置为明显高于广播组上的 broadcast-period 的值,否则服务器可能会断断续续地从列表中消失,即使它们仍在广播,因为时间略有不同。此参数是可选的,默认值为 10000 毫秒(10 秒)。

以下是在定义 JGroups 发现组的另一个示例

<discovery-groups>
   <discovery-group name="my-broadcast-group">
      <jgroups-file>test-jgroups-file_ping.xml</jgroups-file>
      <jgroups-channel>activemq_broadcast_channel</jgroups-channel>
      <refresh-timeout>10000</refresh-timeout>
   </discovery-group>
</discovery-groups>

要接收来自 JGroups 通道的广播,必须指定两个属性 jgroups-filejgroups-channel,如下所述

jgroups-file

属性。这是 JGroups 配置文件的名称。它将用于初始化 JGroups 通道。确保该文件位于 Java 资源路径中,以便 Apache ActiveMQ Artemis 可以加载它。

jgroups-channel

属性。JGroups 通道连接以接收广播的名称。

JGroups 属性 (jgroups-filejgroups-channel) 和上面描述的 UDP 特定属性是互斥的。在一个发现组配置中只能指定一组。不要混用它们!

3.1.4. 客户端的发现组

让我们讨论如何配置 Apache ActiveMQ Artemis 客户端以使用发现来发现可以连接到的服务器列表。使用 JMS 还是核心 API,执行此操作的方式有所不同。

配置客户端发现

使用 udp URL 方案,主机:端口组合与服务器上相应 broadcast-group 中的 group-address 和 group-port 相匹配

udp://231.7.7.7:9876

使用此 URI 创建的连接将在发现组通过监听发现组配置中指定的多播地址维护的服务器列表中进行负载均衡。

前面提到的 refreshTimeout 参数可以直接在 URI 中设置。

还有一个名为 initialWaitTimeout 的 URL 参数。如果在创建后立即使用相应的 JMS 连接工厂或核心会话工厂,则它可能没有足够的时间接收来自集群中所有节点的广播。在第一次使用时,连接工厂将确保它在创建第一个连接之前等待这么长时间。此参数的默认值为 10000 毫秒。

3.2. 使用静态连接器进行发现

有时,在您使用的网络上可能无法使用 UDP。在这种情况下,可以使用初始可能的服务器列表配置连接。这可能只是一台您知道始终可用的服务器,或一台或多台始终可用的服务器列表。

这并不意味着您必须知道所有服务器将托管在何处,您可以将这些服务器配置为使用可靠服务器进行连接。一旦它们连接,它们的连接详细信息将通过它们连接到的服务器进行传播

3.2.1. 配置集群连接

对于集群连接,不需要额外的配置,您只需要确保所有连接器都以通常的方式定义即可(有关连接器的更多信息,请参阅 配置传输)。然后,集群连接配置会引用这些连接器。

3.2.2. 配置客户端连接

普通客户端也可以使用静态可能的服务器列表。

配置客户端发现

可以使用带 () 的语法在连接 URI 中指定用于初始连接尝试的服务器列表,例如

(tcp://myhost:61616,tcp://myhost2:61616)?reconnectAttempts=5

括号将被展开,以便为了方便,可以在最后一个括号之后追加相同的查询。

4. 服务器端消息负载均衡

如果在集群的节点之间定义了集群连接,那么 Apache ActiveMQ Artemis 将对从客户端到达特定节点的消息进行负载均衡。

让我们举一个简单示例,一个由四个节点 A、B、C 和 D 组成的集群,它们以对称集群形式排列(在对称集群部分进行了描述)。我们有一个名为 OrderQueue 的队列部署在集群的每个节点上。

我们有一个连接到节点 A 的客户端 Ca,它将订单发送到服务器。我们还有订单处理程序客户端 Pa、Pb、Pc 和 Pd,它们连接到节点 A、B、C、D 的每个节点。如果没有在节点 A 上定义集群连接,那么当订单消息到达节点 A 时,它们最终都将进入节点 A 上的 OrderQueue,因此只会由连接到节点 A 的订单处理程序客户端 Pa 使用。

如果我们在节点 A 上定义集群连接,那么当订单消息到达节点 A 时,它们将不会全部进入本地 OrderQueue 实例,而是以循环方式分发到集群的所有节点。消息从接收节点转发到集群中的其他节点。这一切都在服务器端完成,客户端保持与节点 A 的单一连接。

例如,到达节点 A 的消息可能会以以下顺序分发到节点之间:B、D、C、A、B、D、C、A、B、D。确切的顺序取决于节点启动的顺序,但使用的算法是循环。

Apache ActiveMQ Artemis 集群连接可以配置为始终以循环方式盲目负载均衡消息,而不管其他节点上是否存在任何匹配的使用者,但它们可以比这更聪明,还可以配置为仅在其他节点具有匹配的使用者时才分发消息。我们将依次查看这两个案例以及一些示例,但首先我们将讨论一般情况下如何配置集群连接。

4.1. 配置集群连接

集群连接将服务器分组到集群中,以便可以在集群的节点之间进行消息负载均衡。让我们看一下典型的集群连接。集群连接始终在 broker.xml 中的 cluster-connection 元素内定义。每个 Apache ActiveMQ Artemis 服务器可以定义零个或多个集群连接。

<cluster-connections>
   <cluster-connection name="my-cluster">
      <address></address>
      <connector-ref>netty-connector</connector-ref>
      <check-period>1000</check-period>
      <connection-ttl>5000</connection-ttl>
      <min-large-message-size>50000</min-large-message-size>
      <call-timeout>5000</call-timeout>
      <retry-interval>500</retry-interval>
      <retry-interval-multiplier>1.0</retry-interval-multiplier>
      <max-retry-interval>5000</max-retry-interval>
      <initial-connect-attempts>-1</initial-connect-attempts>
      <reconnect-attempts>-1</reconnect-attempts>
      <use-duplicate-detection>true</use-duplicate-detection>
      <message-load-balancing>ON_DEMAND</message-load-balancing>
      <max-hops>1</max-hops>
      <confirmation-window-size>32000</confirmation-window-size>
      <call-failover-timeout>30000</call-failover-timeout>
      <notification-interval>1000</notification-interval>
      <notification-attempts>2</notification-attempts>
      <discovery-group-ref discovery-group-name="my-discovery-group"/>
      <client-id></client-id>
   </cluster-connection>
</cluster-connections>

在上面的集群连接中,所有参数都已明确指定。以下是所有可用的配置选项

address

每个集群连接只应用于与指定 address 字段匹配的地址。当地址以 address 字段中指定的字符串开头时,它将与集群连接匹配。集群连接上的 address 字段也支持逗号分隔列表和排除语法 !。要防止地址与该集群连接匹配,请在集群连接地址字符串前面加上 !

在上面显示的情况下,集群连接将对发送到所有地址的消息进行负载均衡(因为它为空)。

地址可以是任何值,并且可以有多个集群连接,这些连接具有不同的 address 值,同时平衡这些地址的消息,可能平衡到不同的服务器集群。通过在不同地址上具有多个集群连接,单个 Apache ActiveMQ Artemis 服务器可以有效地同时参与多个集群。

注意不要让多个集群连接具有重叠的 address 值,例如 "europe" 和 "europe.news",因为这会导致相同的消息分发到多个集群连接,可能会导致重复的交付。

示例

  • 'eu' 匹配所有以 'eu' 开头的地址

  • '!eu' 匹配除以 'eu' 开头的地址以外的所有地址

  • 'eu.uk,eu.de' 匹配所有以 'eu.uk' 或 'eu.de' 开头的地址

  • 'eu,!eu.uk' 匹配所有以 'eu' 开头的地址,但不匹配以 'eu.uk' 开头的地址

  • 地址排除始终优先于地址包含。

  • 集群连接上的地址匹配不支持通配符匹配。

connector-ref

这是将发送到集群中其他节点的连接器,以便它们具有正确的集群拓扑。

此参数是必需的。

check-period

用于检查集群连接是否未能从另一台服务器接收 ping 的周期(以毫秒为单位)。默认值为 30000。

connection-ttl

如果集群连接停止从集群中的特定节点接收消息,则它应该保持活动状态的持续时间。默认值为 60000。

min-large-message-size

如果消息大小(以字节为单位)大于此值,那么在通过网络发送到其他集群成员时,它将被拆分为多个段。默认值为 102400。

call-timeout

当通过集群连接发送数据包并且它是阻塞调用时,例如,对于确认,它将等待多长时间(以毫秒为单位)以等待回复,然后再抛出异常。默认值为 30000。

retry-interval

我们之前提到过,在内部,集群连接会导致在集群的节点之间创建桥梁。如果创建了集群连接,并且目标节点尚未启动,或者正在重新启动,那么来自其他节点的集群连接将不断尝试连接到目标节点,直到它恢复,就像桥梁一样。

此参数确定两次重试尝试之间的间隔(以毫秒为单位)。它与桥梁上的 retry-interval 具有相同的含义(如 核心桥梁 中所述)。

此参数是可选的,其默认值为 500 毫秒。

retry-interval-multiplier

这是一个乘数,用于在每次重新连接尝试后增加 retry-interval,默认值为 1。

max-retry-interval

重试的最大延迟(以毫秒为单位)。默认值为 2000。

initial-connect-attempts

系统最初尝试连接集群中节点的次数。如果达到最大重试次数,该节点将被视为永久性宕机,系统将不再将消息路由到该节点。默认值为 -1(无限重试)。

reconnect-attempts

系统尝试重新连接到集群中节点的次数。如果达到最大重试次数,该节点将被视为永久性宕机,系统将停止将消息路由到该节点。默认值为 -1(无限重试)。

use-duplicate-detection

在内部,集群连接使用桥接器来链接节点,桥接器可以配置为在转发每个消息时添加一个重复 ID 属性。如果桥接器的目标节点崩溃然后恢复,源节点可能会重新发送消息。通过启用重复检测,任何重复消息在目标节点接收时都会被过滤掉并忽略。

此参数与桥接器上的 use-duplicate-detection 具有相同的含义。有关重复检测的更多信息,请参阅 重复检测。默认值为 true

message-load-balancing

此参数决定是否/如何将消息分配到集群中的其他节点。它可以是以下四个值之一 - OFFSTRICTOFF_WITH_REDISTRIBUTIONON_DEMAND(默认值)。此参数替换了已弃用的 forward-when-no-consumers 参数。

如果设置为 OFF,则消息永远不会转发到集群中的另一个节点。

如果设置为 STRICT,则每个传入消息都将进行循环轮询,即使集群中其他节点上的相同队列根本没有消费者,或者它们可能具有与消息过滤器(选择器)不匹配的消费者。请注意,即使将此参数设置为 STRICT,Apache ActiveMQ Artemis 不会 将消息转发到其他节点,除非其他节点上存在相同名称的队列。使用 STRICT 类似于将遗留的 forward-when-no-consumers 参数设置为 true

如果设置为 ON_DEMAND,则 Apache ActiveMQ Artemis 仅在将消息转发到的地址具有具有消费者的队列,并且如果这些消费者具有消息过滤器(选择器),这些选择器中至少有一个必须与消息匹配时,才会将消息转发到集群中的其他节点。使用 ON_DEMAND 类似于将遗留的 forward-when-no-consumers 参数设置为 false

如果设置为 OFF_WITH_REDISTRIBUTION,则类似于 'OFF',消息最初不会被路由到集群中的其他节点。但是,如果配置了 重新分配,它可以以正常方式转发消息。这样,本地消费者将始终具有优先级。

请记住,这种消息转发/平衡被称为“初始分配”。它不同于 重新分配,将在 下面讨论

默认值为 ON_DEMAND

max-hops

当集群连接决定可能对其进行负载均衡消息的节点集时,这些节点不必通过集群连接直接连接到它。Apache ActiveMQ Artemis 可以配置为还将消息负载均衡到可能仅通过其他 Apache ActiveMQ Artemis 服务器作为链中的中介间接连接到它的节点。

这允许 Apache ActiveMQ Artemis 在更复杂的拓扑结构中配置,同时仍然提供消息负载均衡。我们将在本章后面进一步讨论这一点。

此参数的默认值为 1,这意味着消息仅负载均衡到直接连接到此服务器的其他 Apache ActiveMQ Artemis 服务器。此参数是可选的。

confirmation-window-size

用于从连接到的服务器发送确认的窗口的大小(以字节为单位)。因此,一旦服务器收到 confirmation-window-size 字节,它就会通知其客户端,默认值为 1048576。值为 -1 表示没有窗口。

producer-window-size

用于通过集群连接进行生产者流量控制的大小。默认值为 1MB。

call-failover-timeout

类似于 call-timeout,但用于在故障转移尝试期间进行调用时。默认值为 -1(没有超时)。

notification-interval

集群连接在附加到集群时应该广播自身的频率(以毫秒为单位)。默认值为 1000。

notification-attempts

集群连接在连接到集群时应该广播自身的次数。默认值为 2。

discovery-group-ref

此参数决定使用哪个发现组来获取此集群连接将连接到的集群中其他服务器的列表。

或者,如果您希望集群连接使用静态服务器列表进行发现,则可以按以下方式执行。

<cluster-connection name="my-cluster">
   ...
   <static-connectors>
      <connector-ref>server0-connector</connector-ref>
      <connector-ref>server1-connector</connector-ref>
   </static-connectors>
</cluster-connection>

在这里,我们定义了 2 个服务器,我们知道至少其中一个服务器可用。集群中可能还有更多服务器,但一旦建立了初始连接,这些服务器将通过其中一个连接器进行发现。

client-id

用于集群连接的可选标识符。这可以帮助识别远程代理上的连接(例如,通过 Web 控制台)。默认值为空(即未设置)。

4.2. 集群用户凭据

在创建集群节点之间的连接以形成集群连接时,Apache ActiveMQ Artemis 使用在 broker.xml 中定义的集群用户和集群密码。

<cluster-user>ACTIVEMQ.CLUSTER.ADMIN.USER</cluster-user>
<cluster-password>CHANGE ME!!</cluster-password>

务必将这些值从默认值更改,否则远程客户端将能够使用默认值连接到服务器。如果它们没有从默认值更改,Apache ActiveMQ Artemis 将检测到这一点,并在每次启动时都会向您发出警告。

5. 客户端负载均衡

使用 Apache ActiveMQ Artemis 客户端负载均衡,使用单个会话工厂创建的后续会话可以连接到集群中的不同节点。这允许会话平滑地分布在整个集群节点上,而不是“集中”在任何特定节点上。

客户端工厂将使用的负载均衡策略是可配置的。Apache ActiveMQ Artemis 提供四种开箱即用的负载均衡策略,您还可以实现自己的策略并使用它。

开箱即用的策略是

  • 循环轮询。使用此策略,第一个节点是随机选择的,然后每个后续节点按顺序选择,顺序相同。

    例如,节点可能会按照以下顺序选择:B、C、D、A、B、C、D、A、B 或 D、A、B、C、D、A、B、C、D 或 C、D、A、B、C、D、A、B、C。

    使用 org.apache.activemq.artemis.api.core.client.loadbalance.RoundRobinConnectionLoadBalancingPolicy 作为 <connection-load-balancing-policy-class-name>

  • 随机。使用此策略,每个节点都是随机选择的。

    使用 org.apache.activemq.artemis.api.core.client.loadbalance.RandomConnectionLoadBalancingPolicy 作为 <connection-load-balancing-policy-class-name>

  • 随机粘性。使用此策略,第一个节点是随机选择的,然后在后续连接中重复使用。

    使用 org.apache.activemq.artemis.api.core.client.loadbalance.RandomStickyConnectionLoadBalancingPolicy 作为 <connection-load-balancing-policy-class-name>

  • 第一个元素。使用此策略,始终返回“第一个”(即第 0 个)节点。

    使用 org.apache.activemq.artemis.api.core.client.loadbalance.FirstElementConnectionLoadBalancingPolicy 作为 <connection-load-balancing-policy-class-name>

您还可以通过实现 org.apache.activemq.artemis.api.core.client.loadbalance.ConnectionLoadBalancingPolicy 接口来实现自己的策略。

指定使用哪个负载均衡策略取决于您是使用 JMS 还是核心 API。如果您没有指定策略,则将使用默认策略,即 org.apache.activemq.artemis.api.core.client.loadbalance.RoundRobinConnectionLoadBalancingPolicy

可以在 URI 上设置参数 connectionLoadBalancingPolicyClassName 来配置要使用的负载均衡策略。

tcp://127.0.0.1:61616?connectionLoadBalancingPolicyClassName=org.apache.activemq.artemis.api.core.client.loadbalance.RandomConnectionLoadBalancingPolicy

可以通过两种方式之一确定工厂负载均衡的服务器集。

  • 在 URL 中显式指定服务器。这还需要在 URL 上将 useTopologyForLoadBalancing 参数设置为 false

  • 使用发现。这是默认行为。

6. 显式指定集群成员

有时您希望更明确地定义集群,即控制集群中哪些服务器相互连接。这通常用于形成非对称集群,例如链式集群或环形集群。这只能使用静态连接器列表来完成,配置如下。

<cluster-connection name="my-cluster">
   <address/>
   <connector-ref>netty-connector</connector-ref>
   <retry-interval>500</retry-interval>
   <use-duplicate-detection>true</use-duplicate-detection>
   <message-load-balancing>STRICT</message-load-balancing>
   <max-hops>1</max-hops>
   <static-connectors allow-direct-connections-only="true">
      <connector-ref>server1-connector</connector-ref>
   </static-connectors>
</cluster-connection>

在此示例中,我们设置了属性 allow-direct-connections-only,这意味着此服务器可以创建集群连接的唯一服务器是 server1-connector。这意味着您可以显式创建您想要的任何集群拓扑结构。

7. 消息重新分配

集群的另一个重要部分是消息重新分配。前面我们了解了服务器端消息负载均衡如何在集群中进行循环轮询消息。如果 message-load-balancingOFFON_DEMAND,则消息不会被转发到没有匹配消费者的节点。这很好,并确保消息不会被移动到没有消费者来消费它们的队列。但是,有一个情况它没有解决:如果消费者在将消息发送到节点后关闭,会发生什么?如果队列上没有消费者,则消息将不会被消费,我们就会遇到 饥饿 情况。

这就是消息重新分配的用武之地。通过消息重新分配,可以配置 Apache ActiveMQ Artemis 自动将消息从没有消费者或过滤器与消息不匹配的消费者的队列中 重新分配。消息将被重新路由到集群中的其他节点,这些节点具有匹配的消费者。要启用此功能,message-load-balancing 必须为 ON_DEMANDOFF_WITH_REDISTRIBUTION

消息重新分配可以配置为在检测到需要重新分配后立即启动,或者在重新分配之前等待可配置的延迟。默认情况下,消息重新分配被禁用。

消息重新分配可以在每个地址的基础上进行配置,方法是在地址设置中指定重新分配延迟。有关配置地址设置的更多信息,请参阅 通过地址设置配置地址和队列

以下是 broker.xml 中的地址设置片段,显示了如何为一组队列启用消息重新分配。

<address-settings>
   <address-setting match="#">
      <redistribution-delay>0</redistribution-delay>
   </address-setting>
</address-settings>

上面的 address-settings 块将为绑定到任何地址的任何队列设置 redistribution-delay0。因此,上面将为所有地址启用立即(无延迟)重新分配。

属性 match 可以是精确匹配,也可以是符合 Apache ActiveMQ Artemis 通配符语法的字符串(在 通配符语法 中描述)。

元素 redistribution-delay 定义了检测到需要重新分配和实际尝试重新分配之间的时间延迟(以毫秒为单位)。延迟为零表示消息将立即重新分配。值为 -1 表示消息永远不会被重新分配。默认值为 -1

在重新分配之前引入延迟通常是有意义的,因为消费者关闭但另一个消费者很快在同一队列上创建是一个常见情况,在这种情况下,您可能不希望立即重新分配,因为新的消费者很快就会到达。

8. 集群拓扑

Apache ActiveMQ Artemis 集群可以以多种不同的拓扑结构连接在一起,这里我们来考虑两种最常见的拓扑结构。

8.1. 对称集群

对称集群可能是最常见的集群拓扑结构。

在对称集群中,集群中的每个节点都与集群中的所有其他节点相连。换句话说,集群中的每个节点到任何其他节点的距离不超过一跳。

为了形成一个对称集群,集群中的每个节点都定义一个集群连接,并将属性 max-hops 设置为 1。通常,集群连接会使用服务器发现来了解它应该连接到集群中的哪些其他服务器,尽管也可以在集群连接中明确定义每个目标服务器,例如,如果您的网络上不可用 UDP。

在对称集群中,每个节点都知道所有其他节点上存在的所有队列以及它们的消费者。利用这些信息,它可以确定如何平衡负载并在节点之间重新分配消息。

创建对称集群时,请不要忘记 此警告

8.2. 链式集群

在链式集群中,集群中的每个节点并不直接连接到集群中的所有节点,而是形成一个链,链的两端各有一个节点,其他所有节点只连接到链中前一个和下一个节点。

例如,一个由节点 A、B 和 C 组成的三节点链。节点 A 托管在一个网络中,并有许多生产者客户端连接到它发送订单消息。由于公司策略,订单消费者客户端需要托管在另一个网络中,而该网络只能通过第三个网络访问。在这种设置中,节点 B 充当一个中介,没有生产者或消费者。任何到达节点 A 的消息都会转发到节点 B,然后节点 B 会将消息转发到节点 C,在那里可以被消费。节点 A 无需直接连接到 C,但所有节点仍然可以作为集群的一部分。

要以这种方式设置集群,节点 A 将定义一个连接到节点 B 的集群连接,而节点 B 将定义一个连接到节点 C 的集群连接。在这种情况下,我们只需要一个方向的集群连接,因为我们只从节点 A->B->C 移动消息,而不会从 C->B->A 移动消息。

对于这种拓扑结构,我们将 max-hops 设置为 2。值为 2 时,节点 C 上存在哪些队列和消费者的信息会从节点 C 传播到节点 B,再传播到节点 A。然后,节点 A 就会知道在消息到达时将消息分发到节点 B,即使节点 B 本身没有消费者,它也会知道再跳一跳就到达了节点 C,而节点 C 有消费者。

8.3. 缩容

Apache ActiveMQ Artemis 支持缩容集群,且不会丢失消息(即使对于非持久消息也是如此)。这在某些环境(例如云)中特别有用,在这些环境中,集群的大小可能相对频繁地变化。当扩大集群(即添加节点)时,不会有丢失消息的风险,但是当缩容集群(即删除节点)时,这些节点上的消息将会丢失,除非代理将它们发送到集群中的另一个节点。Apache ActiveMQ Artemis 可以被配置为执行此操作。

要启用此行为,请在 live-only ha-policy 中配置 scale-down,例如:

<ha-policy>
   <live-only>
      <scale-down>
         <enabled>true</enabled>
         <discovery-group-ref discovery-group-name="my-discovery-group"/>
      </scale-down>
   </live-only>
</ha-policy>

如果 scale-down/enabledtrue,则当服务器正常关闭(即没有崩溃地停止)时,它会找到集群中的另一个节点,并将所有消息(持久消息和非持久消息)发送到该节点。这些消息按顺序处理,并进入另一个节点上相应队列的末尾(就像这些消息是第一次从外部客户端发送的一样)。

缩容操作的目标可以通过几种不同的方式配置。上面的示例使用 discovery-group-ref 来引用一个 discovery-group,该 discovery-group 将用于查找目标代理。这应该与您的 cluster-connection 引用的 discovery-group 相同。您也可以指定一个静态的 connector 元素列表,例如:

<connectors>
  ...
  <connector name="server0-connector">tcp://server0:61616</connector>
</connectors>
...
<ha-policy>
  <live-only>
    <scale-down>
      <enabled>true</enabled>
      <connectors>
        <connector-ref>server0-connector</connector-ref>
      </connectors>
    </scale-down>
  </live-only>
</ha-policy>

也可以指定 group-name。如果指定了该值,则消息将只发送到使用与正在关闭的服务器相同的 group-name 的集群中的另一个节点,例如:

<ha-policy>
   <live-only>
      <scale-down>
         <enabled>true</enabled>
         <group-name>my-group</group-name>
         <discovery-group-ref discovery-group-name="my-discovery-group"/>
      </scale-down>
   </live-only>
</ha-policy>

如果集群节点以不同的 group-name 值分组在一起,请注意。如果一个组中的所有节点都关闭,则来自该节点/组的消息将丢失。