(02)Chronicle Queue 配置

本章介绍如何配置 Chronicle Queue。 最好在使用“ChronicleQueue.singleBuilder”实例化队列时进行设置。 例如,此构建器将滚动周期设置为RollCycle.HOURLY

String basePath = OS.getTarget() + "/getting-started"
ChronicleQueue queue = ChronicleQueue.singleBuilder(basePath)
    .rollCycle(RollCycle.HOURLY)
    .build();
}

下表显示了可以使用 Chronicle Queue 构建器设置的选项。 通过单击每行中的字段名称,可以找到有关每个属性的更多详细信息。

📝注意: 某些功能仅在 Chronicle Queue Enterprise 中可用。 请在 Chronicle Queue OSS 列中获取 Chronicle Queue OSS 的可用性。

表 1. 使用 Chronicle Queue 构建器时的可用字段

类型字段名称描述缺省值是否在 开源Chronicle Queue 中可用
RollCyclerollCycle滚动间隔,表示将消息附加到新队列的频率DAILYYes
Longepoch以微秒为单位的滚动周期偏移量0Yes
WireTypewireTypeBINARY_LIGHTOSS 除了 DELTA_BINARY
IntegerindexCount每个索引数组的大小The default index count associated with the selected roll cycleYes
IntegerindexSpacing显式索引的摘录之间的空间The default index spacing associated with the selected roll cycleYes
LongblockSize内存映射块的大小。 除非必要,否则不要更改此设置Yes
BufferModewriteBufferModeNone只有默认值None
BufferModereadBufferModeBufferMode只有默认值None
booleandoubleBuffer在争用时启用双缓冲写入falseYes
TimeProvider[timeProvicer]用于评估滚动计时的时间提供者SystemTimeProvider.INSTANCEYes
int[maxTailers]使用 Ring Buffer 时要预分配的尾部数量。 仅在使用 readBufferMode=Asynchronous 时设置1Enterprise only
long[bufferCapacity]以字节为单位设置 Ring Buffer 的容量falseEnterprise only
boolean[enableRingBufferMonitoring]启用 Ring Buffer 的监控功能falseEnterprise only
boolean[ringBufferReaderCanDrain]允许Ring Buffer的读取器进程调用队列消耗器。默认情况下,只允许写入进程调用排水器。falseEnterprise only
boolean[ringBufferForceCreateReader]控制是否强制创建阅读器(从崩溃中恢复)。falseEnterprise only
boolean[ringBufferReopenReader]控制 Ring Buffer 读取器是否在关闭时重置。 如果为 true,重新打开阅读器会使您回到同一个地方,如果阅读器未打开,您的阅读器可以阻止作者falseEnterprise only
HandlerPriority[drainerPriority]ring buffer 的 drainer handler 的优先级MEDIUMEnterprise only
int[drainerTimeoutMS]10,000Enterprise only

滚动周期(Roll Cycle)

Chronicle Queue 是文件系统上目录的逻辑视图。 队列数据本身被拆分成多个文件,每个文件都包含属于一个*周期(Cycle)*的数据。 循环的长度由传递给队列构建器的rollCycle参数决定。

rollCycle 的示例配置:

  • RollCycles.DAILY 存储在队列中的事件将被分组为 24 小时周期
  • RollCycles.HOURLY 每隔一小时,将为写入的事件创建一个新的队列文件

当创建新文件(滚动队列)以容纳写入队列的事件时,持久化数据结构(directory-listing.cq4t)将用目录中出现的最低和最高的cycle号进行更新。 维护此表允许ExcerptTailer忙于旋转等待新数据被追加到队列中,而无需对文件系统进行代价高昂的调用以检查新队列文件是否存在。

以下部分描述了滚动的执行方式、滚动周期间隔的自定义方式,最后提供了一些在决定滚动计划时要考虑的一般性建议。

滚动是如何工作的

当队列到达它应该滚动的时间点时,appender 将自动在当前文件的末尾写入一个文件结束 (EOF) 标记,以指示没有其他 appender 应该写入它。 同样,任何tailer都不应阅读此标记以外的内容。

如果进程被关闭,然后在应该发生滚动之后重新启动,appender将尝试定位旧文件并写入EOF标记。但是,在某个超时之后,tailer 将视文件中有 EOF 标记而不管。。

滚动周期配置

滚动的间隔是使用队列的属性rollCycle配置的。 例如,可以通过提供RollCycle.DAILY 将队列配置为每天滚动其文件(即创建一个新文件):

SingleChronicleQueue queue = ChronicleQueue.singleBuilder("rollCycleTest")
    .rollCycle(RollCycles.DAILY)
    .build();

所有可用的滚动周期都可以显示在 可用 roll-cycles下。

📝注意: 默认 roll-cycle 是RollCycle.HOURLY

覆盖滚动周期

一旦设置了队列的滚动周期,就不能在以后更改它。更正式地说,在对Chronicle Queue进行第一个追加之后,任何配置为使用相同路径的SingleChronicleQueue的进一步实例都必须配置为使用相同的滚动周期。尝试设置此选项2次将引发异常。

如果在进行任何追加之前创建了另一个 Chronicle Queue 实例,并且后续追加操作具有不同的滚动周期,则滚动周期将被更新以匹配持久的滚动周期。 在这种情况下,将打印警告日志消息以通知库用户该情况:

[main] WARN SingleChronicleQueue - Queue created with roll-cycle MINUTELY, but files on disk use roll-cycle HOURLY. Overriding this queue to use HOURLY

控制精确的滚动时间

Chronicle Queue 基于 UTC 时间,并使用 System.currentTimeMillis() 来评估何时滚动队列。 每当启动所选类型的新时间框架时就会发生滚动,这意味着对于每分钟滚动一个新队列会在新的一分钟开始时创建,并且每日滚动发生在 UTC 时间午夜。

可以使用 Chronicle Queue 的属性 epoch() 更改此行为。 Epoch 是指相对于设定时间范围的毫秒偏移量。换句话说,如果您将 epoch 设置为 epoch(1) 并使用 RollCycle.DAILY,则队列将在 UTC 时间午夜后 1 毫秒滚动。 把它放在一起:

SingleChronicleQueue queue = ChronicleQueue.singleBuilder("rollCycleTest")
    .rollCycle(RollCycle.DAILY)
    .epoch(1)
    .build();

通过提供 System.currentTimeMillis() 也可以将当前时间用作滚动时间。

SingleChronicleQueue queue = ChronicleQueue.singleBuilder("rollCycleTest")
    .rollCycle(RollCycle.DAILY)
    .epoch(System.currentTimeMillis())
    .build();

🔔重要: 我们不建议在已经创建了.cq4文件的系统上更改epoch,这些文件使用不同的epoch设置。

可用的滚动周期

如前所述,滚动周期决定了创建新队列的频率。 但是,每个滚动周期还利用具有不同消息容量的队列。 这导致不同的滚动周期在吞吐量方面具有最大容量。

下表总结了可用的滚动周期及其容量:

Roll-cycle 名字每个周期中的最大消息数(十进制)每个周期中的最大消息数(十六进制)在周期长度内每秒最大消息数(平均)
FIVE_MINUTELY1,073,741,8240x400000003,579,139
TEN_MINUTELY1,073,741,8240x400000001,789,569
TWENTY_MINUTELY1,073,741,8240x400000001,491,308
HALF_HOURLY1,073,741,8240x40000000596,523
FAST_HOURLY4,294,967,2950xffffffff1,193,046
TWO_HOURLY4,294,967,2950xffffffff596,523
FOUR_HOURLY4,294,967,2950xffffffff298,261
SIX_HOURLY4,294,967,2950xffffffff198,841
FAST_DAILY4,294,967,2950xffffffff49,710
MINUTELY67,108,8640x40000001,118,481
HOURLY268,435,4560x1000000074,565
DAILY4,294,967,2950xffffffff49,710
LARGE_HOURLY4,294,967,2950xffffffff49,710
LARGE_DAILY137,438,953,4710x1fffffffff1,590,728
XLARGE_DAILY4,398,046,511,1030x3ffffffffff50,903,316
HUGE_DAILY281,474,976,710,6550xffffffffffff3,257,812,230
SMALL_DAILY536,870,9120x200000006,213
LARGE_HOURLY_SPARSE17,179,869,1830x3ffffffff4,772,185
LARGE_HOURLY_XSPARSE4,398,046,511,1030x3ffffffffff1,221,679,586
HUGE_DAILY_XSPARSE281,474,976,710,6550xffffffffffff78,187,493,530
TEST_SECONDLY4,294,967,2950xffffffff4,294,967,295
TEST4_SECONDLY4,0960x10004,096
TEST_HOURLY1,0240x4000
TEST_DAILY640x400
TEST2_DAILY5120x2000
TEST4_DAILY4,0960x10000
TEST8_DAILY131,0720x200001

🔔重要: 名为 TEST* 的滚动循环只能在测试环境中使用。

为了理解上述限制是如何产生的,我们可以看一个例子。 Chronicle Queue 使用由循环号和序列号组成的64 位索引 . 对于每日滚动周期 Chronicle Queue,索引被分成两半保留:

  • 消息序列号为 32 位 - 每天允许 40 亿条消息
  • 31 位为周期数(保留符号的高位) - 允许我们将消息存储到 5,881,421 年。

如果每个周期有超过40亿个消息,则可以增加用于周期的比特数,从而增加每个周期的消息数,但要减少周期的数量。例如,您可能每天有多达1万亿条消息,而您需要23位周期才能允许最多24,936年。如果我们每秒以32位的40亿条消息滚动,我们将在大约10年内耗尽。通过每小时和每天的滚动,它是相当无限的。此外,通过更改epoch,您可以进一步扩展日期,将第一个和最后一个周期之间的限制更改为31位或23位。

时区翻转 ★

Chronicle Queue 的滚动时间基于 UTC 时区。 但是,Chronicle Queue Enterprise 支持时区翻转。 这允许指定考虑用户本地时区而不是 UTC 的队列翻转的时间和周期。

时区滚动周期是一项企业功能,仅适用于每日滚动周期,即滚动周期为以下之一时:

  • SMALL_DAILY
  • DAILY
  • LARGE_DAILY
  • XLARGE_DAILY
  • HUGE_DAILY

配置

时区滚动使用配置方法 rollTime(LocalTime rollTime, ZoneId zoneId) 设置。 它提供了一个 LocalTime.of(int hour, int minute) 的实例,描述了相对于给定时区在一天中的什么小时和分钟滚动。 如果未提供时区,则默认为 UTC。

📝注意:此处 中阅读有关可用 ZoneId:s 的更多信息。

以下是在伦敦时间下午 5 点执行每日滚动的队列示例:

SingleChronicleQueue queue = ChronicleQueue.singleBuilder("/timezone")
    .rollTime(LocalTime.of(17, 0), ZoneId.of("Europe/London"))
    .rollCycle(RollCycle.DAILY)
    .timeProvider(System.currentTimeMillis())
    .build();

归档旧的队列文件 ★

随着时间的推移,可能需要自动删除或归档旧的队列文件。 自动化过程需要确保在尝试删除之前没有在队列文件上打开活动文件句柄。

为促进此操作,Chronicle Queue Enterprise 在内部跟踪对其roll-cycle 文件的引用。 通过检查ChronicleQueue.numberOfReferences()是否返回零来确保没有对给定文件的引用。

建议的方法是从单独的JVM应用程序执行维护操作,方法如下:

public void removeOldQueueFiles() throws IOException {
    final Path queuePath = Paths.get("/path/to/queue");
    try (final SingleChronicleQueue queue = SingleChronicleQueueBuilder.
            binary(queuePath).build()) {

        try (final Stream<Path> queueFiles = Files.list(queuePath).
                filter(p -> p.toString().endsWith(SingleChronicleQueue.SUFFIX))) {

            queueFiles.filter(p -> isReadyForDelete(p)).map(Path::toFile).
                    filter(f -> queue.numberOfReferences(f) == 0).
                    forEach(File::delete);

        }
    }
}

关于滚动的一般建议

在滚动时,会创建一些不可避免的对象和内存映射,并释放旧的内存映射。 此活动可能会给您的应用程序带来轻微的抖动。 Chronicle 的目标是将这种情况保持在最低限度,并控制它何时发生。 但是,仍然建议尽可能避免在关键时间点滚动。

适应停机时间

在并非始终处于活动状态的系统中,建议在停机期间安排滚动。 但是,对于具有繁忙馈送且没有停机时间的应用程序,Chronicle 建议使用分钟滚动(每分钟创建一个新队列)。 这将抖动保持在最低限度,因为只有一分钟的数据必须在队列滚动上取消映射。

避免大文件

通常建议将队列文件的大小限制在 < 250GB 左右,因为取消映射大型 .cq4 文件可能会导致不必要的抖动。 因此,如果可能,请使用更规律的滚动周期以避免与取消映射大文件相关的任何性能损失。

<a name=-“wire_type”>

Wire 类型

可以通过显式设置 WireType 来配置 Chronicle Queue 如何存储数据:

例如:

SingleChronicleQueue queue = ChronicleQueue.singleBuilder("rollCycleTest")
    .wireType(RollCycles.DAILY)
    .build();

📝注意: 默认的WireTypeBINARY_LIGHT

😎警告: 支持的Wire类型

尽管可以在创建构建器时显式提供 WireType ,但不鼓励这样做,因为 Chronicle Queue 尚不支持所Wire类型。

特别是,不支持以下Wire类型:

  • TEXT (基本上都是基于文本的,包括 JSON 和 CSV)
  • RAW
  • READ_ANY

块大小

当一个队列被读/写时,当前队列文件的一部分被映射到一个内存段。 可以使用 blockSize(long blockSize) 方法设置内存映射块的大小,例如 创建队列时:

SingleChronicleQueue queue = ChronicleQueue.singleBuilder("rollCycleTest")
    .blockSize(128 << 20)   // <1>
    .build();

<1>: 以字节为单位的块大小 (128 << 20 = 134217728)。
**📝注意:**最小块大小为 2^16(65536) 字节。

关于块大小的一般建议

块大小最好适应队列消息的大小。 一个好的经验法则是使用至少是消息大小的四倍的块大小。 如果尝试向队列写入超过块大小的消息,则会抛出IllegalStateException并中止写入。

复制队列时,还建议对每个队列实例使用相同的块大小。 块大小不会写入队列的元数据,因此在创建队列实例时最好将其设置为相同的值。

📝注意: 避免不必要地更改 blockSize

索引数

可以限制每个队列文件的索引数组总数,因此也可以确定每个索引数组的大小。 这是通过在构建队列时配置参数 indexCount(int indexCount) 来完成的。

SingleChronicleQueue queue = ChronicleQueue.singleBuilder("queue")
    .indexCount(10)
    .build();

📝注意: (索引数)是索引队列条目的最大数量。

根据下表,默认索引计数因 滚动周期 而异。

滚动周期默认索引计数
HALF_HOURLY, TWENTY_MINUTELY, TEN_MINUTELY, FIVE_MINUTELY, MINUTELY, LARGE_HOURLY_XSPARSE2048
SIX_HOURLY, FOUR_HOURLY, TWO_HOURLY, FAST_HOURLY, HOURLY, LARGE_HOURLY_XSPARSE, FAST_DAILY4096
DAILY, LARGE_HOURLY8192
HUGE_DAILY_XSPARSE16384
LARGE_DAILY32768
XLARGE_DAILY131072
HUGE_DAILY524288

💡提示: 有关队列索引的更多信息,请参阅索引

索引间距

每个队列以固定的时间间隔明确索引某些摘录,以便更快地进行引用。 索引的摘录由参数 indexSpacing(int spacing) 控制,该参数定义索引摘录之间的空间。 在将此属性设置为 1 的极端情况下,每个摘录都会被索引。

与此值相关的自然权衡是更频繁的索引会产生更快的随机访问读取,但会降低顺序写入性能。 但是,顺序读取性能完全不受此属性的影响。

可以在构建 Chronicle queue 时设置索引间距,如下所示:

SingleChronicleQueue queue = ChronicleQueue.singleBuilder("queue")
    .indexSpacing(16)
    .build();

根据下表,默认索引间距因 滚动周期 而异。

滚动周期默认索引间距
SMALL_DAILY8
MINUTELY, HOURLY16
DAILY, LARGE_HOURLY64
LARGE_DAILY128
XLARGE_DAILY, FAST_DAILY, SIX_HOURLY, FOUR_HOURLY, TWO_HOURLY, FAST_HOURLY, HALF_HOURLY, TWENTY_MINUTELY, TEN_MINUTELY, FIVE_MINUTELY256
HUGE_DAILY, LARGE_HOURLY_SPARSE1024
LARGE_HOURLY_XSPARSE, HUGE_DAILY_XSPARSE1048576

💡提示: 有关队列索引的更多信息,请参阅 Indexing.

缓冲模式 ★

这些参数为具有以下选项的读或写定义BufferMode:

  • readBufferMode, writeBufferMode

这些参数为具有以下选项的读或写定义BufferMode:

- None - 默认(也是开源用户唯一可用的),无缓冲;

- Copy - 与加密结合使用;

- Asynchronous - 读取和/或写入时使用 ring-buffer,由 Chronicle Ring Enterprise 产品 Buffer 提供

  • bufferCapacity

使用bufferMode: Asynchronous时以字节为单位的 RingBuffer 容量

原文链接: (https://docs.chronicle.software/chronicle-queue/chronicle-queue/configuration/app_configuration.html)


<<<<<<<<<<<< [完] >>>>>>>>>>>>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值