Apache ActiveMQ Artemis 连接路由器允许将传入的客户端连接分布到多个目标代理。目标代理被分组到中,连接路由器使用密钥根据策略从代理池中选择目标代理。

1. 目标代理

目标代理是可以接受传入客户端连接的代理,可以是本地或远程的。本地目标是表示托管连接路由器的相同代理的特殊目标。远程目标是另一个可到达的代理。

2. 密钥

连接路由器使用密钥来选择目标代理。它是一个从传入客户端连接中检索的字符串,支持的密钥类型有

CLIENT_ID

是 JMS 客户端 ID。

SNI_HOST

是客户端在 TLS 协议的 SNI 扩展中指示的主机名。

SOURCE_IP

是客户端的源 IP 地址。

USER_NAME

是客户端指示的用户名。

ROLE_NAME

是与连接的已认证用户关联的角色。

3. 池

池是一组目标代理,定期检查其状态。它仅在池处于活动状态时提供准备好的目标代理列表以分发传入的客户端连接。当池中达到由 quorum-size 参数定义的最小数量的目标代理时,池变为活动状态。当它处于非活动状态时,它不会提供任何目标,从而避免在启动或重新启动后出现奇怪的分配。将本地代理包含在目标池中允许托管路由器的代理也接受传入的客户端连接。默认情况下,池不包含本地代理,要将其作为目标包含在内,local-target-enabled 参数必须为 true。池有三种类型:集群池发现池静态池

3.1. 集群池

集群池使用集群连接来获取要添加的目标代理。让我们看一下 broker.xml 中使用集群连接的集群池示例

<pool>
  <cluster-connection>cluster1</cluster-connection>
</pool>

3.2. 发现池

发现池使用发现组来发现要添加的目标代理。让我们看一下 broker.xml 中使用发现组的发现池示例

<pool>
    <discovery-group-ref discovery-group-name="dg1"/>
</pool>

3.3. 静态池

静态池使用静态连接列表来定义要添加的目标代理。让我们看一下 broker.xml 中使用静态连接列表的静态池示例

<pool>
    <static-connectors>
        <connector-ref>connector1</connector-ref>
        <connector-ref>connector2</connector-ref>
        <connector-ref>connector3</connector-ref>
    </static-connectors>
</pool>

3.4. 定义池

池由 pool 元素定义,其中包含以下项目

  • username 元素定义连接到目标代理的用户名;

  • password 元素定义连接到目标代理的密码;

  • check-period 元素定义检查目标代理的频率,以毫秒为单位,默认值为 5000

  • quorum-size 元素定义激活池所需的最小数量的准备就绪的目标,默认值为 1

  • quorum-timeout 元素定义获取最小数量的准备就绪的目标的超时时间,以毫秒为单位,默认值为 3000

  • local-target-enabled 元素定义池是否必须包含本地目标,默认值为 false

  • cluster-connection 元素定义集群连接,该连接由集群池使用。

  • static-connectors 元素定义静态池使用的静态连接列表;

  • discovery-group 元素定义发现组,该组由发现池使用。

让我们看一下 broker.xml 中的池示例

<pool>
    <quorum-size>2</quorum-size>
    <check-period>1000</check-period>
    <local-target-enabled>true</local-target-enabled>
    <static-connectors>
        <connector-ref>connector1</connector-ref>
        <connector-ref>connector2</connector-ref>
        <connector-ref>connector3</connector-ref>
    </static-connectors>
</pool>

4. 策略

策略定义如何从池中选择代理,并允许密钥值转换。包含的策略有

FIRST_ELEMENT

从池中选择第一个准备就绪的目标代理。它用于根据其序列顺序定义的优先级选择准备就绪的目标代理,即假设有 2 个目标代理,该策略仅在第一个目标代理未准备好时选择第二个目标代理。

ROUND_ROBIN

从池中依次选择目标,此策略对于均匀分布很有用;

CONSISTENT_HASH

根据密钥选择目标。此策略始终为相同密钥选择相同的目标代理,直到它从池中删除。

LEAST_CONNECTIONS

选择活动连接最少的目标。此策略可帮助您维护活动连接与目标代理之间的均衡分布。

CONSISTENT_HASH_MODULO` 将密钥值转换为从 0 到 N-1 的数字,它采用单个 `modulo

属性来配置边界 N。一个用例是跨 N 个代理集群进行 CLIENT_ID 分片。通过一致的哈希 % N 转换,每个客户端 ID 都可以专属于集群中的一个代理。

策略由 policy 元素定义。让我们看一下 broker.xml 中的策略示例

<policy name="FIRST_ELEMENT"/>

5. 缓存

连接路由器提供一个带超时的缓存,以提高所选目标代理的粘性,只要目标代理存在于缓存中且已准备好,就会为密钥值返回相同的目标代理。因此,启用缓存的连接路由器不会严格遵循配置的策略。默认情况下,缓存未启用。

缓存由 cache 元素定义,其中包含以下项目

  • persisted 元素定义缓存是否必须持久保存条目,默认值为 false

  • timeout 元素定义删除条目之前的超时时间,以毫秒为单位,设置 0 将禁用超时,默认值为 0

让我们看一下 broker.xml 中的缓存示例

<cache>
  <persisted>true</persisted>
  <timeout>60000</timeout>
</cache>

6. 定义连接路由器

连接路由器由 connection-router 元素定义,其中包含以下项目

  • name 属性定义连接路由器的名称,用于从接收器引用路由器;

  • key-type 元素定义选择目标代理的密钥类型,支持的值有:CLIENT_IDSNI_HOSTSOURCE_IPUSER_NAMEROLE_NAME,默认值为 SOURCE_IP,有关更多详细信息,请参阅密钥

  • key-filter 元素定义正则表达式,用于过滤解析后的密钥值

  • local-target-filter 元素定义正则表达式,用于匹配必须返回本地目标的密钥值密钥值可以等于特殊字符串 NULL,如果密钥的值未定义或与 key-filter 不匹配;

  • pool 元素定义将目标代理分组的池,请参阅

  • policy 元素定义用于从池中选择目标代理的策略,请参阅策略

让我们看一下 broker.xml 中的一些连接路由器示例

<connection-routers>
    <connection-router name="local-partition">
         <key-type>CLIENT_ID</key-type>
         <key-filter>^.{3}</key-filter>
         <local-target-filter>^FOO.*</local-target-filter>
    </connection-router>
    <connection-router name="simple-router">
        <policy name="FIRST_ELEMENT"/>
        <pool>
            <static-connectors>
                <connector-ref>connector1</connector-ref>
                <connector-ref>connector2</connector-ref>
                <connector-ref>connector3</connector-ref>
            </static-connectors>
        </pool>
    </connection-router>
    <connection-router name="consistent-hash-router">
        <key-type>USER_NAME</key-type>
        <local-target-filter>admin</local-target-filter>
        <policy name="CONSISTENT_HASH"/>
        <pool>
            <local-target-enabled>true</local-target-enabled>
            <discovery-group-ref discovery-group-name="dg1"/>
        </pool>
    <policy name="CONSISTENT_HASH"/>
    </connection-router>
    <connection-router name="evenly-balance">
      <key-type>CLIENT_ID</key-type>
      <key-filter>^.{3}</key-filter>
      <policy name="LEAST_CONNECTIONS"/>
      <pool>
        <username>guest</username>
        <password>guest</password>
        <discovery-group-ref discovery-group-name="dg2"/>
      </pool>
    </connection-router>
</connection-routers>

7. 密钥值

密钥值是从传入的客户端连接中检索的。如果传入的客户端连接没有使用密钥类型的 value,则密钥值将设置为特殊字符串 NULL。如果传入的客户端连接具有使用密钥类型的 value,则检索到的密钥值可以使用 key-filterpolicy 依次操作。如果定义了 key-filter 并且过滤器无法匹配,则该值将设置为特殊字符串 NULL。如果定义了具有密钥转换的 policy,则密钥值将设置为转换后的 value。

8. 连接路由器工作流程

连接路由器工作流程包含以下步骤

  • 从传入连接中检索密钥值

  • 如果密钥值与本地过滤器匹配,则返回本地目标代理;

  • 委托给

  • 如果缓存的目标代理已准备好,则返回它;

  • 从池中获取准备就绪/活动的 target brokers;

  • 使用策略选择一个 target broker;

  • 将选定的 broker 添加到缓存中;

  • 返回选定的 broker。

让我们看一下连接路由器工作流程的流程图:连接路由器工作流程

9. 数据重力

第一个路由器配置:local-partition,演示了最简单的用例,即通过将应用程序数据的子集限制到给定代理来保留 数据重力。每个代理都有一组它将专有服务或拒绝的密钥。如果代理位于轮询负载均衡器后面或完全了解代理 URL,那么 他们的 代理最终将做出响应。local-target-filter 正则表达式决定了为保留应用程序的 数据重力 最合适的划分粒度。

挑战在于在所有相关应用程序连接中提供一致的密钥

数据重力 的概念试图捕捉这样一种现实,即虽然多个应用程序共享地址,但最好将相关的地址及其数据保存在单个代理上。通常,应用程序应该 连接 到数据,而不是将数据移动到应用程序连接的任何代理。当数据量(积压)很大时,尤其如此,为了遵循消费者而移动数据的成本超过了将数据传递到应用程序的成本。使用“数据重力”思维,操作人员不太关心连接数量,而更关心应用程序及其需要交互的地址。

10. 重定向

Apache ActiveMQ Artemis 为支持的客户端提供原生重定向功能,并为其他客户端提供新的管理 API。原生重定向可以在每个接收器上启用,仅适用于 AMQP、CORE 和 OPENWIRE 客户端。带有 `router` URL 参数的接收器将重定向传入的连接。`router` URL 参数指定要使用的连接路由器的名称,例如,以下接收器将使用名为 `simple-router` 的连接路由器重定向传入的 CORE 客户端连接。

<acceptor name="artemis">tcp://0.0.0.0:61616?router=simple-router;protocols=CORE</acceptor>

10.1. 原生重定向序列

支持原生重定向的客户端连接到启用了重定向的接收器。接收器向客户端发送目标代理进行重定向(如果已准备就绪),并关闭连接。客户端连接到目标代理(如果之前已收到目标代理,否则在断开连接后再次连接到启用了重定向的接收器)。

Native Redirect Sequence

10.2. 管理 API 重定向序列

不支持原生重定向的客户端查询连接路由器的管理 API 以获取目标代理进行重定向。如果 API 返回目标代理,客户端将连接到该代理,否则客户端将再次查询 API。

Management API Redirect Sequence