Apache ActiveMQ Artemis 附带两种持久化选项。文件日志,高度优化消息传递用例,并提供出色的性能,以及 JDBC 存储,它使用 JDBC 连接到您选择的数据库。

1. 文件日志(默认)

文件日志是 *仅追加* 的日志。它由磁盘上的一组文件组成。每个文件预先创建为固定大小,并最初填充有填充。当在服务器上执行操作时,例如添加消息、更新消息、删除消息,记录会被追加到日志中。当一个日志文件已满时,我们就会转到下一个文件。

由于记录只是追加的,即添加到日志的末尾,因此我们最大限度地减少了磁盘磁头移动,即我们最大限度地减少了随机访问操作,而随机访问操作通常是磁盘上最慢的操作。

使文件大小可配置意味着可以选择最佳大小,即使每个文件适合一个磁盘柱面。现代磁盘拓扑结构很复杂,我们无法控制文件映射到的柱面,因此这不是一项精确的科学。但通过最大限度地减少文件使用的磁盘柱面数量,我们可以最大限度地减少磁盘磁头移动量,因为整个磁盘柱面只需通过磁盘旋转即可访问 - 磁头不必移动。

当删除记录被添加到日志时,Apache ActiveMQ Artemis 具有一个复杂的垃圾回收算法,该算法可以确定是否还需要特定日志文件,即所有数据是否已在相同或其他文件中被删除。如果是,则可以回收并重新使用该文件。

Apache ActiveMQ Artemis 还具有一个压缩算法,该算法可以从日志中删除死空间并将数据压缩,以便它在磁盘上占用更少的空间。

如果需要,日志还完全支持事务操作,支持本地事务和 XA 事务。

日志的大部分是用 Java 编写的,但是我们将与实际文件系统的交互抽象出来,以允许不同的可插拔实现。Apache ActiveMQ Artemis 附带两种实现

1.1. 日志和数据保留

ActiveMQ Artemis 提供了一种存储和重播历史数据的方法。有关更多信息,请参阅数据保留章节。

1.2. Java NIO

第一个实现使用标准 Java NIO 与文件系统进行交互。这提供了极好的性能,并在任何有 Java 6+ 运行时的平台上运行。

1.3. Linux 异步 I/O

第二个实现使用一个薄的原生代码包装器与 Linux 异步 I/O 库 (AIO) 交谈。使用 AIO,Apache ActiveMQ Artemis 将在数据已写入磁盘时被回调,使我们能够完全避免显式同步,并在 AIO 通知我们数据已持久化时简单地发送完成确认。

使用 AIO 通常比使用 Java NIO 提供更好的性能。

此日志选项仅在运行 Linux 内核 2.6 或更高版本以及安装了 libaio(如果尚未安装)后可用。有关如何安装 libaio 的说明,请参见安装 AIO 部分。

此外,请注意 AIO 仅适用于以下文件系统:ext2、ext3、ext4、jfs、xfs 和 NFSV4。

有关 libaio 的更多信息,请参见lib AIO.

libaio 是内核项目的一部分。

1.4. 内存映射

第三个实现使用文件支持的 READ_WRITE 内存映射 针对 OS 页面缓存与文件系统进行交互。

这提供了极好的性能(尤其是在严格的进程故障持久性要求下),几乎为零的复制(实际上是内核页面缓存)和为零的垃圾(从 Java HEAP 的角度来看)操作,并在任何有 Java 4+ 运行时的平台上运行。

在断电故障持久性要求下,它的性能至少与 NIO 日志相当,唯一的例外是内核小于或等于 2.6 的 Linux 操作系统,其中确保持久写入所需的 msync) 实现不同(并且更慢)从 NIO 日志中使用 fsync

它受益于 OS 巨型页面 的配置,特别是在使用大量日志文件并将它们的大小设置为 OS 页面大小(以字节为单位)的倍数时。

1.5. 标准文件

标准 Apache ActiveMQ Artemis 核心服务器使用日志的两个实例

  • 绑定日志。

    此日志用于存储与绑定相关的数据。其中包括在服务器上部署的一组队列及其属性。它还存储数据,例如 ID 序列计数器。

    绑定日志始终是 NIO 日志,因为它通常与消息日志相比吞吐量较低。

    此日志上的文件以 activemq-bindings 为前缀。每个文件都有一个 bindings 扩展名。文件大小为 1048576,它位于绑定文件夹中。

  • 消息日志。

    此日志实例存储所有与消息相关的数据,包括消息本身以及重复 ID 缓存。

    默认情况下,Apache ActiveMQ Artemis 将尝试使用 AIO 日志。如果 AIO 不可用,例如平台不是 Linux,内核版本不正确,或者没有安装 AIO,那么它将自动回退到使用 Java NIO,Java NIO 在任何 Java 平台上都可用。

    此日志上的文件以 activemq-data 为前缀。每个文件都有一个 amq 扩展名。默认情况下,文件大小为 10485760(可配置),它位于日志文件夹中。

对于大型消息,Apache ActiveMQ Artemis 将它们持久化到消息日志之外。这将在大型消息中讨论。

Apache ActiveMQ Artemis 还可以配置为在内存不足的情况下将消息分页到磁盘。这将在分页中讨论。

如果根本不需要持久化,Apache ActiveMQ Artemis 还可以配置为根本不将任何数据持久化到存储中,如配置代理以实现零持久化部分所述。

1.5.1. 配置绑定日志

使用 broker.xml 中的以下属性配置绑定日志

bindings-directory

这是绑定日志所在的目录。默认值为 data/bindings

create-bindings-dir

如果将其设置为 true,则如果绑定目录在 bindings-directory 中指定的地址不存在,则该目录将被自动创建。默认值为 true

1.5.2. 配置 JMS 日志

JMS 配置与其绑定日志共享其配置。

1.5.3. 配置消息日志

使用 broker.xml 中的以下属性配置消息日志

journal-directory

这是消息日志所在的目录。默认值为 data/journal

为了获得最佳性能,我们建议日志位于自己的物理卷上,以最大限度地减少磁盘磁头移动。如果日志位于与可能正在写入其他文件的其他进程共享的卷上(例如绑定日志、数据库或事务协调器),则磁盘磁头很可能会在写入这些文件时快速移动,从而极大地降低性能。

当消息日志存储在 SAN 上时,我们建议存储在 SAN 上的每个日志实例都被赋予自己的 LUN(逻辑单元)。

node-manager-lock-directory

这是节点管理器文件锁所在的目录。默认情况下,它与 journal-directory 的值相同。

当消息日志位于 SAN 上,并使用共享存储 HA策略,且代理实例位于同一台物理机上时,这很有用。

create-journal-dir

如果将其设置为 true,则如果日志目录在 journal-directory 中指定的地址不存在,则该目录将被自动创建。默认值为 true

journal-type

有效值为 NIOASYNCIOMAPPED

选择 NIO 将选择 Java NIO 日志。选择 ASYNCIO 将选择 Linux 异步 I/O 日志。如果您选择 ASYNCIO,但没有运行 Linux 或没有安装 libaio,则 Apache ActiveMQ Artemis 将检测到这一点,并自动回退到使用 NIO。选择 MAPPED 将选择 Java 内存映射日志。

journal-sync-transactional

如果将其设置为 true,则 Apache ActiveMQ Artemis 将确保所有事务数据在事务边界(提交、准备和回滚)上被刷新到磁盘。默认值为 true

journal-sync-non-transactional

如果将其设置为 true,则 Apache ActiveMQ Artemis 将确保非事务性消息数据(发送和确认)每次都被刷新到磁盘。此参数的默认值为 true

journal-file-size

每个日志文件的大小(以字节为单位)。此参数的默认值为 10485760 字节 (10MiB)。

journal-min-files

日志将维护的最小文件数。当 Apache ActiveMQ Artemis 启动且没有初始消息数据时,Apache ActiveMQ Artemis 将预先创建 journal-min-files 个文件。

创建日志文件并用填充填充它们是一项相当昂贵的操作,我们希望最大限度地减少在运行时执行此操作,因为文件会填满。通过预先创建文件,当一个文件填满时,日志可以立即继续使用下一个文件,而无需暂停来创建它。

根据您预计队列在稳定状态下包含的数据量,您应该调整此文件数量以匹配该总数据量。

journal-pool-files

系统将创建尽可能多的文件,但是当回收文件时,它将缩减回 journal-pool-files

此参数的默认值为 -1,这意味着一旦创建,它将永远不会删除日志上的文件。

请注意,系统不能无限增长,因为您仍然需要为可以无限增长的目标使用分页。

请注意:如果您得到太多文件,您可以使用压缩

journal-max-io

写入请求在被提交到系统执行之前会被排队。此参数控制 I/O 队列中可以存在的最大写入请求数。如果队列已满,则写入将阻塞,直到释放空间。

使用 NIO 时,此值应始终等于 1

使用 ASYNCIO 时,默认值应为 500

系统根据它是 NIO 还是 ASYNCIO 为此参数维护不同的默认值(NIO 的默认值为 1,ASYNCIO 的默认值为 500)

有一个限制,总的最大 ASYNCIO 不能高于在 OS 级别配置的值(/proc/sys/fs/aio-max-nr),通常为 65536。

journal-buffer-timeout

与每次写入都进行刷写不同,我们维护一个内部缓冲区,并在缓冲区满或超时时刷写整个缓冲区,以先发生者为准。这适用于 NIO 和 ASYNCIO,并允许系统在需要刷写的许多并发写入情况下更好地扩展。

此参数控制缓冲区在未填满的情况下刷写的超时时间。ASYNCIO 通常比 NIO 可以承受更高的刷写速率,因此系统对 NIO 和 ASYNCIO 维护不同的默认值(NIO 的默认值为 3333333 纳秒 - 每秒 300 次,ASYNCIO 的默认值为 500000 纳秒 - 即每秒 2000 次)。

将此属性设置为 0 将禁用内部缓冲区,写入将立即直接写入日志文件。

通过增加超时时间,您可能能够以延迟为代价提高系统吞吐量,默认参数的选择是在吞吐量和延迟之间取得合理平衡。

journal-buffer-size

ASYNCIO 上的定时缓冲区大小。默认值为 490KiB

journal-compact-min-files

在我们能够考虑压缩日志之前,文件的最小数量。压缩算法只有在您至少有 journal-compact-min-files 个文件时才会开始。

将其设置为 0 将完全禁用压缩功能。但这可能很危险,因为日志可能会无限增长。明智地使用它!

此参数的默认值为 10

journal-compact-percentage

启动压缩的阈值。当日志空间中小于此百分比的区域被视为活动数据时,我们将开始压缩。还要注意,只有在日志上至少有 journal-compact-min-files 个数据文件时,压缩才会开始。

此参数的默认值为 30

journal-lock-acquisition-timeout

在放弃之前,等待(以毫秒为单位)获取日志文件锁的时间。

此参数的默认值为 -1(即无限时间)。

journal-datasync

这将禁用在日志写入时使用 fdatasync。启用此选项可确保完全断电持久性,否则在日志写入时会确保进程故障持久性(操作系统保证)。这对 NIOMAPPED 日志特别有效,这些日志依赖于 fsync/msync 来强制将写入更改写入磁盘。

默认值为 true

关于禁用 journal-datasync 的说明

任何现代操作系统都保证,在进程故障(即崩溃)时,所有对页面缓存的未提交更改都将被刷新到文件系统中,从而在对相同页面进行后续操作之间保持一致性,并确保不会丢失任何数据。在禁用 journal-datasync 的情况下,这种刷写的时序的可预测性取决于操作系统的配置,但不会损害(或放宽)如上所述的进程故障持久性语义。依赖于操作系统的页面缓存会牺牲断电保护,同时提高日志操作的效率,能够利用操作系统内核页面缓存子系统提供的读取缓存和写入合并功能。

关于禁用磁盘写入缓存的说明

大多数磁盘包含硬件写入缓存。写入缓存可以提高磁盘的表观性能,因为写入只是进入缓存,然后稍后延迟写入磁盘。

无论您是从操作系统执行了 fsync(),还是正确地从 Java 程序中同步了数据,都会发生这种情况!

默认情况下,许多系统都启用了磁盘写入缓存。这意味着即使在从操作系统同步之后,也不能保证数据已实际写入磁盘,因此如果发生故障,关键数据可能会丢失。

一些更昂贵的磁盘具有非易失性或电池支持的写入缓存,在发生故障时不一定丢失数据,但您需要对其进行测试!

如果您的磁盘没有昂贵的非易失性或电池支持的缓存,并且它不是某种冗余阵列(例如 RAID)的一部分,并且您重视数据完整性,则需要确保禁用了磁盘写入缓存。

请注意,禁用磁盘写入缓存可能会对性能造成负面影响。如果您一直习惯于使用默认情况下启用了写入缓存的磁盘,而不知道数据完整性可能会受到损害,那么禁用它将让您了解您的磁盘在真正可靠地执行操作时能够执行多快。

在 Linux 上,您可以使用工具 hdparm(对于 IDE 磁盘)或 sdparmsginfo(对于 SDSI/SATA 磁盘)来检查和/或更改磁盘的写入缓存设置。

在 Windows 上,您可以通过右键单击磁盘并单击属性来检查/更改设置。

1.6. 安装 AIO

Java NIO 日志提供了出色的性能,但如果您使用的是 Linux 内核 2.6 或更高版本的 Apache ActiveMQ Artemis,我们强烈建议您使用 ASYNCIO 日志以获得最佳的持久性性能。

在其他操作系统或更早版本的 Linux 内核下,无法使用 ASYNCIO 日志。

如果您运行的是 Linux 内核 2.6 或更高版本,并且尚未安装 libaio,则可以使用以下步骤轻松安装它。

使用 yum(例如,在 Fedora 或 Red Hat Enterprise Linux 上)

yum install libaio

使用 aptitude(例如,在 Ubuntu 或 Debian 系统上)

apt-get install libaio

2. JDBC 持久性

Apache ActiveMQ Artemis JDBC 持久性层提供了使用数据库存储代理状态(消息、地址和队列定义等)的功能。

使用 ActiveMQ Artemis 文件日志是推荐的配置,因为它提供了更高水平的性能并且更加成熟。JDBC 在分页和大型消息方面的性能尤其降低。JDBC 持久性层面向必须使用数据库的用户,例如,由于公司内部政策。

ActiveMQ Artemis 目前支持有限数量的数据库供应商。

  1. PostgreSQL

  2. MySQL

  3. Microsoft SQL Server

  4. Oracle

  5. DB2

  6. Apache Derby

JDBC 存储使用 JDBC 连接将消息和绑定数据存储在数据库表中的记录中。存储在数据库表中的数据使用 Apache ActiveMQ Artemis 内部编码进行编码。

2.1. 配置 JDBC 持久性

要将 Apache ActiveMQ Artemis 配置为使用数据库来持久化消息和绑定数据,您必须执行以下两项操作。

  1. 有关如何使 JDBC 驱动程序对代理可用的信息,请参阅有关添加运行时依赖项的文档。

  2. 在您的 <core> 元素下的 broker.xml 配置文件中创建一个 store 元素。例如

<store>
   <database-store>
      <jdbc-driver-class-name>org.apache.derby.jdbc.EmbeddedDriver</jdbc-driver-class-name>
      <jdbc-connection-url>jdbc:derby:data/derby/database-store;create=true</jdbc-connection-url>
      <bindings-table-name>BINDINGS_TABLE</bindings-table-name>
      <message-table-name>MESSAGE_TABLE</message-table-name>
      <page-store-table-name>MESSAGE_TABLE</page-store-table-name>
      <large-message-table-name>LARGE_MESSAGES_TABLE</large-message-table-name>
      <node-manager-store-table-name>NODE_MANAGER_TABLE</node-manager-store-table-name>
   </database-store>
</store>
jdbc-connection-url

您的数据库服务器的完整 JDBC 连接 URL。连接 URL 应包含所有配置参数和数据库名称。

使用 XML 配置文件配置服务器时,请确保转义任何非法字符;例如,“&” 是 JDBC 连接 URL 中的典型字符,应将其转义为 "&"。
bindings-table-name

将为 ActiveMQ Artemis 服务器持久化绑定数据的表的名称。指定表名允许用户在多个服务器之间共享单个数据库,而不会相互干扰。

message-table-name

将为 ActiveMQ Artemis 服务器持久化绑定数据的表的名称。指定表名允许用户在多个服务器之间共享单个数据库,而不会相互干扰。

large-message-table-name

将为 ActiveMQ Artemis 服务器持久化消息和相关数据的表的名称。指定表名允许用户在多个服务器之间共享单个数据库,而不会相互干扰。

page-store-table-name

用于存放页面存储目录信息的表的名称。请注意,每个地址都有自己的页面表,该表将使用此名称附加一个最多 20 个字符的唯一 ID。

node-manager-store-table-name

将为 ActiveMQ Artemis 服务器持久化 HA 共享存储锁(即主和备用)和 HA 相关数据的表的名称。指定表名允许用户在多个服务器之间共享单个数据库,而不会相互干扰。每个共享存储主/备用对必须使用相同的表名,并且不支持在多个(且无关)主/备用对之间共享相同的表。

jdbc-driver-class-name

所需数据库驱动程序的全限定类名。

jdbc-network-timeout

以毫秒为单位的 JDBC 网络连接超时时间。默认值为 20000 毫秒(即 20 秒)。使用共享存储时,建议将其设置为小于或等于 jdbc-lock-expiration

jdbc-lock-renew-period

JDBC 锁的保持活动服务的周期,以毫秒为单位。默认值为 2000 毫秒(即 2 秒)。

jdbc-lock-expiration

JDBC 锁在保持活动状态的情况下被视为有效的时长,以毫秒为单位。默认值为 20000 毫秒(即 20 秒)。

jdbc-journal-sync-period

日志与 JDBC 同步的时长,以毫秒为单位。默认值为 5 毫秒。

jdbc-allowed-time-diff

在更新和验证主锁和备用锁时,请求数据库当前时间时,代理和数据库之间允许的最大时间偏移量,以毫秒为单位。当前,此值只影响日志记录,如果检测到的差异超过限制,则会显示警告。默认值为 250 毫秒。

jdbc-max-page-size-bytes

页面可以使用的最大尺寸。默认值和推荐的最大值为 100K 字节。使用更大的尺寸会导致下载大型 Blob,这会影响使用分页消息时的性能。

某些 DBMS(例如 Oracle,30 个字符)对表名的尺寸有限制,在为 Artemis 数据库存储配置表名时应考虑这一点,特别是注意页面存储表名,它可以附加一个最多 20 个字符的唯一 ID。(对于 Oracle,这意味着配置最大尺寸为 10 个字符的 page-store-table-name)。

如果需要对用户和密码进行编码,也可以显式添加它们,而不是在 JDBC URL 中添加它们,这将类似于

<store>
   <database-store>
      <jdbc-driver-class-name>org.apache.derby.jdbc.EmbeddedDriver</jdbc-driver-class-name>
      <jdbc-connection-url>jdbc:derby:data/derby/database-store;create=true</jdbc-connection-url>
      <jdbc-user>ENC(dasfn353cewc)</jdbc-user>
      <jdbc-password>ENC(ucwiurfjtew345)</jdbc-password>
      <bindings-table-name>BINDINGS_TABLE</bindings-table-name>
      <message-table-name>MESSAGE_TABLE</message-table-name>
      <page-store-table-name>MESSAGE_TABLE</page-store-table-name>
      <large-message-table-name>LARGE_MESSAGES_TABLE</large-message-table-name>
      <node-manager-store-table-name>NODE_MANAGER_TABLE</node-manager-store-table-name>
      <jdbc-page-max-size-bytes>100K</jdbc-page-max-size-bytes>
   </database-store>
</store>

2.2. 配置 JDBC 连接池

要将 Apache ActiveMQ Artemis 配置为使用带有 JDBC 连接池的数据库,您需要设置数据源属性,例如

<store>
    <database-store>
        <data-source-properties>
            <data-source-property key="driverClassName" value="com.mysql.jdbc.Driver" />
            <data-source-property key="url" value="jdbc:mysql://127.0.0.1:3306/artemis" />
            <data-source-property key="username" value="artemis" />
            <data-source-property key="password" value="artemis" />
            <data-source-property key="poolPreparedStatements" value="true" />
        </data-source-properties>
        <bindings-table-name>BINDINGS</bindings-table-name>
        <message-table-name>MESSAGES</message-table-name>
        <large-message-table-name>LARGE_MESSAGES</large-message-table-name>
        <page-store-table-name>PAGE_STORE</page-store-table-name>
        <node-manager-store-table-name>NODE_MANAGER_STORE</node-manager-store-table-name>
    </database-store>
</store>

您可以在 https://commons.apache.org/proper/commons-dbcp/configuration.html 上找到数据源属性的文档。

要掩盖属性的值,可以使用与掩盖密码相同的步骤。

请注意,重新连接只有在没有客户端发送消息时才有效。相反,如果在重新连接期间尝试写入日志的表,则代理将快速失败并关闭。

3. 零持久性

在某些情况下,消息系统有时需要零持久性。将 Apache ActiveMQ Artemis 配置为执行零持久性很简单。只需在 broker.xml 中将参数 persistence-enabled 设置为 false 即可。

请注意,如果将此参数设置为 false,则将发生持久性。这意味着不会持久化任何绑定数据、消息数据、大型消息数据、重复 ID 缓存或分页数据。