数据采集工具 - Flume

一、Flume概述

        无论数据来自什么企业,或是多大量级,通过部署Flume,可以确保数据都安全、及时的送达大数据平台,我们可以集中精力在如何洞悉数据上。

1.1 Flume的定义

        由Cloudera公司开发的,是一个分布式、高可靠、高可用的海量日志采集、聚合、传输的系统。

        Flume支持在日志系统中定制各类数据发送方,用于采集数据。
        Flume提供对数据进行简单处理,并写到各种数据接收方的能力。
        简单的说,Flume就是实时采集日志的数据采集引擎
        

        特点:
        1.分布式:flume分布式集群部署,扩展性好。
        2.可靠性好:当节点出现故障时,日志能被传送到其它节点上而不会丢失。
        3.易用性:flume配置使用繁琐,对使用人员专业技术要求高。
        4.实时采集:flume采集流模式进行数据实时采集。

        适用场景:日志文件实时采集。

        其它数据采集还有:dataX、kettle、Logstash、Scribe、sqoop。
        dataX:阿里开源的软件,异构数据源离线同步工具。实现包括关系型数据库(MySql、Oracle等)、HDFS、Hive、Hbase、FTP等各种异构数据源之间稳定高效的数据同步功能。
                特点:
                易用性:没有界面,以执行脚本的方式运行,对使用人员的技术要去较高。
                性能:数据抽取性能高。
                部署:可独立部署。
                适用场景:在异构数据库/文件系统之间高速交换数据。
        kettle:开源的ETL工具。支持数据库、FTP、文件、rest接口、hdfs、hive等平台的数据行抽取、转换、传输等操作,java编写跨平台,C/S架构,不支持浏览器模式。
                特点:
                易用性:有可视化设计器进行可视化操作,使用简单。
                功能强大:不仅能进行数据传输,能同时进行数据清洗转换等操作。
                支持多种源:支持各种数据库、FTP、文件、rest接口、hdfs、Hive等源。
                部署方便:独立部署,不依赖第三方产品。
                适用场景:数据量及增量不大,业务规模变化较快,要求可视化操作,对技术人员技术门槛要求低。
        Logstash:应用程序日志、事件的传输、处理、管理和搜索的平台。可以用它来统一对应用程序日志进行收集管理,通过了web接口用于查询和统计。
        Scribe:是facebook开源的日志收集系统,它能够在各种日之源上收集日志,存储到一个中央存储系统(可以是NFS,分布式文件系统等)上,以便进行集中统计分析处理。
        sqoop:之后会讲到。

1.2 Flume体系结构

        

        Flume架构中的组件:
        Agent本质上是一个JVM进程,控制Event数据流从外部日志生产者那里传输到目的地(或下一个Agent)。一个完成的Agent包括三个组件Source、Channel、Sink,Source是指数据的来源和方式,Channel是一个数据的缓冲池,Sink定义了数据输出的方式和目的地。

        Source负责接受数据到Flume Agent的组件,Source组件可以处理各种类型、各种格式的日志数据,包括avro、exec、spooldir、netcat等。

        Channel是位于Source和Sink之间的缓冲区。Channel允许Source和Sink运行在不同的速率上。Channel是线程安全的,可以同时处理多个Source的写入操作和多个Sink的读取操作。常用的Channel包括:
                Memory Channel是内存中的队列。在允许数据丢失的情景下使用。如果不允许数据丢失,应避免使用Memory Channel,因为程序死亡、机器宕机或者重启都会导致数据丢失。
                File Channel将所有数据写到磁盘。因此在程序关闭、机器宕机的情况下不会丢失数据。

        Sink不断地轮回Channel中的事件且批量移除它们,并将这些事件批量写入到存储或者索引系统、或者被发送到另外一个Flume Agent。

        Sink是完全事务性的。从Channel批量删除数据之前,每个Sink用Channel启动一个事务。批量事件一旦成功写出到存储系统或下一个Flume Agent,Sink就利用Channel提交事务。事务一旦被提交,该Channel从自己的内部缓冲区删除事件。

        Sink组件包括hdfs、logger、avro、file、null、HBase、消息队列等。

        Event是Flume定义的一个数据流传输的最小单元。

1.3 Flume拓扑结构

        1. 串行模式:
                将多个Flume给顺序连起来,从最初的source开始到最终sink传输的目的存储系统。
                此模式不建议桥接过多flume数量,flume数量过多不仅会影响传输速率,而且一旦传输过程中某个节点flume宕机,会影响整个传输系统。
                

        2. 复制模式(单Source,多Channel、Sink模式)
                将事件流向一个或多个目的地。这种模式将数据源复制到多个Channel中,每个Channel都有相同的数据,Sink可以选择传送到不同到不同的目的地。
                

        3. 负载均衡模式(单Source、Channel,多Sink)
                将多个Sink逻辑上分到一个Sink组,Flume将数据发送到不同的Sink,主要解决负载均衡和故障转移问题。
        

         4. 聚合模式
                这种模式最常见,也非常实用,日常web应用通常分布在上百个服务器,大者甚至上百个、上万个服务器。产生的日志处理起来非常麻烦。用这种组合方式能很好解决这一问题,每台服务器部署一个flume采集日志,再传送到一个集中的收集日志的flume,再由此flume上传到hdfs、hbase、hive、消息队列中。
        

1.4 Flume内部原理

        总体数据流向:Source =》Channel =》Sink
        Channel:处理器、拦截器、选择器
        
        具体过程:
        1. Source接受事件,交给其Channel处理器处理事件。
        2. 处理器通过拦截器Interceptor,对事件进行一些处理,比如压缩解码、正则拦截、时间戳拦截、分类等。
        3. 经过拦截器处理过的事件再传给Channel选择器,将事件写入相应的Channel。
                Channel选择器有两种:
                        Replication Channel Selector(默认),会将source过来的event发往所有的Channel(比较常用的场景是,用多个Channel实现冗余副本,保证可用性。)
                        Multiplexing Channel Selector,根据配置分发event。此selector会根据event中某个header对应的value来讲event发往不同的Channel。
        4. 最后由Sink处理器处理各个Channel的事件。

二、安装部署

        Flume官网地址:http://flume.apache.org/
        文档查看地址:http://flume.apache.org/FlumeUserGuide.html
        下载地址:http://archive.apache.org/dist/flume/
        选择的版本 1.9.0

        安装步骤:
        1. 下载软件 apache-flume-1.9.0-bin.tar.gz,并上传到 linux123 上的/opt/lg/software 目录下。
        2. 解压 apache-flume-1.9.0-bin.tar.gz 到 /opt/lg/servers/ 目录下;并重命名为 flume-1.9.0。
        3. 在 /etc/profile 中增加环境变量,并执行 source /etc/profile,使修改生效
                export FLUME_HOME=/opt/lagou/servers/flume-1.9.0
                export PATH=$PATH:$FLUME_HOME/bin
        4. 将 $FLUME_HOME/conf 下的 flume-env.sh.template 改名为 flume-env.sh,并添加 JAVA_HOME的配置:
                cd $FLUME_HOME/conf
                mv flume-env.sh.template flume-env.sh
                vi flume-env.sh
                export JAVA_HOME=/opt/lagou/servers/jdk1.8.0_231

三、基础应用

        Flume支持的数据源种类有很多,可以来自directory、http、kafka等。
        Flume提供了Source组件用来采集数据源。

        常见的Source有:
        (1). avro source:监听avro端口来接受外部avro客户端的事件流。avro-source接受的是经过avro序列化后的数据,然后反序列化数据继续传输。利用avro可以实现多级流动、扇出流、扇入流等效果。接收通过flume提供的avro客户端发送的日 志信息。
                avro是Apache的一个数据序列化系统,由Hadoop的创始人 Doug Cutting开发,设计用于支持大批量数据交换的应用。它的主要特点有:
                        支持二进制序列化方式,可以便捷、快速的处理大量数据。
                        动态语言友好,avro提供的机制使动态语言可以方便的处理avro数据。
                
        (2). exec source:可以将命令的输出作为source。如 ping 192.168.234.123、tail -f hive.log。
        (3). netcat source:一个netcat source用来监听一个指定的端口,并接受监听到的数据。
        (4). spooling directory source:将指定文件加入到“自动搜集”目录中。flume会持续监听这个目录,把文件作为source来处理。注意:一旦文件被放入到目录后,便不能修改,如果修改,flume会报错。此外,也不能有重名文件。
        (5). Taildir Source(1.7):监控指定的多个文件,一旦文件内有新写入的数据,就会将其写入到指定的Sink内,本来源可靠性高,不会丢失数据。不会对跟踪的文件有任何处理,不会重命名也不会删除,不会做任何修改。目前不支持windows系统,不支持读取二进制文件,支持一行一行的读取文本文件。

        采集到的日志需要进行缓存,Flume提供了Channel组件来缓存数据。
        常见的Channel有:
        (1). memory Channel:缓存到内存中(最常用)。
        (2). file Channel:缓存到文件中。
        (3). JDBC Channel:通过jdbc缓存到关系型数据库中。
        (4). kafka Channel:缓存到kafka中。

        缓存的数据最终要进行保存,Flume提供了Sink组件来保存数据。
        常见的Sink有:
        (1). logger sink:将信息显示到标准输出上,主要用于测试。
        (2). avro sink:Flume events发送到sink,转换为Avro events,并发送到配置好的hostname/port。从配置好的channel按照配置好的批量大小批量获取events。
        (3). null sink:将接受到的events全部丢弃。
        (4). HDFS sink:将events写到HDFS。支持创建文件和序列文件,支持两种文件类型压缩。文件可以基于数据的经过时间、大小、事件的数量周期性的滚动。
        (5). Hive sink:该sink streams将包含分割文件或者JSON数据的events直接传送到hive表或分区中。使用hive事务写events。当一系列events提交到hive时,马上被hive查询到。
        (6). Hbase sink:保存到Hbase中。
        (7). kafka sink:保存到kafka中。

        日志采集就是根据业务需求,选择合适的Source、Channel、Sink,将其组合到一起。

3.1 入门案例

        中文flume帮助文档: https://flume.liyifeng.org/

        业务需求:监听本机8888端口,flume将监听的数据实时显示在控制台。

        需求分析:
                使用telnet工具可以向8888端口发送数据。
                监听端口数据,使用netcat source。
                channel选择memory。
                数据实时显示,选择logger sink。

        实现步骤:
                1. 安装 telnet 工具 yum install telnet
                2. 检查8888端口是否被占用,如果该端口被占用,可以选择其它端口完成任务
                        lsof -i:8888
                3. 创建Flume Agent配置文件。 flume-netcat-logger.conf         

# a1是agent的名称。source、channel、sink的名称分别为:r1 c1 k1
a1.sources = r1
a1.channels = c1
a1.sinks = k1
# source
a1.sources.r1.type = netcat
a1.sources.r1.bind = linux123
a1.sources.r1.port = 8888
# channel
a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
a1.channels.c1.transactionCapacity = 100
# sink
a1.sinks.k1.type = logger
# source、channel、sink之间的关系
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1

        Memory Channel是使用内存缓冲Event的Channel的实现。缓存速度比较快,容量会受到JVM内存大小的限制,可靠性不够高。适用于允许丢数据,但对性能要求较高的日志采集业务。 
        4. 启动Flume Agent
                $FLUME_HOME/bin/flume-ng --name a1 \
                --conf-file $FLUME_HOME/conf/flume-netcat-logger.conf \
                --Dflume.root.logger=INFO,console
                name:定义agent的名字,要与参数文件一致。
                conf-file:指定参数文件位置。
                -D表示flume运行时动态修改flume.root.logger参数指定的属性值,并将控制台日志打印级别设置为INFO级别。日志级别包括:log、info、warn、error。
        5. 使用telnet向本机的8888端口发送消息  telnet linux123 8888
        6. 在flume监听页面查看数据接受情况
                INFO sink.LoggerSink: Event: { headers:{} body: 68 65 6C 6C 6F
                20 77 6F 72 6C 64 0D       hello world. }
                INFO sink.LoggerSink: Event: { headers:{} body: 41 72 65 20 79
                6F 75 20 6F 6B 3F 0D       Are you ok?. }

3.2 监控日志文件信息到HDFS

        业务需求:监听本地日志文件,收集内容实时上传到HDFS。

        需求分析:
                使用tail -F命令即可找到本地日志文件产生的信息。
                source选用exec,监听一个指定的指令,获取指令的结果作为数据源。source组件从这个命令的结果中取数据,当agent挂掉重启后可能存在数据丢失。
                channel选用memory。
                sink选用HDFS。

                tail -f  等同于--follow=descriptor,根据文件描述符进行追踪,当文件改名或被删除,追
踪停止。
                tail -F  等同于--follow=name --retry,根据文件名进行追踪,并保持重试,即该文件被
删除或改名后,如果再次创建相同的文件名,会继续追踪。

        实现步骤:
                1. 准备环境:Flume要想将数据输出到HDFS,必须有Hadoop相关jar包。将
commons-configuration-1.6.jar hadoop-auth-2.9.2.jar hadoop-common-
2.9.2.jar hadoop-hdfs-2.9.2.jar commons-io-2.4.jar htrace-core4-4.1.0-
incubating.jar拷贝到 $FLUME_HOME/lib 文件夹下。
                # 在$HADOOP_HOME/share/hadoop/httpfs/tomcat/webapps/webhdfs/WEB-
  INF/lib 有这些文件
cd $HADOOP_HOME/share/hadoop/httpfs/tomcat/webapps/webhdfs/WEB-INF/lib
cp commons-configuration-1.6.jar $FLUME_HOME/lib/
cp hadoop-auth-2.9.2.jar $FLUME_HOME/lib/
cp hadoop-common-2.9.2.jar $FLUME_HOME/lib/
cp hadoop-hdfs-2.9.2.jar $FLUME_HOME/lib/
cp commons-io-2.4.jar $FLUME_HOME/lib/
cp htrace-core4-4.1.0-incubating.jar $FLUME_HOME/lib/
                2. 配置文件:flume-exec-hdfs.conf

# Name the components on this agent
a2.sources = r2
a2.sinks = k2
a2.channels = c2

# Describe/configure the source
a2.sources.r2.type = exec
a2.sources.r2.command = tail -F /tmp/root/hive.log

# Use a channel which buffers events in memory
a2.channels.c2.type = memory
a2.channels.c2.capacity = 10000
a2.channels.c2.transactionCapacity = 500

# Describe the sink
a2.sinks.k2.type = hdfs
a2.sinks.k2.hdfs.path = hdfs://linux121:8020/flume/%Y%m%d/%H%M
# 上传文件的前缀
a2.sinks.k2.hdfs.filePrefix = logs-
# 是否使用本地时间戳
a2.sinks.k2.hdfs.useLocalTimeStamp = true
# 积攒500个Event才flush到HDFS一次
a2.sinks.k2.hdfs.batchSize = 500
# 设置文件类型,支持压缩。DataStream没启用压缩
a2.sinks.k2.hdfs.fileType = DataStream
# 1分钟滚动一次
a2.sinks.k2.hdfs.rollInterval = 60
# 128M滚动一次
a2.sinks.k2.hdfs.rollSize = 134217700
# 文件的滚动与Event数量无关
a2.sinks.k2.hdfs.rollCount = 0
# 最小冗余数
a2.sinks.k2.hdfs.minBlockReplicas = 1

# Bind the source and sink to the channel
a2.sources.r2.channels = c2
a2.sinks.k2.channel = c2

        3. 启动Agent
                $FLUME_HOME/bin/flume-ng agent --name a2 \
                --conf-file $FLUME_HOME/conf/flume-exec-hdfs.conf \
                -Dflume.root.logger=INFO,console
        4. 启动hadoop和hive,操作hive产生日志
                start-dfs.sh
                start-yarn.sh
                # 在命令行多次执行
                hive -e "show databases"
        5. HDFS上查看文件

3.3 监控目录采集信息到HDFS

        业务需求:监控指定目录,收集信息实时上传到HDFS。

        需求分析:
                source选用spooldir。spooldir能够保证数据不丢失,且能够实现断点续传,但延迟性高,不能实时上传。
                channel选用memory。
                sink选用HDFS。

                spooldir监听一个指定的目录,即只要向指定目录添加新文件,source组件就可以获取到该信息,并解析改文件内容,写入到channel。sink处理完之后,该文件被标记已完成处理,文件名添加.completed后缀。虽然是自动监控整个目录,但只能监控文件,如果以追加的方式向已被处理的文件中添加内容,source并不能识别。
                需要注意的是:
                拷贝到spool目录下的文件不可以再打开编辑。
                无法监控子目录的文件夹变动。
                被监控文件夹500毫秒扫描一次文件变动。
                适合用于同步新文件,但不适合对实时追加日志的文件进行监听并同步。

        1. 创建文件 flume-spooldir-hdfs.conf

# Name the components on this agent
a3.sources = r3
a3.channels = c3
a3.sinks = k3

# Describe/configure the source
a3.sources.r3.type = spooldir
a3.sources.r3.spoolDir = /root/upload
a3.sources.r3.fileSuffix = .COMPLETED
a3.sources.r3.fileHeader = true
# 忽略以.tmp结尾的文件,不上传
a3.sources.r3.ignorePattern = ([^ ]*\.tmp)

# Use a channel which buffers events in memory
a3.channels.c3.type = memory
a3.channels.c3.capacity = 10000
a3.channels.c3.transactionCapacity = 500

# Describe the sink
a3.sinks.k3.type = hdfs
a3.sinks.k3.hdfs.path =
hdfs://linux121:8020/flume/upload/%Y%m%d/%H%M
# 上传文件的前缀
a3.sinks.k3.hdfs.filePrefix = upload-
# 是否使用本地时间戳
a3.sinks.k3.hdfs.useLocalTimeStamp = true
# 积攒500个Event,flush到HDFS一次
a3.sinks.k3.hdfs.batchSize = 500
# 设置文件类型
a3.sinks.k3.hdfs.fileType = DataStream
# 60秒滚动一次
a3.sinks.k3.hdfs.rollInterval = 60
# 128M滚动一次
a3.sinks.k3.hdfs.rollSize = 134217700
# 文件滚动与event数量无关
a3.sinks.k3.hdfs.rollCount = 0
# 最小冗余数
a3.sinks.k3.hdfs.minBlockReplicas = 1

# Bind the source and sink to the channel
a3.sources.r3.channels = c3
a3.sinks.k3.channel = c3

        2. 启动Agent
                $FLUME_HOME/bin/flume-ng agent --name a3 \
                --conf-file $FLUME_HOME/conf/flume-spooldir-hdfs.conf \
                -Dflume.root.logger=INFO,console

        3. 向upload文件夹中添加文件。

        4. 查看HDFS上的数据

        HDFS Sink
        一般使用HDFS Sink都会采用滚动生成文件的方式,滚动生成文件的策略有:
        基于时间:
                hdfs.rollInterval
                缺省值:30,单位秒
                0禁用
        基于文件大小:
                hdfs.rollSize
                缺省值:1024字节
                0禁用
        基于event数量:
                hdfs.rollCount
                10
                0禁用
        基于文件空闲时间:
                hdfs.idleTimeout
                缺省值:0。禁用
        基于HDFS文件副本数:
                hdfs.minBlockReplicas
                默认:与HDFS的副本数一致
                要将该参数设置为1;否则HFDS文件所在块的复制会引起文件滚动 

        其它重要配置:
        hdfs.useLocalTimeStamp
                使用本地时间,而不是event header的时间戳
                默认值:false
        hdfs.round
                时间戳是否四舍五入
                默认值false
                如果为true,会影响所有的时间,除了t%
        hdfs.roundValue
                四舍五入的最高倍数(单位配置在hdfs.roundUnit),但是要小于当前时间
                默认值:1
        hdfs.roundUnit
                可选值为:second、minute、hour
                默认值:second

        如果要避免HDFS Sink产生小文件,参考如下参数设置:

a1.sinks.k1.type=hdfs
a1.sinks.k1.hdfs.useLocalTimeStamp=true
a1.sinks.k1.hdfs.path=hdfs://linux121:9000/flume/events/%Y/%m/
%d/%H/%M
a1.sinks.k1.hdfs.minBlockReplicas=1
a1.sinks.k1.hdfs.rollInterval=3600
a1.sinks.k1.hdfs.rollSize=0
a1.sinks.k1.hdfs.rollCount=0
a1.sinks.k1.hdfs.idleTimeout=0

3.4 监控日志文件采集数据到HDFS、本地文件系统

        业务需求:监控日志文件,收集信息上传到HDFS 和 本地文件系统。

        需求分析:
                需要多个Agent级联实现。
                source选用taildir。
                channel选用memory。
                最终sink分别选用hdfs、file_roll。

                taildir Source是Flume1.7.0加入的新Source,相当于spooldir Source + exec Source。可以监控多个目录,并且使用正则表达式匹配该目录中的文件进行实时收集。实时监控一批文件,并记录每个文件最新消费位置,agent进程重启后不会有数据丢失的问题。

                目前不适用于windows系统;其不会对跟中的文件有任何处理,不会重命名也不会删除,不会做任何修改。不支持读取二进制文件,支持一行一行的读取文本文件。

         实现步骤:
        1. 创建第一个配置文件 flume-taildir-avro.conf 包括:
                1个 taildir source
                2个 memory channel
                2个 avro sink
        

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

# 将数据流复制给所有channel
a1.sources.r1.selector.type = replicating

# source
a1.sources.r1.type = taildir
# 记录每个文件最新消费位置
a1.sources.r1.positionFile = /root/flume/taildir_position.json
a1.sources.r1.filegroups = f1
# 备注:.*log 是正则表达式;这里写成 *.log 是错误的
a1.sources.r1.filegroups.f1 = /tmp/root/.*log

# sink
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = linux123
a1.sinks.k1.port = 9091
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = linux123
a1.sinks.k2.port = 9092

# channel
a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
a1.channels.c1.transactionCapacity = 500
a1.channels.c2.type = memory
a1.channels.c2.capacity = 10000
a1.channels.c2.transactionCapacity = 500

# Bind the source and sink to the channel
a1.sources.r1.channels = c1 c2
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c2

        2. 创建第二个配置文件 flume-avro-hdfs.conf 包括
                1个 avro source
                1个 memory channel
                1个 hdfs sink 

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

# Describe/configure the source
a2.sources.r1.type = avro
a2.sources.r1.bind = linux123
a2.sources.r1.port = 9091

# Describe the channel
a2.channels.c1.type = memory
a2.channels.c1.capacity = 10000
a2.channels.c1.transactionCapacity = 500

# Describe the sink
a2.sinks.k1.type = hdfs
a2.sinks.k1.hdfs.path = hdfs://linux121:8020/flume2/%Y%m%d/%H
# 上传文件的前缀
a2.sinks.k1.hdfs.filePrefix = flume2-
# 是否使用本地时间戳
a2.sinks.k1.hdfs.useLocalTimeStamp = true
# 500个Event才flush到HDFS一次
a2.sinks.k1.hdfs.batchSize = 500
# 设置文件类型,可支持压缩
a2.sinks.k1.hdfs.fileType = DataStream
# 60秒生成一个新的文件
a2.sinks.k1.hdfs.rollInterval = 60
a2.sinks.k1.hdfs.rollSize = 0
a2.sinks.k1.hdfs.rollCount = 0
a2.sinks.k1.hdfs.minBlockReplicas = 1

# Bind the source and sink to the channel
a2.sources.r1.channels = c1
a2.sinks.k1.channel = c1

        3. 创建第三个配置文件 flume-avro-file.conf 包括
                1个 avro source
                1个 memory channel
                1个 file_roll sink 

# Name the components on this agent
a3.sources = r1
a3.sinks = k1
a3.channels = c2

# Describe/configure the source
a3.sources.r1.type = avro
a3.sources.r1.bind = linux123
a3.sources.r1.port = 9092

# Describe the sink
a3.sinks.k1.type = file_roll
# 目录需要提前创建好
a3.sinks.k1.sink.directory = /root/flume/output

# Describe the channel
a3.channels.c2.type = memory
a3.channels.c2.capacity = 10000
a3.channels.c2.transactionCapacity = 500
# Bind the source and sink to the channel
a3.sources.r1.channels = c2
a3.sinks.k1.channel = c2

        4. 分别启动3个Agent
                $FLUME_HOME/bin/flume-ng agent --name a3 \
                --conf-file ~/conf/flume-avro-file.conf \
                -Dflume.root.logger=INFO,console &
                $FLUME_HOME/bin/flume-ng agent --name a2 \
                --conf-file ~/conf/flume-avro-hdfs.conf \
                -Dflume.root.logger=INFO,console &
                $FLUME_HOME/bin/flume-ng agent --name a1 \
                --conf-file ~/conf/flume-taildir-avro.conf \
                -Dflume.root.logger=INFO,console & 

        5. 执行hive命令产生日志
                hive -e "show databases"

        6. 分别检查HDFS文件、本地文件、以及消费位置文件

        3种监控日志文件Source的对比
        exec Source:适用于监控一个实时追加的文件,但不能保证数据不丢失;
        spooldir Source:能够保证数据不丢失,且能够实现断点续传,但延迟较高,不能实时监控;
        taildir Source:既能够实现断点续传,又可以保证数据不丢失,还能够进行实时监控。

四、高级特性

4.1 拦截器

        Flume支持在运行时对event进行修改或丢弃,通过拦截器来实现。
        Flume里面的拦截器是实现了org.apache.flume.interceptor.interceptor接口的类。
        拦截器可以根据配置修改 甚至 丢弃 event。
        Flume支持链式的拦截器执行方式,在配置文件里面配置多个拦截器就可以了。
        拦截器的顺序取决于它们的配置顺序,Event按照顺序经过每一个拦截器。

        1. 时间添加戳拦截器
        这个拦截器会向每个event的header中添加一个时间戳属性进去,key默认值是‘timestamp’(也可以通过下面表格中的header来定义),value就是当前的毫秒值(其实也就是System.currentTimeMills()得到的)。如果event已经存在同名的属性,可以选择是否保留原始的值。    

         测试:
                1.再次运行 入门案例 中的测试,观察 event header信息。

                可见header为空。
                2. 在入门案例的基础上,在配置文件中增加时间拦截器的配置。命名为timestamp.conf

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

# configure the source
a1.sources.r1.type = netcat
a1.sources.r1.bind = linux123
a1.sources.r1.port = 8888

# 这部分是新增 时间拦截器的 内容
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = timestamp
# 是否保留Event header中已经存在的同名时间戳,缺省值false
a1.sources.r1.interceptors.i1.preserveExisting= false
# 这部分是新增 时间拦截器的 内容

# Describe the sink
a1.sinks.k1.type = logger

# Use a channel which buffers events in memory
a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
a1.channels.c1.transactionCapacity = 500

# Bind the source and sink to the channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1

                 3. 启动Agent,启动 telnet 输入信息
                        $FLUME_HOME/bin/flume-ng agent --name a1 \
                        --conf-file ~/conf/timestamp.conf \
                        -Dflume.root.logger=INFO,console
                        telnet linux3 8888
                        # 输入 hello world

                可以看见 event header 中出现了时间戳信息。

        2 Host添加拦截器
        这个拦截器会把当前Agent的hostname或ip地址写入到Event的header中,key默认是“host”(也可以通过配置自定义key),value可以选择使用hostname或ip地址。
        

         测试:
        1. 在时间拦截器案例的基础上,在配置文件中增加主机名拦截器的配置。命名为hostname.conf   

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

# configure the source
a1.sources.r1.type = netcat
a1.sources.r1.bind = linux123
a1.sources.r1.port = 8888

# 这部分是新增 时间拦截器 的内容
a1.sources.r1.interceptors = i1 i2
a1.sources.r1.interceptors.i1.type = timestamp
a1.sources.r1.interceptors.i1.preserveExisting= false
# 这部分是新增 时间拦截器 的内容

# 这部分是新增 主机名拦截器 的内容
a1.sources.r1.interceptors.i2.type = host
# 如果header中已经存在同名的属性是否保留
a1.sources.r1.interceptors.i2.preserveExisting= false
# true:使用IP地址;false:使用hostname
a1.sources.r1.interceptors.i2.useIP = false
# 这部分是新增 主机名拦截器 的内容

# Describe the sink
a1.sinks.k1.type = logger

# Use a channel which buffers events in memory
a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
a1.channels.c1.transactionCapacity = 500

# Bind the source and sink to the channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1

        2. 启动Agent,启动 telnet 输入信息
                $FLUME_HOME/bin/flume-ng agent --name a1 \
                --conf-file $FLUME_HOME/conf/hostname.conf \
                -Dflume.root.logger=INFO,console
                telnet linux123 8888
                # 输入 hello world 

        可以看见event headers 中 增加了主机名信息、时间戳信息。

        3. 正则表达式过滤拦截器
        这个拦截器会把Event body当做字符串来处理,并用配置的正则表达式来匹配。可以配置指定被匹配到的Event丢弃还是没被匹配到的Event丢弃。

4.2 选择器

        Source可以向多个Channel同时写数据,所以也就产生了以何种方式向多个channel写的问题。
        replication(复制,缺省)。数据完整的发送到每一个channel;
        multiplexing(多路复用)。通过配置来按照一定的规则进行分发;

        1. 复制选择器:(默认的选择器)
        

         a1.sources = r1
        a1.channels = c1 c2 c3
        a1.sources.r1.selector.type = replicating
        a1.sources.r1.channels = c1 c2 c3
        a1.sources.r1.selector.optional = c3

        上面这个例子中,c3配置成了可选的。向c3发送数据如果失败了会被忽略。c1和c2
没有配置成可选的,向c1和c2写数据失败会导致事务失败回滚。

        2. 多路复用选择器
        

a1.sources = r1
a1.channels = c1 c2 c3 c4
a1.sources.r1.selector.type = multiplexing
a1.sources.r1.selector.header = state    #以每个Event的
header中的state这个属性的值作为选择channel的依据
a1.sources.r1.selector.mapping.CZ = c1    #如果state=CZ,则选
择c1这个channel
a1.sources.r1.selector.mapping.US = c2 c3  #如果state=US,则选
择c2 和 c3 这两个channel
a1.sources.r1.selector.default = c4     #默认使用c4这个
channel

           3. 自定义选择器
        自定义选择器就是开发一个org.apache.flume.ChannelSelector接口的实现类。实现类和依赖的jar包在启动的时候都必须放入flume的classpath下。
        a1.sources = r1
        a1.channels = c1
        a1.sources.r1.selector.type =
        org.liyifeng.flume.channel.MyChannelSelector      

4.3 Sink组逻辑处理器

        可以把多个Sink分成一个组,Sink组逻辑处理器可以对这同一个组里的几个Sink进行负载均衡 或者 其中一个Sink发生故障后将输出Event的任务转移到其它的Sink上。

        N个Sink将Event输出到N个目的地,通过Sink组逻辑处理器可以把这N个Sink配置成负载均衡或者故障转移的工作方式。
        负载均衡:将channel里面的event,按照配置的负载均衡(比如轮询)分别发送到sink各自对应的目的地。
        故障转移:这N个Sink同一时间只有一个在工作,其余的作为备用,工作的Sink挂掉之后,备用的Sink顶上。
        

        默认:
        默认的逻辑处理器就是只有一个Sink的情况下,这种情况就没有必要配置Sink组了。前面的例子都是 source - channel - sink这种一对一,单个sink的。

        故障转移:
        故障转移组逻辑处理器维护了一个发送Event失败的sink的列表,保证有一个Sink是可以用来发送Event的。
        工作原理是将故障Sink降级到一个池中,在池中为它们分配冷却期(超时时间),在重试之前随顺序故障而增加。Sink成功发送事件后,它将恢复到实时池。Sink具有与之相关的优先级,数值越大,优先级越高。如果在发送Event的时候Sink发生故障,会继续尝试下一个具有最高优先级的Sink。例如,在优先级为80的sink之前激活优先级为100的sink。如果未指定优先级,则根据配置中的顺序来选取。
        要使用故障转移选择器,不仅要设置sink组的选择器为failover,还有为每一个sink
设置一个唯一的优先级数值。 可以使用 maxpenalty 属性设置故障转移时间的上限(毫秒)。
        

a1.sinkgroups = g1
a1.sinkgroups.g1.sinks = k1 k2
a1.sinkgroups.g1.processor.type = failover
a1.sinkgroups.g1.processor.priority.k1 = 5
a1.sinkgroups.g1.processor.priority.k2 = 10
a1.sinkgroups.g1.processor.maxpenalty = 10000

         负载均衡:
        负载均衡Sink 选择器提供了在多个sink上进行负载均衡流量的功能。 它维护一个活
动sink列表的索引来实现负载的分配。 支持轮询( round_robin )【默认值】和随机( random )两种选择机制分配负载。
        工作时,此选择器使用其配置的选择机制选择下一个sink并调用它。 如果所选sink无法正常工作,则处理器通过其配置的选择机制选择下一个可用sink。 此实现不会将失败的Sink列入黑名单,而是继续乐观地尝试每个可用的Sink。
        如果所有sink调用都失败了,选择器会将故障抛给sink的运行器。
        如果 backoff 设置为true则启用了退避机制,失败的sink会被放入黑名单,达到一定的超时时间后会自动从黑名单移除。 如从黑名单出来后sink仍然失败,则再次进入黑名单而且超时时间会翻倍,以避免在无响应的sink上浪费过长时间。 如果没有启用退避机制,在禁用此功能的情况下,发生sink传输失败后,会将本次负载传给下一个sink继续尝试,因此这种情况下是不均衡的。
        

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

4.4 事务机制与可靠性

        一提到事务首先想到的就是关系型数据库中的事务,事务一个典型的特征就是将一批操作组做成原子性的,要么都成功,要么都失败。

        在Flume中一共有两个事务:
                Put事务:在Source到Channel之间。
                Get事务:在Channel到Sink之间。

        从Source到Channel过程中,数据在Flume中会被封装成Event对象,把一批Event放到事务中,把这个事务也就是这批Event一次性放到Channel中。同理,Get事务中,也是把这一批Event组成的事务统一拿出来到Sink放到HDFS上。

        Flume中的Put事务:
        1. 事务开始的时候会调用一个doPut方法,doPut方法将一批数据放在putList中;
                putList在向Channel发送数据前会检查Channel的容量是否能放得下,如果放不下一个都不放,只能doRollBack;        
                数据批的大小取决于配置参数 batch size的值;        
                putList的大小取决于配置Channel的参数transaction capacity的大小(Channel的另一个参数capacity指的是Channel的容量);
        2. 数据顺利放到putList之后,接下来可以调用doCommit方法,把putList中所有event放到Channel中,成功放完之后就清空putList;

        在doCommit提交之后,事务在向Channel存放数据的过程中,事务容易出问题。如Sink取数据慢,而Source放数据快,容易造成Channel中数据的积压,如果putList中的数据放不进去,会如何?
        答:此时会调用doRollback方法,doRollback方法会进行两项操作:将putList清空;抛出ChannelException异常。Source会捕捉到soRollback抛出的异常,然后Source就会将刚才的一批数据重新采集,然后重新开始一个新的事务,这就是事务的回滚。

        Flume中的Take事务:
        Take事务同样也有takeList,HDFS sink配置有一个batch size,这个参数决定Sink从Channel中取数据时一次取多少,所以该batch size得小于taskList的大小,而takeList的大小取决于transaction capacity的大小。
        

         1. doTake方法会将channel中的event剪切到takeList中。如果后面接的是HDFS Sink的话,在把Channel中的event剪切到takeList中的同时也往写入HDFS的IO缓冲流中放一份event(数据写入HDFS是先写入IO缓冲流然后flush到HDFS);
        2. 当takeList中存放了batch size 数量的event之后,就会调用doCommit方法,doCommit方法会做两个操作:
                针对HDFS Sink,手动调用IO流的flush方法,将IO流缓冲区的数据写入到HDFS磁盘中。
                清空takeList中的数据。

        flush到HDFS的时候组容易出问题。flush到HDFS的时候,可能由于网络原因超时导致数据传输失败,这个时候调用doRollback方法来进行回滚,回滚的时候由于takeList 中还有备份数据,所以将takeList中的数据原封不动地还给channel,这时候就完成了事务的回滚。
        但是,如果flush到HDFS的时候,数据flush了一半之后出问题了,这意味着已经有一半的数据已经发送到HDFS上面了,现在出了问题,同样需要调用doRollback方法来进行回滚,回滚并没有“一半”之说,它只会把整个takeList中的数据返回给channel,然后继续进行数据的读写。这样开启下一个事务的时候容易造成数据重复的问题。
        Flume在数据进行采集传输的时候,有可能会造成数据的重复,但不会丢失数据。

        Flume在数据传输的过程中是否可靠,还需要考虑具体使用Source、Channel、Sink的类型。
        分析Source
                exec Source ,后面接  tail -f ,这个数据也是有可能丢的。
                TailDir Source ,这个是不会丢数据的,它可以保证数据不丢失。
        分析Sink:
                Hdfs Sink,数据有可能重复,但是不会丢失。
        分析Channel:
                理论上说:要想数据不丢失的话,还是要用 File channel;memory channel 在 Flume 挂掉的时候是有可能造成数据的丢失的。
        如果使用 TailDir source 和 HDFS sink,所以数据会重复但是不会丢失。

4.5 高可用案例

        案例:实现Agent的故障转移
        

        1. 在linux121、linux122上部署Flume、修改环境变量
                # 在liunx123上执行
                /opt/lg/servers
                scp -r flume-1.9.0/ linux121:$PWD
                scp -r flume-1.9.0/ linux122:$PWD
                cd /etc
                scp profile linux121:$PWD
                scp profile linux122:$PWD
                # 在linux121、linux122上分别执行
                source /etc/profile

        2. conf文件
                linux123:flume-taildir-avro.conf

# agent name
a1.sources = r1
a1.channels = c1
a1.sinks = k1 k2

# source
a1.sources.r1.type = TAILDIR
a1.sources.r1.positionFile =/root/flume_log/taildir_position.json
a1.sources.r1.filegroups = f1
a1.sources.r1.filegroups.f1 = /tmp/root/.*log
a1.sources.r1.fileHeader = true

# interceptor
a1.sources.r1.interceptors = i1 i2
a1.sources.r1.interceptors.i1.type = static
a1.sources.r1.interceptors.i1.key = Type
a1.sources.r1.interceptors.i1.value = LOGIN
# 在event header添加了时间戳
a1.sources.r1.interceptors.i2.type = timestamp

# channel
a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
a1.channels.c1.transactionCapacity = 500

# sink group
a1.sinkgroups = g1
a1.sinkgroups.g1.sinks = k1 k2

# set sink1
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = linux121
a1.sinks.k1.port = 9999

# set sink2
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = linux122
a1.sinks.k2.port = 9999

# set failover
a1.sinkgroups.g1.processor.type = failover
a1.sinkgroups.g1.processor.priority.k1 = 100
a1.sinkgroups.g1.processor.priority.k2 = 60
a1.sinkgroups.g1.processor.maxpenalty = 10000

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

                linux122:flume-avro-hdfs.conf   

# set Agent name
a2.sources = r1
a2.channels = c1
a2.sinks = k1

# Source
a2.sources.r1.type = avro
a2.sources.r1.bind = linux121
a2.sources.r1.port = 9999

# interceptor
a2.sources.r1.interceptors = i1
a2.sources.r1.interceptors.i1.type = static
a2.sources.r1.interceptors.i1.key = Collector
a2.sources.r1.interceptors.i1.value = linux121

# set channel
a2.channels.c1.type = memory
a2.channels.c1.capacity = 10000
a2.channels.c1.transactionCapacity = 500

# HDFS Sink
a2.sinks.k1.type=hdfs
a2.sinks.k1.hdfs.path=hdfs://linux121:8020/flume/failover/
a2.sinks.k1.hdfs.fileType=DataStream
a2.sinks.k1.hdfs.writeFormat=TEXT
a2.sinks.k1.hdfs.rollInterval=60
a2.sinks.k1.hdfs.filePrefix=%Y-%m-%d
a2.sinks.k1.hdfs.minBlockReplicas=1
a2.sinks.k1.hdfs.rollSize=0
a2.sinks.k1.hdfs.rollCount=0
a2.sinks.k1.hdfs.idleTimeout=0

a2.sources.r1.channels = c1
a2.sinks.k1.channel=c1

         linux121:flume-avro-hdfs.conf

# set Agent name
a3.sources = r1
a3.channels = c1
a3.sinks = k1

# Source
a3.sources.r1.type = avro
a3.sources.r1.bind = linux122
a3.sources.r1.port = 9999

# interceptor
a3.sources.r1.interceptors = i1
a3.sources.r1.interceptors.i1.type = static
a3.sources.r1.interceptors.i1.key = Collector
a3.sources.r1.interceptors.i1.value = linux122

# set channel
a3.channels.c1.type = memory
a3.channels.c1.capacity = 10000
a3.channels.c1.transactionCapacity = 500

# HDFS Sink
a3.sinks.k1.type=hdfs
a3.sinks.k1.hdfs.path=hdfs://linux121:8020/flume/failover/
a3.sinks.k1.hdfs.fileType=DataStream
a3.sinks.k1.hdfs.writeFormat=TEXT
a3.sinks.k1.hdfs.rollInterval=60
a3.sinks.k1.hdfs.filePrefix=%Y-%m-%d
a3.sinks.k1.hdfs.minBlockReplicas=1
a3.sinks.k1.hdfs.rollSize=0
a3.sinks.k1.hdfs.rollCount=0
a3.sinks.k1.hdfs.idleTimeout=0

a3.sources.r1.channels = c1
a3.sinks.k1.channel=c1

        3. 分别在linux121、linux122、linux123上启动对应服务(先启动下游的agent)
                # linux121
                flume-ng agent --name a2 --conf-file ~/conf/flume-avro-hdfs.conf
                # linux122
                flume-ng agent --name a3 --conf-file ~/conf/flume-avro-hdfs.conf
                # linux123
                flume-ng agent --name a1 --conf-file ~/conf/flume-taildir-avro2.conf

        4. 先hive.log中写入数据,检查HDFS目录

        5. 杀掉一个Agent,看看另外Agent是否能启动

  • 0
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

小羊and阿童木

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值