之前一直在团队中接触监控平台,并没有做相关的业务项目,经过这一段时间以来,对流式计算有了一个大体的认识,之后的深入学习还要继续,今天算是系统的将这个项目整理了一遍。
这个工程各个团队公司都会或多或少的接触过。主要是将团队中所有业务日志经storm集群流式计算,最后在平台展现一些重要的监控指标,例如异常率,执行次数,平均执行时间等等。最后将这些指标数据在web端进行展示(主要是利用SpringBoot+Vue.js)。目前storm主流的有两个,一个是国外的,另一个是阿里自研的,这里所指的是国外的storm,其实都大同小异,包括Flink,spark streaming,其思想和storm都差不多。
下图是我画的这个日志监控系统的整体架构图,通过这样的一个项目,在实时大量数据的产生情况下,可以对流式计算有了一个初步的理解。
一.日志采集阶段
所有的业务日志都会存到kafka中,那么它是如何存入到kafka中的呢?我们知道log4j是一个很好的处理日志的开源框架,它主要靠三个重要组件,Logger,Appender和Layout。Logger是日志记录器,负责收集处理日志记录。Appender是日志输出目的地,负责日志的输出,主要是输出到什么地方,项目工程中每产生一条日志数据,就规定它追加到Appender末尾,之后在将Appender中所有的数据自动传输到kafka,Layout组件是日志格式化,负责对输出的日志格式化(以什么形式展现)
二.消息中间件kafka(之后日志数据的源头)
关于kafka详细的介绍,官方文档写的很清楚,在这里就不一一阐述了,重点来说一下它作为一个分布式流处理平台,如何安置这些流数据的。Kafka有一个核心概念:提供一串流式的记录— topic ,Kafka中的Topics总是多订阅者模式,一个topic可以拥有一个或者多个消费者来订阅它的数据。对于每一个topic, Kafka集群都会维持一个分区日志,如下所示:
每个分区都是有序且顺序不可变的记录集,并且不断地追加到结构化的commit log文件。分区中的每一个记录都会分配一个id号来表示顺序,我们称之为offset,offset用来唯一的标识分区中每一条记录。
另外一方面kafka之所以大家喜欢用,主要是它的读写速度很快,其实kafka中的数据是保存在磁盘上的,我们知道,磁盘的读写性能一般要弱于读写内存性能,然而,实际上,快或慢关键在于寻址的方式,磁盘分为顺序读写与随机读写,内存也一样分为顺序读写与随机读写。基于磁盘的随机读写确实很慢,但磁盘的顺序读写性能却很高,一般而言要高出磁盘随机读写三个数量级,一些情况下磁盘顺序读写性能甚至要高于内存随机读写。磁盘的顺序读写是磁盘使用模式中最有规律的,并且操作系统也对这种模式做了大量优化,Kafka就是使用了磁盘顺序读写来提升的性能。Kafka的message是不断追加到本地磁盘文件末尾的,而不是随机的写入,这使得Kafka写入吞吐量得到了显著提升。
此外,在写入数据的时候,kafka采用了MMFile(Memory mapped file)文件的形式,即便是顺序写入硬盘,硬盘的访问速度还是追不上内存,所以kafka并不是实时的写入硬盘,充分利用了操作系统现代分页存储技术来利用内存提高IO效率。Memory Mapped Files(后面简称mmap)也被翻译成 内存映射文件 ,在64位操作系统中一般可以表示20G的数据文件,它的工作原理是直接利用操作系统的Page来实现文件到物理内存的直接映射。
完成映射之后你对物理内存的操作会被同步到硬盘上(操作系统在适当的时候)。
通过mmap,进程像读写硬盘一样读写内存(当然是虚拟机内存),也不必关心内存的大小有虚拟内存为我们兜底。
使用这种方式可以获取很大的I/O提升,省去了用户空间到内核空间复制的开销(调用文件的read会把数据先放到内核空间的内存中,然后再复制到用户空间的内存中。)
但也有一个很明显的缺陷——不可靠,写到mmap中的数据并没有被真正的写到硬盘,操作系统会在程序主动调用flush的时候才把数据真正的写到硬盘。
Kafka提供了一个参数——producer.type来控制是不是主动flush,如果Kafka写入到mmap之后就立即flush然后再返回Producer叫 同步 (sync);写入mmap之后立即返回Producer不调用flush叫异步 (async)。
在读取数据方面,kafka基于sendFile实现了少量的copy,传统的网络文件传输过程是
硬盘—>内核buf—>用户buf—>socket相关缓冲区—>协议引擎,可以看到,经历了4次的copy,而通过sendFile的方式,减少了两次的copy操作。
Kafka把所有的消息都存放在一个一个的文件中,当消费者需要数据的时候Kafka直接把文件发送给消费者,配合mmap作为文件读写方式,直接把它传给sendfile。
最后,kafka也采用了批量压缩的方式。在很多情况下,系统的瓶颈不是CPU或磁盘,而是网络IO,对于需要在广域网上的数据中心之间发送消息的数据流水线尤其如此。进行数据压缩会消耗少量的CPU资源,不过对于kafka而言,网络IO更应该需要考虑。
1、如果每个消息都压缩,但是压缩率相对很低,所以Kafka使用了批量压缩,即将多个消息一起压缩而不是单个消息压缩
2、Kafka允许使用递归的消息集合,批量的消息可以通过压缩的形式传输并且在日志中也可以保持压缩格式,直到被消费者解压缩
3、Kafka支持多种压缩协议,包括Gzip和Snappy压缩协议
Kafka速度的秘诀在于,它把所有的消息都变成一个批量的文件,并且进行合理的批量压缩,减少网络IO损耗,通过mmap提高I/O速度,写入数据的时候由于单个Partion是末尾添加所以速度最优;读取数据的时候配合sendfile直接暴力输出。
三.storm处理流数据
storm是一个比较强大的分布式流计算框架,一些流式数据经过storm集群预处理,计算,存储合并,得到我们想要的一些指标数据,最后持久化到mysql或者nosql数据库中。
先说几个组件的概念,有利于理解storm计算过程。
topology:storm的运行是依靠 topology(拓扑)来进行的,开始时会新建一个 topology(拓扑),storm结束时将会杀死这个topology
spout:用来管理storm集群的各种输入流
bolt:是处理逻辑组件,spout把数据传递给bolt,bolt可以进行计算,聚合,过滤,查询等等。
每个topology中会有spout,bolt等等,简单拓扑从spouts开始。Spouts将数据发射到一个或多个Bolts,并且Bolts的输出可以发射到另一个Bolts作为输入Spouts和Bolts连接在一起,形成拓扑结构。实时应用程序逻辑在Storm拓扑中指定。
有了这几个组件的概念之后,我们再来看storm集群的计算过程,究竟是如何将日志数据流变成我们最后想要的一些指标数据。首先storm去消费kafka中的数据的第一步是建立拓扑,拓扑建立一个spout来收集kafka中的数据流,可以把spout理解为收集物料的作用,之后按照图中所示,建立第一个bolt,它的作用是反序列化,将从spout源中流出的数据流进行反序列化的处理,当第一条反序列化成功的对象过来之后,会进行对象转换并进行属性值的初始化,然后存起来,当下一条消息数据过来之后,如果方法名相同,则进行相加更新操作,当到达一个时钟周期时,将收集的所有对象先发送到下一个bolt,同理,之后的操作会按照图中那样,在各个bolt之间进行处理(其实经历了反序列化bolt,计算bolt,合并bolt,存储bolt),在各个bolt之间的数据交接和传递过程中,并不会像mapreduce那样,批量的处理和传递数据,storm实时性还是很强的,它只是会在每个时钟周期点将收集的数据发送至下一个bolt。在最后一个bolt处理完成后,会将处理好的数据存入Hbase,一些产生的静态数据存入Mysql。
四.web展示
最后展示在web上的数据主要有两部分,一部分是那些核心指标,例如异常率,执行次数,平均执行时间等,这一部分数据主要是经过storm计算后,从Hbase里面取。另一部分数据是经storm计算后,持久化之后的一些静态数据,例如产品线,服务名,方法名,报警配置信息。
展示的话后端用了SpringBoot,持久层Mybatis,前端利用Vue和elementUI来搭建的。