背景
为了应对流量,保证服务的可用性和水平扩展性,现在服务节点多数都是无状态的,集群的方式部署在云上。
当流量被分发在各个机器接点上时,对应的日志同样也写在各个节点的文件里。
因为当集群规模很大的时候,不可能登录到每台机器上去查看日志,所以应该有一个公共的日志组件,来将日志收集到集中的一个地方。
一. 需求分析
首先考虑日志的应用场景,大致有以下几个作用:
- 业务方记录,排查问题
- 通过日志进行流处理分析,动态调整某些策略(例如用户行为日志)
- 根据日志产生离线统计分析报表
通过以上分析可得知,这个组件大致需要包含以下特点:
- 可以准实时的提供给多个使用方
- 收集的操作不侵入业务,保证业务代码只是写日志这一个操作
- 尽可能的不丢日志(允许丢弃少量的情况发生)
- 至少能够保存7-15天日志记录
- 在落盘前,对数据进行清洗过滤
- 能够多维度,准实时进行日志查询分析
- 能够根据日志,进行离线数据分析
ok,大致需求分析完毕,下面进行具体方案的落地
二. 确定方案
首先想到的结构就是logstash + es + kibana的经典组合
- es做日志数据的落地存储
- logstash以日志文件作为输入,es作为输出端
- kibana界面化操作展示es数据
这套方案已经非常的成熟了,但是方案选型不光要考虑成熟程度,还需要结合公司实际的支持程度进行适配,所以需要对这个方案进行一些调整。
1. 日志入kafka
由于现在公司内服务节点都上了云,对于节点容器公司支持的是flume收集日志,输出源支持「kafka」和「hdfs」文件。
我们的数据需要离线分析,需要落到hdfs,但是需要通过格式校验&清洗之后才能落入。
并且hdfs这只是其中一个数据落地流程,还需要支持多方的日志消费,所以最好的方法是用消息中间件来进行解耦。
通过将日志推入kafka中,各个使用方来订阅topic,达到流程解耦,多流程并行处理的目的。
2. kafka入es
将每条日志以消息形式推入kafka中后,我们需要将消息落入es当中,供kibana的图形界面查询。
2.1 方案选择
消费kafka消息写es的这个过程,可以自由搭建实现,不受限于公司技术支持,可以有以下方式可选
- filebeat
- logstash
- flume
- 个人开发消费
这里需要注意的是,在落入数据之前,要有一个格式校验&清洗的过程。
2.2 放弃logstash
这里第一版的方案定的流程是
- 使用kafka自带shell脚本拉取消息到本地文件A
- 通过脚本执行自定义过滤清洗逻辑,写入固定目录下分片文件B
- logstash监听固定目录push到es中
这个方案上线一段时间后存在几个问题
- 太重
消费消息->清洗数据->写入es这个过程其实是一个很简单的流处理形式,但是由于是跑在jvm上,机器的内存占用和cpu占用都偏高。
- 每条消息都需要写一次es
网络io密集,es集群压力大,导致需要控制写入速度,在流量高的时候消费延迟就会很高。
需要做的是将消息打包,批量提交到es集群,减轻es的压力,提高消费速度。
2.3 变更方案
最终选择使用go开发一个消费程序,理由是go语言对io频繁的场景比较适配,并且自定义清洗逻辑和写入的规则都能由自己支配,更加灵活。
消费程序的整体流程借鉴了一个工作流的实现
首先主协程根据主题的分区数来确定需要启动工作流的协程数
然后按照下图所示的流程进行消息的积攒批量发送
消息延迟量效果
优化前:平均4w左右
优化后:平均1.5k内
3. kafka入hdfs
es非常依赖机器的内存资源,存储7-15天的数据还好,但是要存长时间的海量数据就不合适了。
为了能够离线统计分析和产出报表,还需要将日志持久化存储到hdsf中,做一个长久的存储。
公司大数据平台提供了flink集群和hdfs的支持,只需要在中间定义一个消费写入的java代码即可,过程很简单就不再赘述。
4. 最终方案
- 业务侧将消息以json形式写入日志
- flume监听日志文件,增量推入kafka
- go消费程序接收消息->清洗过滤->批量写入es
- java消费程序接收消息->清洗过滤->写入hdfs
三. 上线效果
日均日志条数:3.5亿
es写入次数:90w+
实时性:秒级
延迟情况:流量峰值最高7k