Apache ActiveMQ Artemis 透明地支持包含数百万条消息的巨大队列,而服务器运行时的内存有限。

在这种情况下,不可能在任何时候将所有队列都存储在内存中,因此 Apache ActiveMQ Artemis 透明地将消息*分页*进出内存,以便按需使用它们,从而允许在低内存占用下创建巨大的队列。

当某个地址中所有内存中消息的大小超过配置的最大大小时,Apache ActiveMQ Artemis 将开始将消息分页到磁盘。

Artemis 的默认配置中,目标具有分页功能。

1. 页面文件

消息按地址存储在文件系统中。每个地址都有一个单独的文件夹,其中消息存储在多个文件(页面文件)中。每个文件将包含最多配置的最大大小 (page-size-bytes) 的消息。系统将根据需要浏览这些文件,并在所有消息被确认到该点之前删除页面文件。

浏览器将通过页面游标系统读取。

具有选择器的消费者也将浏览页面文件,并将忽略不匹配条件的消息。

当您有一个队列,而消费者使用非常严格的选择器过滤队列时,您可能会遇到在从队列中消费消息之前无法从分页中读取更多数据的状况。

示例:在一个消费者中,您将选择器设为 'color="red"',但您只有一个颜色为红色的 100 万条消息在蓝色消息之后,您将无法消费红色消息,直到您消费完蓝色消息。

这与浏览不同,因为我们将“浏览”整个队列寻找消息,同时在“取消分页”消息时向队列提供消息。

1.1. 配置

您可以在 broker.xml 中配置分页文件夹的位置。

  • paging-directory 页面文件存储的位置。Apache ActiveMQ Artemis 将为每个正在分页的地址在该配置的位置下创建一个文件夹。默认值为 data/paging

2. 分页模式

一旦传递到某个地址的消息超过配置的大小,该地址将单独进入分页模式。如果 max-size-bytes == 0 或 max-size-messages == 0,则地址将始终使用分页来路由消息。

分页是针对每个地址单独进行的。如果您为某个地址配置了 max-size-bytes 或 max-messages,这意味着每个匹配的地址都将具有您指定的最大大小。但这并不意味着所有匹配地址的总大小限制为 max-size-bytes。请使用 global-max-sizeglobal-max-messages 来实现这一点!

2.1. 配置

配置在 broker.xml 中的地址设置中完成。

<address-settings>
   <address-setting match="jms.someaddress">
      <max-size-bytes>104857600</max-size-bytes>
      <max-size-messages>1000</max-size-messages>
      <page-size-bytes>10485760</page-size-bytes>
      <address-full-policy>PAGE</address-full-policy>
      <page-limit-bytes>10G</page-limit-bytes>
      <page-limit-messages>1000000</page-limit-messages>
      <page-full-policy>FAIL</page-full-policy>
   </address-setting>
</address-settings>
管理地址 设置不能更改或覆盖,即不允许管理消息进行分页/阻塞/失败,它们被认为是内部代理管理机制。管理地址 的内存占用在评估是否达到 global-max-size 时不会被考虑,并且不会导致其他非管理地址触发配置的 address-full-policy

以下是地址设置中可用参数的列表。

属性名称 描述 默认值

max-size-bytes

地址在进入分页模式之前可以拥有的最大内存。

-1(禁用)

max-size-messages

地址在进入分页模式之前可以拥有的最大消息数。

-1(禁用)

page-size-bytes

分页系统中使用的每个页面文件的大小

10MB

address-full-policy

这必须设置为 PAGE 才能启用分页。如果该值为 PAGE,则后续消息将分页到磁盘。如果该值为 DROP,则后续消息将被静默丢弃。如果该值为 FAIL,则消息将被丢弃,并且客户端消息生产者将收到异常。如果该值为 BLOCK,则客户端消息生产者在尝试发送后续消息时将被阻塞。

PAGE

max-read-page-messages

代理可以每队列读取到内存中的分页消息的最大数量。默认值为 -1,这意味着没有限制。

-1(禁用)

max-read-page-bytes

可以用于将分页消息读取到内存中的最大内存(以字节为单位),每队列。在应用此限制时,代理会同时考虑当前正在传递的消息和准备好传递给消费者的消息。默认值为 2 * page-size(通常为 20 MB)。如果消费者确认消息的速度很慢,您可以增加默认值,以确保内存不会被等待确认的消息消耗,从而导致代理消息短缺。

2 * page-size-bytes

prefetch-page-messages

代理可以从磁盘读取到内存中的分页消息的数量,每队列。默认值取自 max-read-page-messages,通常为 -1,这意味着没有限制。

max-read-page-messages

prefetch-page-bytes

代理可以从磁盘读取到内存中的分页消息的数量,每队列。默认值取自 max-read-page-messages,通常为 -1,这意味着没有限制。

如果未定义,则为 max-read-page-bytes

page-limit-bytes

进入分页模式后,系统允许多少数据进入。请注意,这将被内部转换为页面数。

page-limit-messages

进入分页模式后,系统允许多少消息进入分页。

page-full-policy

有效结果为 DROP 或 FAIL。这告诉在系统达到 page-limit-bytespage-limit-messages 后如何处理分页。

使用 JDBC 存储时,使用的有效 page-size-bytes 限制为 jdbc-max-page-size-bytes,在 JDBC 存储部分配置。

2.2. max-size-bytes 和 max-size-messages 同时使用

可以同时定义 max-size-messages(作为最大消息数)和 max-messages-size(作为地址使用的最大估计内存)。配置的策略将根据第一个达到标记的值开始。

2.2.1. 从页面读取的最大值

max-read-page-messagesmax-read-page-bytesprefetch-page-messagesprefetch-page-bytes 用于控制从分页文件读取到队列。只要满足所有这些限制,代理就会添加消息。

如果所有这些值都设置为 -1,则代理将继续读取消息,只要消费者正在请求更多消息。但是,这将使代理无法受到消费者分配大型事务或未启用流量控制的消费者的保护。

3. 全局最大大小

除了地址上的 max-size-bytes 之外,您还可以设置主配置上的 global-max-size。如果您在分页上设置了 max-size-bytes = -1,则 global-max-size 仍然可以使用。

4. 全局最大消息数

您也可以在主配置上指定 global-max-messages,指定系统在进入配置的已满策略模式之前可以接受多少条消息。

当您拥有的消息数量超过配置的 global-max-size 时,任何新产生的消息都将使该目标经历其分页策略。

global-max-size 计算为 Java 虚拟机可用最大内存的一半,除非在 broker.xml 配置中指定。

默认情况下,global-max-messages = -1,这意味着它已禁用。

5. 丢弃消息

地址也可以配置为在达到最大大小时直接丢弃消息,而不是对消息进行分页。

要执行此操作,只需在地址设置中将 address-full-policy 设置为 DROP

6. 丢弃消息并向生产者抛出异常

地址也可以配置为在达到最大大小时丢弃消息,并在客户端发生错误时抛出异常。在客户端发生错误时抛出异常。

要执行此操作,只需在地址设置中将 address-full-policy 设置为 FAIL

7. 阻塞生产者

地址也可以配置为在达到最大大小时阻止生产者发送更多消息,从而防止服务器上的内存耗尽,而不是对消息进行分页。

当服务器上释放内存时,生产者将自动解除阻塞,并能够继续发送。

要执行此操作,只需在地址设置中将 address-full-policy 设置为 BLOCK

在默认配置中,所有地址都配置为在地址中包含 10 MiB 数据后阻止生产者。

8. 多个多播队列地址的注意事项

当一条消息被路由到绑定了多个多播队列的地址时(例如,主题中的 JMS 订阅),内存中只有一份消息副本。每个队列只处理对该消息的引用。因此,只有在所有引用该消息的队列都已传递消息后才会释放内存。

如果您有一个单一的延迟订阅,则整个地址将受到 IO 性能的影响,因为所有队列都将通过分页系统中的额外存储发送消息。

例如

  • 一个地址有 10 个多播队列

  • 其中一个队列不传递其消息(可能是因为消费者速度很慢)。

  • 消息不断到达地址,并启动分页。

  • 其他 9 个队列为空,即使已发送消息。

在此示例中,所有其他 9 个队列都将从页面系统中消费消息。如果这是不希望出现的状态,这可能会导致性能问题。

9. 监控磁盘

代理可以配置为对磁盘进行扫描,以确定磁盘是否超过配置的限制。由于磁盘是数据完整性的关键基础设施,因此如果磁盘空间不足,代理将自动关闭自身。配置限制允许代理对向代理发送消息的客户端实施流量控制,以确保磁盘永远不会完全填满。

如果用于发送消息的协议不支持流量控制(例如 STOMP),则会抛出异常,并且客户端的连接将被断开,因此它将无法再发送消息并消耗磁盘空间。

9.1. 最大磁盘使用量

可以通过 max-disk-usage 配置对使用的最大磁盘空间的限制。这是一个**百分比**值,表示磁盘使用的比例。例如,如果磁盘容量为 500GiB 且 max-disk-usage50,则代理将在使用 250GiB 磁盘空间后开始阻塞生产者。

9.2. 最小磁盘剩余空间

可以通过 min-disk-free 配置对最小磁盘剩余空间的限制。这指的是一个具体的数值,而不是像 max-disk-usage 那样使用百分比。例如,如果磁盘容量为 500GiB 且 min-disk-free100GiB,则代理将在使用 400GiB 磁盘空间后开始阻塞生产者。

如果同时配置了 max-disk-usagemin-disk-free,则 min-disk-free 将具有优先级。

10. 页面同步超时时间

页面会定期同步,同步周期通过 page-sync-timeout(以纳秒为单位)配置。当使用 NIO 日志时,默认情况下它与 journal-buffer-timeout 的值相同。当使用 ASYNCIO 时,默认值应为 3333333

11. 分页消息的内存使用情况

系统应至少将一个分页文件保存在内存中,以便提前缓存读取消息。此外,每个活动的订阅都可以在内存中保留一个分页文件。因此,如果你的系统有太多队列,建议将页面大小降至最低。

12. 页面限制和页面已满策略

2.28.0 版本开始,可以配置对分页数据量的限制。这是为了防止单个目标在消费者消失的情况下占用整个磁盘。

你可以在地址设置中配置 page-limit-bytespage-limit-messages,以及 page-full-policy,限制在分页中记录的数据量。

如果将 page-full-policy 配置为 DROP,消息将被简单地丢弃,而客户端不会收到任何异常。如果将其配置为 FAIL,生产者将收到一个 JMS 异常以表示错误情况。

page-limit-bytes 用于识别内部的页面文件最大数量(即 page-limit-bytes / page-size-bytes),然后将其与当前的页面文件数量进行比较。如果配置了 page-limit-bytes,它必须等于或大于 page-size-bytes,否则将导致立即阻塞。如果从 page-limit-bytes 确定的限制(转换为页面数量后)小于存储中的当前页面文件数量,则分页将根据 page-full-policy 阻塞,直到当前页面文件数量降至小于或等于计算出的文件限制为止。一旦页面文件数量超过由 page-limit-bytes 确定的值(page-limit-bytes / page-size-bytes),它将再次被阻塞。

13. 示例

请参阅 分页示例,它展示了如何在 Apache ActiveMQ Artemis 中使用分页。