头图

1. 定义

Flume全称Apache Flume

  • 技术角度:使用Java语言开发的一个分布式、高可靠、高可用中间件
  • 项目角度:最早是Cloudera提供的日志收集系统,现在是Apache软件基金会(ASF)的顶级项目,是Hadoop生态圈中的一个组件。当前Flume有两个版本Flume 0.9X版本的统称Flume-og,Flume1.X版本的统称Flume-ng。由于Flume-ng经过重大重构,与Flume-og有很大不同,使用时请注意区分。
  • 产品角度:用来收集、聚合、转移不同来源的大量日志数据到中央数据仓库的工具(Flume是一种日志采集工具)

官方文档:https://flume.apache.org/rele...
民间中文网:https://flume.liyifeng.org/

2. 使用背景

2.1 为什么要进行日志数据采集?

在现在的数据技术时代中,数据有着不可替代的地位,抛开数据谈大数据服务就是瞎扯,没有数据作支撑的大数据平台就是一个空壳。数据是一切数据分析、数据挖掘、大数据处理、ai算法的核心。

在目前的来看,绝大多数公司或者组织做大数据处理时,他们的数据来源于:设备收集、数据库、日志、爬虫等等。

“只有在程序出问题以后才会知道打一个好的日志有多么重要”,因此现在许多公司的业务平台都有非常严格的日志规范,每天都会产生大量的日志数据,因此这些日志数据中就隐含着巨大的价值。

2.2 常用的日志数据采集工具

类型LogstashFluentdFilebeatFlume
运行环境javarubygojava
资源环境非常小
配置简单复杂简单较复杂
可靠性不稳定可靠稳定高可靠

3. 初步认识并使用Flume

用一个故事理解:有一个池子,它一头进水,另一头出水,进水口可以配置各种管子,出水口也可以配置各种管子,可以有多个进水口、多个出水口。水术语称为Event,进水口术语称为Source、出水口术语成为Sink、池子术语成为Channel,Source+Channel+Sink,术语称为Agent。

1635837643(1).png

Flume的名词详细介绍

  • Event
数据在flume内部是以Event封装的形式存在的。因此source组件在获取到原始数据后,需要封装成Event后发送到channel中,然后sink从channel取出Event后,根据配置要求再转成其他的形式进行数据输出。
Event封装的对象主要有两部分:Headers和Body;
Headers是一个集合Map类型,用于存储元数据(如标志、描述等)
Body就是一个字节数组,装载具体的数据内容

1635991222(1).png

  • agent
flume最核心的角色就是agent。
对于每个agent来说就是一个独立的守护进程(JVM),它负责从数据源接收数据,并发送到下一个目的地。
agent内部有三个重要的组件:source,channel,sink
  • source
Source是数据的收集端,负责将数据捕获后进行特殊的格式化,将数据封装到事件(event) 里,然后将事件推入Channel中。Flume提供多种数据接收方式,比如直接读取本地文件,Avro,Thrift等。

1636022802(1).png

  • channel
channel是一种短暂的存储容器,它从source处接收到event格式数据后进行缓存,直到被消费掉。它在source和sink之间起到了桥梁作用。
支持的类型有:FileSystem Channel,Memory channel,Kafka channel等。
  • sink
sink将数据存储到集中存储器比如Hbase和HDFS,它从channels消费数据(events)并将其传递给目标地. 目标地可能是HDFS,HBase等.

Flume安装包可以直接从官方网站下载 https://flume.apache.org/down...,下载后上传服务器直接解压即可。

Flume启动基本上是基于本地配置文件的(遵循Java properties文件格式的文本文件),目前还有一个实验性的功能可以基于Zookeeper的在线配置。

Flume每隔30秒轮询扫描一次指定配置文件的变动。如果首次扫描现有文件或者上次轮询时的文件「修改时间」发生了变动,Flume Agent就会重新加载新的配置内容。

基于以上这些,可以进行一个简单的demo,实时监控服务器/root/tmp.txt文件,将收集到的信息打印到控制台。

a1.sources = r1
a1.channels = c1
a1.sinks = k1

a1.sources.r1.type = TAILDIR
a1.sources.r1.channels = c1
a1.sources.r1.filegroups = f1
a1.sources.r1.filegroups.f1 = /root/tmp.txt

a1.channels.c1.type = memory

a1.sinks.k1.type = logger
a1.sinks.k1.channel = c1

flume启动命令
bin/flume-ng agent -n a1 -c conf -f conf/example.conf -Dflume.root.logger=INFO,console

4. Flume组件详解

4.1 数据序列化与avro

为什么需要数据序列化呢?因为数据要“传输”,比如将数据网络通信传递给其他服务器,或者持久化到磁盘。那么传输为什么需要序列化呢?因为在内存中的数据,当前进程是知道数据格式和内容的,但是数据传输是二进制(或文本格式),所以需要有一个内存数据格式转换为二进制(或文本格式)的过程。数据序列化,可以进行数据压缩、数据格式多语言兼容等。下面就按照序列化的技术演变过程一起看下序列化的发展之路。

  • 文本格式序列化
    直接将数据转变为文本格式,也就是字符串形式的文本保存,如果一个数据存在多个字段可使用固定分隔符(比如",")分隔,该方案存储简单但是针对复杂对象比如嵌套数据,存储起来较为麻烦,并且无法表示本来就是二进制格式的数据,比如图片、视频等。
  • 语言内置序列化
    目前编程语言基本都内置了至少一种数据序列化机制,比如Java的 Serialization、Python的pickle等,语言内置的序列化机制大都和该语言绑定,也就是说反序列化也必须是该语言才行,因此很难做到跨语言的读取写入。
  • 跨语言序列化
    语言内置的序列化大都和语言绑定,因此有了应用范围广泛、跨语言的数据序列化格式,例如json、xml等,但是这些跨语言序列化方案存在的最大劣势就是有性能问题,并且无数据类型信息,同时数据序列化冗余较大,比如json会保存每个属性名字。除了json和xml这种跨语言序列化之外,还有类似于hession这种跨语言序列化(这是序列化框架本身支持跨语言,目前只会Python/Java/C++等)的机制,序列化数据是二进制格式,并且包含数据类型信息。
  • 带schema描述序列化
    带有schema描述的数据表示格式,通过统一化的schema描述,可约束每个字段的类型,进而为存储和解析数据带来优化的可能。此外,统一schema的引入,可减少属性名称重复存储带来的开销,同时,也有利于数据共享。带有schema描述的序列化常用的有Thrift、Protocol Buffers和Avro,它们一般被称为Language Of Data,使得跨语言序列化成为可能,并且它们提供了代码生成工具,方便为开发者生成各个语言的代码。Language Of Data具有特性如下:

    1. 提供IDL(Interface Description language)用以描述数据schema,用来定义结构化或者非结构化数据;
    2. 跨语言支持,至少支持Java、Python和C/C++;
    3. 数据编码压缩,比如字符串压缩和整数变长编码等;
    4. 数据序列化兼容,保证序列化的向后兼容性,比如旧schema序列化的数据可由新schema反序列化,新schema序列化也可以由旧schema解析等。
  • Avro
    Avro([ævrə])是Hadoop的一个子项目,由Hadoop的创始人Doug Cutting(也是Lucene,Nutch等项目的创始人)牵头开发。Avro是一个数据序列化系统,设计用于支持大批量数据交换的应用。它的主要特点有:支持二进制序列化方式,可以便捷,快速地处理大量数据;动态语言友好,Avro提供的机制使动态语言可以方便地处理Avro数据。

4.2 Source配置

SourceDesc
Avro Source通过监听一个网络端口来接受数据,而且接受的数据必须是使用avro序列化框架序列化后的数据;
Thrift Source通过监听一个网络端口来接受数据,而且接受的数据必须是使用thrift序列化框架序列化后的数据;
HTTP Source监听一个网络端口,通过http post/get来接收数据,GET方式目前还只是实验性的;该source基于Jetty 9.4,可以解析为JSON或Blob
NetCat Source监听一个网络端口,获取通过TCP或UDP协议的数据
Kafka SourceKafka Source就是一个Apache Kafka消费者,它从Kafka的topic中读取消息。 如果运行了多个Kafka Source,则可以把它们配置到同一个消费者组,以便每个source都读取一组唯一的topic分区。
Exec Source启动一个用户所指定的linux shell命令;采集这个linux shell命令的标准输出,作为收集到的数据,转为event写入channel
Spooling Directory Source监视一个指定的文件夹,如果文件夹下有没采集过的新文件,则将这些新文件中的数据采集,并转成event写入channel;注意:spooling目录中的文件必须是不可变的,而且是不能重名的!否则,source会loudly fail!
Taildir Source监视指定目录下的一批文件,只要某个文件中有新写入的行,则会被tail到;它会记录每一个文件所tail到的位置,记录到一个指定的positionfile保存目录中,格式为json(如果需要的时候,可以人为修改,就可以让source从任意指定的位置开始读取数据);它对采集完成的文件,不会做任何修改
Syslog Sources读取系统日志数据生成event。
JMS Source从JMS目标(例如队列或主题)读取消息;作为JMS应用程序,它应可与任何JMS提供程序一起使用,但仅经过ActiveMQ的测试;注意,应该使用plugins.d目录(首选),命令行上的–classpath或通过flume-env.sh中的FLUME_CLASSPATH变量将提供的JMS jar包含在Flume类路径中
Stress SourceStressSource 是一个内部负载生成Source的实现, 对于压力测试非常有用 。可以配置每个Event的大小(headers为空)、也可以配置总共发送Event数量以及发送成功的Event最大数量。
Custom Source自定义source

4.3 Channel配置

ChannelDesc
Memory Channelevent存储在内存中,且可以配置最大值。对于需要高吞吐而且可以容忍数据丢失的情况下,可以选择该channel
File Channelevent被缓存在本地磁盘文件中;可靠性高,不会丢失;
Kafka Channelagent利用kafka作为channel数据缓存;kafka channel要跟 kafka source、 kafka sink区别开来;kafka channel在应用时,可以没有source
JDBC Channelevent被持久到数据库中,目前支持derby.适用于可恢复的场景
Spillable Memory Channelevent存储在内存和磁盘上。内存充当主存储,磁盘充当溢出。这个channel目前是实验性的,不建议用于生产环境 。
Custom Channel自定义channel

4.4 Sink配置

SinkDesc
Null Sink直接丢弃
Logger Sink数据输出到日志中,通常用于debug
File Roll Sink数据存储到本地文件系统
HDFS Sink数据被最终发往hdfs;可以生成text文件或 sequence 文件,而且支持压缩;支持生成文件的周期性roll机制:基于文件size,或者时间间隔,或者event数量;目标路径,可以使用动态通配符替换,比如用%D代表当前日期;当然,它也能从event的header中,取到一些标记来作为通配符替换
Hive Sink可将text或json数据直接存储到hive分区表
HBaseSink数据存储到hbase中
ElasticSearchSink直接存储到es中
Kafka Sink存储到kafka中
Kite Dataset Sink将事件写入Kite数据集。该接收器将反序列化每个传入事件的主体,并将结果记录存储在Kite数据集中。它通过按URI加载数据集来确定目标数据集。(kite是专门来操纵大数据集的,可以通过kite将数据存储到hdfs、local、hive、hbase中,并且还提供了partition分区机制,加快数据访问速度。并且kite支持avro、parquet、csv、json等几种存储数据的方式。)
MorphlineSolrSink(Solr是一个独立的企业级搜索应用服务器)该接收器从Flume事件中提取数据,对其进行转换,并将其几乎实时地加载到Apache Solr服务器中,后者再为最终用户或搜索应用程序提供查询
Avro Sinkavro sink用来向avro source发送avro序列化数据,这样就可以实现agent之间的级联
Thrift Sink同avro sink
HTTP Sink将接收到的数据通过post请求发生到远程服务,event内容作为请求体发送
IRC Sink因特网中继聊天(Internet Relay Chat),一般称为互联网中继聊天,简称:IRC。它是由芬兰人Jarkko Oikarinen于1988年首创的一种网络聊天协议。
Custom Sink自定义sink

4.5 拦截器 Interceptor

1635849972(1).png
拦截器,就是工作在source之后,可以从source获得event,做一个逻辑处理,然后再返回处理之后的event。这也就可以让用户不需要改动source代码的情况下,插入一些处理逻辑。学过java的同学对拦截器应该比较清楚了。具体类型如下:

InterceptorDesc
host往event的header中插入主机名信息
timestamp向event中,写入一个kv到header里;key的名称可以随意配置,value就是当前时间戳
uuid用于在每个event header中生成一个uuid字符串
static静态属性写入拦截器让用户往event中添加一个自定义的header,key-value形式的,当然这个kv在配置文件中是写死的
remove_header删除属性拦截器这个拦截器可以删除Event header里面的属性,可以是一个或多个。
search_replace查找-替换拦截器该拦截器基于Java正则表达式提供简单的基于字符串的搜索和替换功能;类似于Java中的Matcher.replaceAll方法
regex_filter正则过滤拦截器这个拦截器会把Event的body当做字符串来处理,并用配置的正则表达式来匹配。可以配置指定被匹配到的Event丢弃还是没被匹配到的Event丢弃。
regex_extractor正则提取拦截器这个拦截器会使用正则表达式从Event内容体中获取一组值并与配置的key组成n个键值对,然后放入Event的header中,Event的body不会有任何更改。
Morphline 实时清洗拦截器此拦截器通过 morphline配置文件 过滤Event,配置文件定义了一系列转换命令,用于将记录从一个命令传递到另一个命令。
custom type as FQCN自定义实现拦截器

4.6 Source通道选择器(Flume Channel Selectors)

微信图片_20211103102115.jpg

一个source可以对接多个channel,那么问题来了,source的数据是怎么在多个channel之间进行传递的呢?这就是selector的功能了,通过selector选择器根据策略可以将event从source传递到指定的channel中去。具体的selector选择器类型如下表格:

SelectorDesc
replicating 复制选择器默认的选择器,将event进行复制分发给下游所有的节点
Multiplexing 多路复用选择器多路选择器,可以根据event中的一个指定key对应的value来决定这条消息会被写入到那个哪个channel中
Custom Selector自定义选择器

4.7 Sink组逻辑处理器(Flume Sink Processors)

src=http___img2018.cnblogs.com_blog_1522745_202001_1522745-20200126141539318-516906624.jpg&refer=http___img2018.cnblogs.jfif

一个agent中,多个sink可以被组装到一个组中,而数据在组内多个sink之间发送。接收处理器可以在组内提供负载均衡的功能,或者是在临时故障的情况下实现从一个接收器转移到另一个接收器上。具体的接收器模式见下表格:

ProcessorDesc
default默认的接收处理器仅接受一个sink,当然用户也没有必要为了一个sink去创建processor
Failover故障转移模式,即一个组内只有优先级高的sink在工作,而其他的sink处于等待中
load_balance负载均衡模式,允许channel中的数据在一组sink中的多个sink之间进行轮转,具体的策略有:round-robin(轮流发送);random(随记发送)
Custom processor目前还不支持自定义Sink组逻辑处理器

5. Flume进阶

5.1 Flume的高可靠性

事务是flume高可靠性的保证

首先我们先来了解一下消息传递的可靠性保证的三种方式:

  1. At-Least-Once
  2. At-Most-Once
  3. Exactly-Once

基本上所有工具的使用用户都希望工具框架能保证消息 Exactly-once ,这样就不必在设计实现上考虑消息的丢失或者重复的处理场景。但是事实上很少有工具和框架能做到这一点,真正能做到这一点所付出的成本往往很大,或者带来的额外影响反而让你觉得不值得。假设 Flume 真的做到了 Exactly-once ,那势必降低了稳定性和吞吐量,所以 Flume 选择的策略是 At-least-once 。
Flume采用的是At-Least-Once策略,这里并不是说Flume任意一个组件就可以实现这种策略,而是通过三个组件(source,channel,sink)之间上下投递消息来保证,但是如果选择了Memory Channel的话,这个就不敢打包票能实现At-Least-Once了。Flume要保障at-least-once的基础就是Transaction。

  • Source到Channel是事务性的
  • Channel到Sink也是事务性的

1635928083(1).png

flume的事务工作流:
1635937153(1).png

这里再详解一下上图的流程,Flume的transaction是有生命周期的,分别是start、commit、rollback和close.

当source往channel投递事件的时候,会首先调用start方法开启事务,将event怼入putList中,此时putList会包裹在一层事务中(为了数据传输的原子性),当putList的数据成绩怼入channel,进行事务commit确认,当putList中的数据怼入到channel失败的时候(通常表现为channel中的数据已满了,putList中的数据没办法在放进channel里面,这里需要责怪设定channel capacity的那个家伙),进行事务rollback回滚(会将当前的putList清空,source将刚才提交的消息事件向源头进行ack确认,需要上游具备消息重发功能),然后close事务。

sink消费channel也是同样的模式,唯一不同的地方是sink在往目标源完成写入后才会对事务进行commit,通常为下游的存储系统宕机导致信息无法发出,此时会执行事务回滚(会将当前的takeList清空,并将takeList的信息回滚到channel中,如果此时channel也是满的,直接报错)。

数据可能会重复

Flume 保证事件至少一次被送到它们的目的地,只有一次倾力写数据,且不存在任何类型的故障事件只被写一次。但是像网络超时或部分写入存储系统的错误,可能导致事件不止被写一次,因为Flume 将重试写操作直到它们完全成功。

5.2 Flume的分布式

flume灵活的source和sink组件,使得多个agent之间可以进行数据通信,这也是flume扩展性和分布式的基础,可以支持大量的部署方案。Flume内置了专门的RPC sink-source对。对于agent-to-agent通信,首选的RPC sink-RPC source对是Avro Sink-Avro Source对。要从其它的Flume Agent或客户端接收数据,Agent接收数据可以被配置为使用Avro Source,且发送数据的Agent必须配置用来运行Avro Sink。

1635992361(1).png

日志收集场景中比较常见的是数百个日志生产者发送数据到几个日志消费者Agent上,然后消费者Agent负责把数据发送到存储系统。例如从数百个web服务器收集的日志发送到十几个Agent上,然后由十几个Agent写入到HDFS集群。

1635992478(1).png

日志生产者到日志消费者的数据传输通常使用负载均衡的方式提高整个集群的吞吐量和稳定性。

1635994431(1).png

如果生产时间的服务器数量持续增加,该层从应用程序服务器接收数据的agent数量也需要增加。这意味着在某种程度上,它可能需要增加后续层来增加缓冲容量,以适应数据产量的增加。

1635994741(1).png

Flume支持多路复用数据流到一个或多个目的地。这是通过使用一个流的[多路复用器](multiplexer)来实现的,它可以 复制 或者 选择(多路复用) 数据流到一个或多个channel上。

1635992603(1).png

使用flume的channel选择器可以根据需要将数据复制或分组发送到不同的目的地

1635994927(1).png

根据官方的建议:向HDFS发送数据的Agent数量尽量不要超过8个;上层和下层的数量比率不要超过32:1。

Flume真的适合你吗?
如果你需要将文本日志数据提取到Hadoop / HDFS中,那么Flume最合适不过了。但是,对于其他情况,你最好看看以下建议:
Flume旨在通过相对稳定,可能复杂的拓扑部署来传输和收集定期生成的Event数据。“Event数据”定义非常广泛,对于Flume来说一个Event就是一个普通的字节数组而已。 Event大小有一些限制,它不能比你的内存或者服务器硬盘还大,实际使用中Flume Event可以是文本或图片的任何文件。关键的是这些Event应该是以连续的流的方式不断生成的。 如果你的数据不是定期生成的(比如你将大量的数据批量加载到Hadoop集群中),虽然Flume可以做这个事情,但是有点“杀鸡用牛刀”的感觉,这并不是Flume所擅长和喜欢的工作方式。 Flume喜欢相对稳定的拓扑结构,但也不是说永远一成不变,Flume可以处理拓扑中的更改而又不丢失数据,还可以处理由于故障转移或者配置的定期重新加载。如果你的拓扑结构每天都会变动, 那么Flume可能就无法正常的工作了,毕竟重新配置也是需要一定思考和开销的。

5.3 Flume的监控

Flume作为一个强大的数据收集工具,虽然功能非常强大实用,但是却无法看到flume收集数据的详细信息,所以我们需要一个能展示flume实时收集数据动态信息的界面,包括flume成功收集的日志数量、成功发送的日志数量、flume启动时间、停止时间、以及flume一些具体的配置信息,像通道容量等,于是顺利成章的监控能帮我们做到这些,有了这些数据,在遇到数据收集瓶颈或者数据丢失的时候,通过分析监控数据来分析、解决问题。

source监控项:

指标项说明
OpenConnectionCount目前与客户端或sink保持连接的总数量
AppendBatchAcceptedCount成功提交到channel的批次的总数量
AppendBatchReceivedCount接收到事件批次的总数量
AppendAcceptedCount逐条录入的次数
AppendReceivedCount每批只有一个事件的事件总数量
EventAcceptedCount成功写出到channel的事件总数量
EventReceivedCount目前为止source已经接收到的事件总数量
StartTimesource启动时的毫秒值时间
StopTimesource停止时的毫秒值时间,为0表示一直在运行

channel监控项:

指标项说明
EventPutAttemptCountSource尝试写入Channe的事件总次数
EventPutSuccessCount成功写入channel且提交的事件总次数
EventTakeAttemptCountsink尝试从channel拉取事件的总次数
EventTakeSuccessCountsink成功从channel读取事件的总数量
ChannelSize目前channel中事件的总数量
ChannelCapacitychannel的容量
ChannelFillPercentagechannel已填入的百分比
StartTimechannel启动时的毫秒值时间
StopTimechannel停止时的毫秒值时间,为0表示一直在运行

sink监控项:

指标项说明
ConnectionCreatedCount创建的连接数量
ConnectionClosedCount关闭的连接数量
ConnectionFailedCount由于错误关闭的连接数量
BatchEmptyCount批量处理event的个数为0的数量-表示source写入数据的速度比sink处理数据的速度慢
BatchUnderflowCount批量处理event的个数小于批处理大小的数量
BatchCompleteCount批量处理event的个数等于批处理大小的数量
EventDrainAttemptCountsink尝试写出到存储的事件总数量
EventDrainSuccessCountsink成功写出到存储的事件总数量
StartTimechannel启动时的毫秒值时间
StopTimechannel停止时的毫秒值时间,为0表示一直在运行
  • JMX Reporting
    Java 管理扩展(Java Management Extension,JMX)是从jdk1.4开始的,但从1.5时才加到jdk里面,并把API放到java.lang.management包里面。MX监控可以通过在flume-env.sh脚本中修改JAVA_OPTS环境变量中的JMX参数来开启
    如果一个 Java 对象可以由一个遵循 JMX 规范的管理器应用管理,那么这个Java 对象就可以称为一个可由 JMX 管理的资源。
  • JSON Reporting
    Flume也支持以JSON格式报告运行指标。为了对外提供这些报告数据,Flume会在某个端口(可自定义)上运行一个web服务来提供这些数据。
    1636015716(1).png
  • Ganglia Reporting
    Ganglia是UC Berkeley发起的一个开源集群监视可视化工具,设计用于测量数以千计的节点。Ganglia的核心包含gmond、gmetad以及一个Web前端。主要是用来监控系统性能,如:cpu 、mem、硬盘利用率, I/O负载、网络流量情况等,通过曲线很容易见到每个节点的工作状态,对合理调整、分配系统资源,提高系统整体性能起到重要作用。
    1636016017(1).png
  • Custom Reporting
    可以通过编写自己的执行报告服务向其他系统报告运行指标。 报告类必须实现org.apache.flume.instrumentation.MonitorService 接口。

5.4 Flume的架构设计考虑

1636264230(1).png
flume日志采集常用架构分为三层:Agent层,Collector层和Store层。其中Agent层每个机器部署一个进程,负责对单机的日志收集工作;Collector层部署在中心服务器上,负责接收Agent层发送的日志,并且将日志根据路由规则写到相应的Store层中;Store层负责提供永久或者临时的日志存储服务,或者将日志流导向其它服务器。

5.4.1 可用性(availablity)

对日志收集系统来说,可用性(availablity)指固定周期内系统无故障运行总时间。

  • Agent死掉

    Agent死掉分为两种情况:机器死机或者Agent进程死掉。

    对于机器死机的情况来说,由于产生日志的进程也同样会死掉,所以不会再产生新的日志,不存在不提供服务的情况。

    对于Agent进程死掉的情况来说,确实会降低系统的可用性。对此,我们有下面三种方式来提高系统的可用性。首先,所有的Agent在supervise的方式下启动,如果进程死掉会被系统立即重启,以提供服务。其次,对所有的Agent进行存活监控,发现Agent死掉立即报警。最后,对于非常重要的日志,建议应用直接将日志写磁盘,Agent使用taildir的方式获得最新的日志。

  • Collector死掉
    由于中心服务器提供的是对等的且无差别的服务,且Agent访问Collector做了LoadBalance和重试机制。所以当某个Collector无法提供服务时,Agent的重试策略会将数据发送到其它可用的Collector上面。所以整个服务不受影响。

5.4.2 可扩展性(scalability)

对日志收集系统来说,可扩展性(scalability)是指系统能够线性扩展。当日志量增大时,系统能够以简单的增加机器来达到线性扩容的目的。

对于基于Flume的日志收集系统来说,需要在设计的每一层,都可以做到线性扩展地提供服务。下面将对每一层的可扩展性做相应的说明。

  • Agent层
    对于Agent这一层来说,每个机器部署一个Agent,可以水平扩展,不受限制。一个方面,Agent收集日志的能力受限于机器的性能,正常情况下一个Agent可以为单机提供足够服务。另一方面,如果机器比较多,可能受限于后端Collector提供的服务,但Agent到Collector是有Load Balance机制,使得Collector可以线性扩展提高能力。
  • Collector层
    对于Collector这一层,Agent到Collector是有Load Balance机制,并且Collector提供无差别服务,所以可以线性扩展。其性能主要受限于Store层提供的能力。
  • Store层
    对于Store这一层来说,Hdfs和Kafka都是分布式系统,可以做到线性扩展。Bypass属于临时的应用,只对应于某一类日志,性能不是瓶颈。

5.4.3 权限控制

关于权限控制,可以使用过滤器进行过滤,丢弃不符合规则的event。

如果权限控制放在Agent端,优势是可以较好地控制垃圾数据在系统中流转。但劣势是配置修改麻烦,每增加一个日志就需要重启或者重载Agent的配置。

如果权限控制放在Collector端,优势是方便进行配置的修改和加载。劣势是部分没有注册的数据可能在Agent/Collector之间传输。

考虑到Agent/Collector之间的日志传输并非系统瓶颈,且目前日志收集属内部系统,安全问题属于次要问题,所以选择采用Collector端控制。

5.4.4 Flume参数调优

调优其实也是针对组件的优化,那首先我们需要先定位到具体是哪个组件有延迟问题,然后有针对性的优化,一般是根据官网提供的参数来不断调节平衡优化。如果调节参数没有太大作用的话,这时候就要考虑策略优化,即看是否需要重新调整采集流程,或者对硬件进行升级等等操作。

channel容量设置

这是团队部署Flume最常见的问题之一。Flume Channel的容量在生产数据的应用程序和存储系统之间提供了一个缓冲区。当下游不可用时收集的数据将全部存储在channel之中,所以我们要设计一个容量大小,保证在下游修复好之前的这一段时间产生的数据能够全部承载。

首先我们要确定故障最大时间MaxTSR(单位为秒)、每秒产生的event数PMax
那么这个时期产生的事件总数可以表示为:TMax = PMax*MaxTSR

这个时候还要考虑flume启动时配置的内存大小够不够,flume启动时可以配置-Xmx参数为JVM内存大小,如果channel使用memory的话需要计算启动时内存大小。

BatchSize设置
  1. Sink的 Batch Size 应该等于输入流 Batch Size 的和。举个例子,如果你有一个 flume agent, 上游的10个Flume agent通过avro以100的batch Size 向这个agent发送事件,那么这个agent的batch Size 就应该设置为10X100 = 1000
  2. 如果你发现你需要将Batch Size设置的非常大,比如比10000还大,那么需要考虑多设置几个channel来提高并发性(每一个channel都有一个自己的线程)。实践中, 使用一个batch Size 为20000的HDFS Sink的性能要比 使用 两个batch Size 为10000的channel 性能要差
  3. 在性能可接受的情况下,尽量选择较小的Batch Size

6. 实践选择

6.1 Agent-collector-store架构

Memory Channel 有很大的丢数据风险,而且容量一般,File Channel 虽然能缓存更多的消息,但如果缓存下来的消息还没写入 Sink,此时 Agent 出现故障则 File Channel 中的消息一样不能被继续使用,直到该 Agent 恢复。而 Kafka Channel 容量大,容错能力强。但是使用kafka作为channel不能灵活的将数据发送到想要的topic中去,最多只能发送到某个topic下的不同分区。而使用kafka Sink可以根据Event Header中的属性选择指定的kafka Topic。
这里我选择使用File Channel保证数据不丢失,使用kafka Sink将不同系统的数据写入不同的kafka Topic中方便后续使用。

  • 业务代码中实时发送日志

1636509968(1).png

  • 业务服务器安装flume读取本地日志文件,发送日志

1636510004(1).png

flume Collector配置文件:

# Name the components on this agent
a1.sources = r1
a1.sinks = k1
a1.channels = c1

# Describe/configure the source 
a1.sources.r1.type = avro
a1.sources.r1.channels = c1
a1.sources.r1.bind = node4
a1.sources.r1.port = 4444

a1.channels.c1.type = file

a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
a1.sinks.k1.kafka.bootstrap.servers = node1:9092,node2:9092,node3:9092
a1.sinks.k1.channel = c1

本地读取文件发送flume Collector的配置文件:

# Name the components on this agent
a1.sources = r1
a1.sinks = k1 k2
a1.channels = c1

# Describe/configure the source 
a1.sources.r1.type = TAILDIR
a1.sources.r1.positionFile = ./taildir_position.json
a1.sources.r1.filegroups = f1 f2
a1.sources.r1.filegroups.f1 = /root/tmp1.txt
a1.sources.r1.filegroups.f2 = /root/tmp2.txt
a1.sources.r1.headers.f1.topic = testflume
a1.sources.r1.headers.f2.topic = testflume2
a1.sources.r1.batchSize = 1
a1.sources.r1.channels = c1

a1.channels.c1.type = file

a1.sinks.k1.type = avro
a1.sinks.k1.hostname = node4
a1.sinks.k1.port = 4444
a1.sinks.k1.channel = c1

a1.sinks.k2.type = avro
a1.sinks.k2.hostname = node5
a1.sinks.k2.port = 4444
a1.sinks.k2.channel = c1

a1.sinkgroups = g1
a1.sinkgroups.g1.sinks = k1 k2
a1.sinkgroups.g1.processor.type = load_balance
a1.sinkgroups.g1.processor.selector = random

使用taildir采集数据的时候可以加上一些header属性,方便后面使用,比如加上topic属性,后面可以根据这个存放在不同的kafka的主题中。

6.2 Logback配置flume

业务系统中向flume发送日志通常要结合日志框架(Log4j、Logback等)一起使用,SpringBoot天生自带logback,我们这里只需要引入一个logback-flume依赖即可。

<dependency>
  <groupId>com.teambytes.logback</groupId>
  <artifactId>logback-flume-appender_2.10</artifactId>
  <version>0.0.9</version>
</dependency>

然后在logback的配置文件中进行相关配置:
添加一个appender,配置向flume发送消息的相关信息,配置多个flumeAgent时默认会使用负载均衡的方式进行发送

<appender name="flumeTest" class="com.teambytes.logback.flume.FlumeLogstashV1Appender">
    <flumeAgents>
        192.168.197.130:4444,192.168.197.131:4444
    </flumeAgents>
    <flumeProperties>
        connect-timeout=4000;
        request-timeout=8000
    </flumeProperties>
    <batchSize>100</batchSize>
    <reportingWindow>1000</reportingWindow>
    <additionalAvroHeaders>
        myHeader = myValue
    </additionalAvroHeaders>
    <application>wk's Application</application>
    <layout class="ch.qos.logback.classic.PatternLayout">
        <pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - \(%file:%line\) - %message%n%ex</pattern>
    </layout>
</appender>

然后启用这个appender即可

<logger name="com" level="info">
    <appender-ref ref="flumeTest"/>
    <appender-ref ref="fileAppender"/>
</logger>

6.3 nginx转发agent消息

可以在Agent层和collector之间加一个nginx转发层,这样可以更加灵活的控制collector层的变化,从而避免修改Agent层的配置文件。因为nginx -s reload不会强制结束正在工作的连接,而是等所有连接都结束才会重启。
1636963589(1).png

  • Agent-nginx
    taildir-file-nginxavro.conf

    # Name the components on this agent
    a1.sources = r1
    a1.sinks = k1 k2
    a1.channels = c1
    
    # Describe/configure the source 
    a1.sources.r1.type = TAILDIR
    a1.sources.r1.positionFile = ./taildir_position.json
    a1.sources.r1.filegroups = f1
    a1.sources.r1.filegroups.f1 = /root/tmp1.txt
    a1.sources.r1.headers.f1.topic = testflume1
    a1.sources.r1.batchSize = 1
    a1.sources.r1.channels = c1
    
    a1.channels.c1.type = file
    
    a1.sinks.k1.type = avro
    a1.sinks.k1.hostname = node7
    a1.sinks.k1.port = 4444
    a1.sinks.k1.channel = c1
    
    a1.sinks.k2.type = avro
    a1.sinks.k2.hostname = node8
    a1.sinks.k2.port = 4444
    a1.sinks.k2.channel = c1
    
    a1.sinkgroups = g1
    a1.sinkgroups.g1.sinks = k1 k2
    a1.sinkgroups.g1.processor.type = load_balance
    a1.sinkgroups.g1.processor.selector = random
  • Nginx-collector
    nginx从1.9.0开始,新增加了一个stream模块,用来实现四层协议的转发、代理或者负载均衡等。这个模块在默认情况下不是构建的,应该使用--with-stream配置参数来启用它。可以使用这个模块来实现avro消息的转发。
    nginx.conf

    stream {
       upstream avro_server {
         server node4:4444;
         server node5:4444;
       }
    
       server {
        listen 4444;
        proxy_pass avro_server;
       }
    }
  • collector-store
    avro-file-kafka.conf不变

6.4 REST接口生成Agent层安装包

  • 根据用户配置,动态生成Flume配置文件;
  • 编写Shell脚本用以检查环境、检查配置、启动Flume;
  • 编写使用说明ReadME.md给用户;
  • 将配置文件、Shell脚本、使用说明一起放入Flume压缩包,并导出;
    demo地址:https://gitee.com/wkartist/fl...

参考:
https://flume.liyifeng.org
https://mp.weixin.qq.com/s/E6...
https://mp.weixin.qq.com/s/_F...


wkArtist
17 声望5 粉丝