记一次日志采集链路的验证及优化

搭建基础环境

日志源: 测试用例模拟
生成服务器flume:本机启动
kafka: 运维提供的测试环境的kafka
flume集群:本机启动
es:运维提供的测试环境的es

验证配置文件发生变更是否重新读取

  1. 配置文件发生变更,是否重新加载?
    验证结果:会重新加载,它启动了一个定时任务,每隔一定的时间(默认30),获取文件的最后修改时间。
    如果要修改配置文件,一定要在原文件上修改,不要rm之后,再创建同名新文件。

传输过程可靠性验证

生产服务器上的flume组件配置为 : TailDirSource --> FileChannel --> KafkaSink
消费者组件: kafkaSource --> FileChannel --> ESSink

TailDirSource

主要涉及到三个线程:
FileCheck:检查文件更新时间以及新创建的文件
Reader : read():读取日志 – > process():交付channel --> commit():更新已经读取的文件偏移量
PositionWrite : 定时任务,负责把已经读取的文件的偏移量持久化到磁盘
flume重新启动的时候,只要没有设置skipToEnd,就会通过持久化到磁盘的已读文件信息,断点续传。
但是,Reader 和 PositionWrite 之间并没有同步机制,commit仅仅是修改内存,不会主动调用PositionWrite刷新到磁盘,所以理论上:TailDirSource 可以做到不漏数据,但无法做到不重复。​

FileChannel

在MemberChannel的基础上,增加了写入日志文件的步骤(NIO的channel顺序写)。写入成功之后返回文件编号以及偏移量,放入内存队列中,take操作也会记录日志。如果意外挂掉,重新起来时会根据日志进行回放。

KafkaSink/kafkaSource/ESSink
 基本是都是用了官方的sdk
正常情况下的实验:

模拟程序通过logback写入100W条日志。
结果:日志最终能够正常流入es,并且没有发生丢失和重复的情况。不过es的延迟是一个陡峭的上升曲线(如下图),说明日志堆积很严重。用户此时可能查询不到最新日志。
flume-taildir读取一批(50条)日志的耗时在1-2毫秒之间
kafka的性能也很好,基本是没感受到压力。
ES的写入性能是最终的瓶颈,观察es的机器(3cpu),发现load已经到10了

模拟程序flume-taildirkafka-sinkkafka-sourcees-sink
耗时(秒)9153797112.8分钟
延迟(毫秒)0500未采集120010.5分钟

image.png | left | 747x424

模拟消费者组件(kafkaSource --> FileChannel --> ESSink)挂掉重启的情况

总共kill然后重启消费者组件两次,最后收集到的日志数量多了1000条,而ESSink设置的批次量正好是500. 显然,幂等性要自己实现。

image.png | left | 747x84

通过生成UID作为es的id实现幂等

ES支持索引的时候指定ID,如果根据每条日志的特征,生成唯一ID,就不会存在数据重复现象。
UID = ip(最后一节) + inode(linux文件标识) + dd(月份中的天) + pos(字节偏移量)
UID的生成放在flume读取日志的taildir模块
经过多次试验,采用自生成的uid作为key,是可以避免数据重复的。
通过下图的表格中的数据也可以看出,使用自生成的uid,写入es的性能会下降6%左右,这是因为ES需要确认这个id是否存在。

image.png | left | 747x66

模拟程序flume-taildirkafka-sinkkafka-sourcees-sink
9155756913.5分钟
0500未采集111911.9分钟
日志传输过程中实现去重(没能实现)

ES写入作为性能最终的瓶颈,再把去重工作交给它,整个链路的性能会再次下降。有没有办法在传输过程中实现去重呢?
布隆过滤器:牺牲一定的最准确
redis存储键值对:功能上能够实现,但是这样会占用大量的内存空间(key为string,value为int)
redis 的bitmap能够在性能和内存占用上满足需求,不过如果采用字节偏移量作为bitmap的offset,也会存在大量的内存浪费。所以我们应该采用类似于行号这种连续数据,并且一定要随着flume的position文件保存到磁盘,下次启动时能够连续。
这个方案最终没能实现,问题的关键是去重操作只能放在ESSink模块中,即使提高了ES写入的性能,数据准备阶段耗时增加了,也没有任何意义。

模拟生产者组件(TailDirSource --> FileChannel --> KafkaSink)挂掉重启的情况

有点出乎意料,居然还是发生了数据丢失现象,而且只读取到了649297,丢失了2/5。利用kafka数据回放的能力,换了一个groupID 进行消费,依然只读取到了649297,可以确实是生产端没有投递。

image.png | left | 343x249

丢失的数据太多,不太可能是交付的时候没有成功就被kill掉引起的,比较怀疑是日志文件滚动引起的。首先把日志文件调大,不让它滚动进行验证。
可以看到投递了1019040条记录,而ES中之索引了1000000条,重复的日志已经去掉。

image.png | left | 351x241

image.png | left | 723x140

还有一个滚动日志的坑,最后发现是因为我配置的flume收集只指定了plugin.log,滚动生成的日志没有覆盖,配成plugin.*就好了。又想到了另一个问题,logback支持gz,这种情况下会出问题吗?试了下果然有坑。。。。这个后面在解决,先知道就好

ES写入性能优化

__refresh_interval __
新索引的数据,不会被立即查询到,只有等把内存中的数据,刷新到磁盘,建立一个新的segment,才能被搜索到该。该属性就是控制刷新到磁盘的频率,默认为1s,增大该属性可以有效降低IO,并且减少后期合并segment的压力。
修改为10s,写入性能显著提升,最终只用了七分钟,而且es的写入延迟也只有五分钟左右了。显然,这个值设的越大,es的写入性能就会越高,最终设为多少,等到了生产环境观察ES的写入延迟,只要小于写入延迟就是合理的。
JVM
es默认的jvm内存为1G,增加到3G,测试之后写入性能几乎没有提升,估计在查询性能上会有比较明显的提升吧,比较多了很多内存作为cache。
LoadBalancing Sink Processor
使用SinkProcessor配置两个个sink,多线程写入,最终时间为3.5分钟,延迟也只有3分钟了,显然写入时间还没有优化到极限,等到生成环境在测试最佳的写入线程比例。

待验证特性

Kafka Channel : flume支持使用kafka作为channel,这样比kafka作为sink减少了一步,理论上性能更高,待验证
Kafka降级: 当kafka出现故障时,是否选择跳过kafka,直接放给flume处理集群?有没有机制保障不引发雪崩?
ES索引创建策略: 当前每个应用每天都会创建一个新的索引,观察线上发现有的索引很大(40G),有的很小只有几KB。很显然这并不合理,创建过多的小索引对性能也有影响,所以后面的索引创建逻辑打算改成:
先查看下之前索引大小—>大于20G,则创建新的 —>小于20G,则只创建新的别名指向之前的。
创建别名是为了方便业务开发查看日志

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值