logstash是一个具备实时管道功能的开源数据收集引擎。可以动态的统一不同来源的数据。并将数据规范化到选择的目的地。
logstash的执行流程类似消息队列。根据ecs规范将数据包装成为一个event事件。所有输入管道的数据都会被包装放到内存或者磁盘上的中央队列。然后每个管道的工作线程都会从队列中取出一批事件执行。默认情况下,中央队列使用的是内存有界队列。存在丢数据的风险。可以配置持久化
有序性问题
logstash默认情况下是获取一批事件进行处理。这个过程中不保证数据的顺序(1. 同一批次内的事件在过滤期间可以进行重排序。2. 当一批事件的处理速度比其他批次快的时候可以对正在运行的批次重排序)如果要保证有序性,使用单个工作线程(pipeline.ordered ⇒ true)可以保证事件被一个一个计算。
原理
logstash的运行原理比较简单,一个输入、一个输出,中间有一个管道(不是必须的);管道用来收集、解析和转换日志的。
在 Logstash 管道中,每个输入插件都是在独立的线程中读取数据并产生事 件。但输入插件并不会将事件直接传递给过滤器插件或输出插件,而是将事件写 人到一个队列中。队列可以是基于内存的,也可以是基于硬盘的可持久化队列。 Logstash 管道会预先生成一组工作线程,这些线程会从队列中取出事件并在过 滤器中处理,然后再通过输出插件将事件输出。事件队列可以协调输入插件与过 滤器插件、输出插件的处理能力,使得 Logstash 具备了一定的消峰填谷能力。 除了输入插件使用事件队列与过滤器插件、输出插件交互以外,Logstash 还 在输出插
与目标数据源之间提供了一个死信队列(Dead Letter Queue) 。死信 队列会在目标数据源没有成功接收到数据时,将失败的事件写人到队列中以供后续做进步处理。
关键组件
- 队列
大量的突发事件会导致应用访问量在某一时间点呈几何式增长。这种情况下,应用产生的数据也会瞬间爆发,而logstash数据管道要搬运的数据也会突然增加。如果没有应对突然流量的机制,轻则会导致应用数据丢失,重则导致系统直接崩溃,甚至引发雪崩效应,将其他应用一并拖垮。通用的应对突发流量的解决方案是队列。logstash中就存在这样的队列,可以用于应对瞬间流量爆炸、提高系统的可用性。除了输入插件使用的事件队列,输出插件还有一个死信队列。这个队列会将没有发送成功的事件保存到磁盘中。
- 持久化队列
logstash输入插件默认使用的是内存队列,这就意味着logstash一旦因为意外崩溃,那么队列中没有来得及处理的事件就会丢失,除此之外,基于内存队列容量小切不能通过配置进行扩容,所以内存队列起到的缓存作用非常有限。因此可以采用硬盘持久化队列。持久化队列将输入插件发送过来的事件存储在硬盘中,只有过滤器插件或者输出插件处理事件,才会将事件从队列中删除。logstash意外崩溃重启之后也可以重新将未处理的事件取出处理。默认的持久化队列容量为1024MB。一旦容量到达上限,logstash就会控制输入创建插件产生事件的频率或者拒绝接受输入事件直到队列有空闲空间。许多高访问量的系统还会再前面再加一层mq避免系统被流量压垮。
- 死信队列
死信队列位于输出插件和目标数据源之间。如果logstash处理某个事件失败就会将其写入死信队列中。死信队列以文件的形式存储在磁盘中,为失败事件提供了采取补救措施的可能。logstash认为只有目标数据源返回400或者404的时候认为事件失败,目前只有es支持这种逻辑。
虽然死信队列可以缓存一定数量的错误事件,但当容量超过上限的时候还会被删除。所以需要一种机制来处理这些事件。logstash提供了死信队列的输入都插件,可以将死信队列中的事件读取出来传输到另外一个管道处理。
bug
项目中有一个场景通过日志采集的方式将数据入到es,后续业务侧再查询es。但是在数据入库的时候出现了这样一个bug。对同一个文档数据进行你操作的时候先进行的操作后执行完成,导致文档修改停留在了第一次修改上。
经过logstash日志排查以及filebeat日志排查,发现日志正常被采集并且正常处理并发送到了es。一度怀疑是es的问题,猜测可能是es集群的数据一致性方案导致数据没有及时落地。后来经过仔细的排查日志发现,两个事件时间间隔非常近,并且发现第二次操作的日志出现在了前面,这才发现是logstash的批处理机制和多线程机制引起的问题。
解决方案:
- 强制使用单线程处理(大数据情况下会影响性能)
- 利用es的版本控制(es在操作文档的时候会判断版本号,如果该操作携带的是旧的版本号,es就直接拒绝,保证最终状态的一致性;这个方案具有一定的复杂性,应用层需要确保每个事件都包含一个严格递增的版本号)