默认情况下,Logstash 管道阶段(输入→管道工作器)之间使用内存中有界队列来缓冲事件。 但是,为了防止异常终止期间的数据丢失,Logstash 具有持久性队列功能,可以启用该功能以将消息队列存储在磁盘上。 队列位于输入和过滤器阶段之间,如下所示:
input → persistent queue → filter + output
根据持久队列博客文章,Logstash 持久队列应该对整体吞吐量产生很小的影响。 尽管对于管道受 CPU 约束的用例通常是这样,但在其他情况下,持久队列可能会导致 Logstash 性能大幅下降。 因此,在此博客文章中,我将讨论何时会发生这种情况以及使用哪种技术来减少启用 Logstash 持久队列可能对性能造成的影响。
此博客的动机
在最近的一次咨询活动中,持续的队列导致速度下降了大约75%,从 40K 事件/秒降低到 10K 事件/秒。 调查表明,磁盘或 CPU 都没有饱和,标准的 Logstash 调优技术(例如测试不同的批处理大小和添加更多的工作线程)无法弥补这种减慢。
为什么持久队列可能会影响 Logstash 性能
如果为管道启用了持久队列,则 Logstash 将为该管道运行一个单线程的持久队列-持久队列不会在单个管道内的多个线程之间运行。 换句话说,单个管道只能使用单个线程来驱动磁盘。 即使管道有多个输入,也是如此,因为单个管道中的其他输入不会增加磁盘 I/O 线程。
因为启用持久队列将同步磁盘 I/O(这会增加等待时间)添加到 Logstash 管道中,所以即使关联服务器上的所有资源都没有饱和,持久队列也可能会降低吞吐量。
改善整体表现的解决方案
如果 Logstash 无法使磁盘饱和,则吞吐量可能会由于同步磁盘 I/O 导致的等待时间而受到限制。在这种情况下,并行运行的其他持久队列线程可能能够更加有效地驱动磁盘,这将增加总体吞吐量。
这可以通过在单个 Logstash 进程中并行运行多个(相同的)Logstash 管道,然后对管道之间的输入数据流进行负载平衡来实现。例如,如果我们将 Filebeat 用作 Logstash 的输入源,则可以通过在 Filebeat 中指定多个 Logstash 输出来实现跨多个 Logstash 管道的负载平衡。
下面,我们提供一个标准(未优化)Logstash 持久队列实现的示例,然后是一个改进的实现,该实现由两个并行运行的管道组成。
一个简单的 Logstash 管道
下面提供了一个从 Filebeat 接收数据的 Logstash 管道的示例。如上所述,由于这只是单个管道,因此其持久队列是单线程的,因此其吞吐量可能会受到同步磁盘 I/O 引起的等待时间的限制。
Logstash pipeline:
input {
beats {
port => 5044
}
}
filter {
# Custom filters go here
}
output {
elasticsearch {
hosts => ["http://<elasticsearch hostname>:9200"]
index => "<index name>"
}
}
下面提供了一个示例 Filebeat 配置,该配置可以将数据驱动到上述 Logstash 管道中:
Filebeat configuration:
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/*.log
output.logstash:
hosts: ["<logstash hostname>:5044"]
并行运行多个 Logstash 管道
为了增加驱动磁盘的持久队列线程的总数,我们可以在单个 Logstash 进程中运行多个 Logstash 管道。 例如,Logstash 可以并行执行以下两个管道:
Pipeline #1:
input {
beats {
port => 5044
}
}
filter {
# Custom filters go here
}
output {
elasticsearch {
hosts => ["http://<elasticsearch hostname>:9200"]
index => "<index name>"
}
}
Pipeline #2:
input {
beats {
port => 5045 # Different than Pipeline #1
}
}
filter {
# Same as Pipeline #1
}
output {
# Same as Pipeline #1
}
给定上述 Logstash 管道,可以将 Filebeat 配置为在这些管道之间进行负载平衡。 如果我们的 Logstash 实例正在按照我们刚才描述的方式运行,其中 pipeline#1 在端口 5044 上侦听,流水线 pipeline #2 在端口 5045 上侦听,那么我们的文件信号实例可以配置为在这两个 Logstash pipeline 之间保持平衡,如下所示:
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/*.log
output.logstash:
hosts: ["<logstash hostname>:5044", "<logstash hostname>:5045"]
loadbalance: true
结果
在上述咨询服务中,启用持久队列最初导致性能下降75%,我们创建了四个并行运行的相同 Logstash 管道,并平衡了这4个管道的filebeat 输出。 这导致性能提高高达3万个事件/秒,与没有持久队列的情况相比仅差25%。 此时,磁盘已饱和(根据需要),无法进一步提高性能。
结论
即使基础服务器的资源未达到饱和,Logstash 持久队列中磁盘 I/O 的单线程性质也可能导致性能下降。 要解决此问题,可以并行执行多个相同的 Logstash 管道,并且可以在这些管道之间平衡输入数据。 这样可以增加可以同时写入磁盘的并行线程数量,从而可以提高 Logstash 的整体吞吐量。 在您自己的集群中尝试一下,或者启动 Elasticsearch Service 的14天免费试用,如果你有任何疑问,请在我们的讨论论坛中进行探讨。