改进 Elastic Agent 和 Beats 中的事件队列

作者:Fae Charlton, Alexandros Sapranidis

内部改进如何降低 Elastic 8.13 中的内存使用。

在 8.12 版本中,我们引入了性能预设 ------ 一种更简单的方法,用于调整 Elastic Agent 和 Beats 以适应各种场景。这提高了常见环境的性能,而过去通常需要进行详细调整。

从 8.13 版本开始,我们专注于改进我们的内部库,以更好地支持这些预设。其结果是我们的内部事件队列进行了重写,为所有 Beats 带来了降低的内存使用。

在我们的内部基准测试套件中,Filebeat 8.13 在所有预设上显示出大约 20% 的内存减少。在这篇文章中,我们将探讨如何实现这一点。

Beats 事件队列

由 Elastic Agent 和 Beats 接收的事件在发送到输出端时会存储在一个队列中。队列的配置对于需要多少内存来存储这些事件有很大影响。在 8.13 版本之前,这种配置涉及一些容易被误解的参数,这些参数常常是配置错误的常见来源。

我们来回顾一下这些调优参数及其作用。

bulk_max_size 和 flush.min_events

当输出 worker 准备好发送数据时,它会从内部队列请求一批事件。这个请求的大小由 bulk_max_size 控制,这是一个重要的输出调优参数。如果 bulk_max_size 是 100,那么队列将尝试提供 100 个事件给输出 worker 发送。

队列还有一个 flush.timeout 参数。当这个参数为零时,队列会立即返回事件,即使它没有足够的事件。在我们的示例中,如果请求了 100 个事件但队列中只有 50 个,那么输出 worker 将获得 50 个事件。但是当 flush timeout 为正值时,队列会等待达到指定的超时时间以收集更多事件。

但是,请注意:假设我们设置了一个 5 秒的 flush timeout 并请求 100 个事件。你可能会期望,如果队列有 100 个事件,它将立即返回这 100 个事件,否则它将延迟多达 5 秒以达到 100 个事件。从 8.13 版本开始,你会是正确的。但是旧的队列并不是这样的!队列不是等待填充一个输出请求 ------ 它等待填充一个内部队列缓冲区,这个缓冲区的大小可能完全不同。

内部队列缓冲区的大小由 flush.min_events 控制,这是一个看起来非常类似于 bulk_max_size 的参数,经常被误解,但是它可能会产生非常不同的影响。

这些问题可能导致以下一些性能问题:

示例 1:增加内存使用量

yaml 复制代码
1.  bulk_max_size: 50
2.  flush.timeout: 10s
3.  flush.min_events: 1500

最大的问题是内存使用。在 8.13 版本之前,队列一次管理一个完整的缓冲区的内存。在此示例配置中,一个完整的事件缓冲区可以提供 30 个输出批次,每个批次 50 个事件。这意味着我们需要完全处理 30 个批次,才能释放最初那一个批次的内存!

示例 2:增加延迟

markdown 复制代码
1.  bulk_max_size: 100
2.  flush.timeout: 5s
3.  flush.min_events: 200

假设输出请求 100 个事件。队列中有 100 个事件,但在填满 200 个事件的完整缓冲区之前,它不会返回任何事件。如果不再有更多事件进来,它将等待整整 5 秒才返回任何事件,尽管请求本可以立即得到满足。

这一直是一个陷阱,但在 8.12 版本中,我们将默认的 flush.timeout 从 1 秒增加到 10 秒时,这个问题变得更加严重。对大多数用户来说,这提高了性能,因为大批量事件的处理更有效率。但是,那些将输出的 bulk_max_size 设置得较低的用户看到了增加的延迟,尽管理论上有足够的事件可以立即开始处理。

示例 3:事件批次变小

markdown 复制代码
1.  bulk_max_size: 100
2.  flush.timeout: 1s
3.  flush.min_events: 150

我们有一个队列,里面有 300 个事件,输出每次请求 100 个事件。理论上,我们应该能够将 300 个事件作为三批每批 100 个事件发送,但队列的缓冲区大小为 150 个事件,队列一次只从一个缓冲区返回事件。因此,实际上发生的是每个缓冲区被分成两批 ------ 一批 100 个事件和一批 50 个事件。

对于大多数配置来说,即使队列中还有更多事件,输出工作者偶尔也会得到比其请求的少的事件。这不足以造成巨大差异,但略微降低了效率,如果我们每次都能返回正确数量的事件,那将会更好。

我们对此做了什么

我们最终重写了队列,完全删除了内部缓冲区的链条。相反,我们现在使用一个固定的单一缓冲区,根据需要在末尾循环。事件也从这个共享缓冲区中复制出来,以组装输出批次。现在,flush.timeout 的工作方式与大多数人预期的完全一致:如果请求的事件可用,队列将立即返回请求的数量的事件,否则它将等待达到指定的超时时间来填充请求(但仅限于当前请求,而不是某些更大的内部限制!)。

这次重构需要做出一个妥协,即原始队列试图避免的 ------ 事件批次不再是内存中的单个连续序列。但作为交换,我们得到了巨大的好处:

  • 示例一中的内存问题已经消失。一旦输出确认了一批事件已经被处理,队列就可以释放它的内存,不管它相对于其他事件批次的位置如何。
  • 示例二中的延迟问题已经消失。如果队列有足够的事件,它将返回它们。它们在事件序列中的位置已经不重要了。
  • 示例三中的批处理大小问题已经消失。由于事件不再需要来自一个连续的缓冲区,我们不需要根据内部内存边界来分割批次。

flush.min_events 现在是一个遗留参数,为了向后兼容性,它指定了事件批次大小的全局最大值。如果你使用的是性能预设(performance preset),则可以完全忽略此参数,但现在建议自定义队列配置将其设置为一个大值,并使用 bulk_max_size 来控制批次大小。只要 flush.min_events 足够大,再也没有改变它的性能优势了。

结果

在我们的内部基准测试中,我们发现对于所有预设(presets),都实现了显著的内存节省(这些差异是通过使用 Filebeat 在结构化 JSON 事件上进行文件流输入进行测量的):

|------------|--------------------|
| Preset | Memory savings |
| balanced | 17% |
| throughput | 18% |
| scale | 18% |
| latency | 24% |

所有预设还显示出每个事件的 CPU 成本减少了3%。

性能预设的回报

通过为常见的性能目标定义内置预设,我们大大减少了配置 Elastic Agent 和 Beats 时的猜测工作。现在我们正在看到后续的好处,因为这为我们的优化工作提供了一组常见的配置目标。期望这些改进将在 8.14 版本及以后继续!

Elastic 8.13 中还有什么新功能?请查看 8.13 版本的发布公告以了解更多信息>>

本文中描述的任何功能或功能的发布和时间安排仍然完全由 Elastic 自行决定。当前尚不可用的任何功能或功能可能无法按时或根本无法交付。

原文:Improving the event queue in Elastic Agent and Beats | Elastic Blog

相关推荐
Mephisto.java4 小时前
【大数据学习 | Spark】Spark的改变分区的算子
大数据·elasticsearch·oracle·spark·kafka·memcache
mqiqe5 小时前
Elasticsearch 分词器
python·elasticsearch
小马爱打代码5 小时前
Elasticsearch简介与实操
大数据·elasticsearch·搜索引擎
java1234_小锋13 小时前
Elasticsearch是如何实现Master选举的?
大数据·elasticsearch·搜索引擎
梦幻通灵19 小时前
ES分词环境实战
大数据·elasticsearch·搜索引擎
Elastic 中国社区官方博客20 小时前
Elasticsearch 中的热点以及如何使用 AutoOps 解决它们
大数据·运维·elasticsearch·搜索引擎·全文检索
小黑屋说YYDS1 天前
ElasticSearch7.x入门教程之索引概念和基础操作(三)
elasticsearch
Java 第一深情1 天前
Linux上安装单机版ElasticSearch6.8.1
linux·elasticsearch·全文检索
KevinAha2 天前
Elasticsearch 6.8 分析器
elasticsearch
wuxingge2 天前
elasticsearch7.10.2集群部署带认证
运维·elasticsearch