Hadoop提供了一个中央化的存储系统,有利于进行集中式的数据分析与数据共享。Hadoop对存储格式没有要求,包括用户访问日志、产品信息和网页数据等。但是,数据分散在各个离散的设备上或保存在传统的存储设备和系统中,我们需要将数据存入Hadoop。常见数据来源包括网页信息、用户操作日志等非结构化数据和传统关系型数据库(像MySQL、Oracled等)中的结构化数据,例如商品信息、用户信息等。如何将分散的、多样化的数据源中的数据收集整理后可靠的迁移到Hadoop系统中,并且要支持数据源的动态扩展,通过并行处理提高性能是需要解决的问题。
Flume是由Cloudera公司开源的分布式、可靠、高可用的海量日志数据采集系统,数据源和数据存储系统支持定制和扩展。Flume属于一种中间件,屏蔽了数据源和数据存储系统的异构性。Flume有如下特点:
Flume分Flume OG和Flume NG两个版本,Flume NG架构有所简化,代码更精简。
Flume可以将数据从任一地方搬运到任一其他地方,不一定是HDFS。Agent是Flume中类似数据搬运工的组件,从上面的架构中看,四个Client(机器或是应用程序)中的数据写入Agent,如果前一层Agent数量太多,就把Agent汇总到后一层Agent再传递,Agent数量太多会对HDFS的冲击太大,而且会产生大量的小文件。
Agent的内部由Source(负责获取数据,是数据的源头)、Channel(类似缓冲区)和Sink(从Channel拿数据发给后端)组成。Agent是一个生产者和消费者模型,如果生产者和消费者数据量不对等,会产生数据堆积,所以引入缓冲区Channel。Source、Channel和Sink支持插拔,Flume提供了很多类型的Source、Channel和Sink,可以根据实际情况进行配置,整体开发量比较小。
a1.channels = ch-1
a1.sources = src-1
a1.sources.src-1.type = spooldir
a1.sources.src-1.channels = ch-1
a1.sources.src-1.spoolDir = /var/log/apache/flumeSpool
a1.sources.src-1.fileHeader = true
配置文件中配置Agent为a1。Channel和Source可以设置多个,所以Agent属性是channels和sources,Source要设置是对应哪个Channel。
Taildir Source
监听文件内容,一旦写入一行新数据,则读取。支持断点续读,定期将最新读取数据的偏移量写入json文件,根据文件修改时间决定读取优先级,最新的文件优先读取,读取完的文件不会做任何处理(比如删除,重命名等),目前仅支持文本文件。
代码示例
a1.sources = r1
a1.channels = c1
a1.sources.r1.type = TAILDIR
a1.sources.r1.channels = c1
a1.sources.r1.positionFile = /var/log/flume/taildir_position.json a1.sources.r1.filegroups = f1 f2
a1.sources.r1.filegroups.f1 = /var/log/test1/example.log a1.sources.r1.headers.f1.headerKey1 = value1
a1.sources.r1.filegroups.f2 = /var/log/test2/.*log.* a1.sources.r1.headers.f2.headerKey1 = value2 a1.sources.r1.headers.f2.headerKey2 = value2-2
a1.sources.r1.fileHeader = true
用filegroups参数可以支持多个目录
Exec
可执行任意Unix命令 ,无容错性。
a1.sources = r1
a1.channels = c1
a1.sources.r1.type = exec
a1.sources.r1.command = tail -F /var/log/secure
a1.sources.r1.channels = c1
Avro
Avro是一个RPC框架,由多个Client和一个Server组成,Client和Server的开发语言可以不一样。
启动一个avro server,监听端口号
代码示例
a1.sources = r1
a1.channels = c1
a1.sources.r1.type = avro
a1.sources.r1.channels = c1
a1.sources.r1.bind = 0.0.0.0
a1.sources.r1.port = 4141
2、Channel
Channel位于Source和Sink之间,用于缓存Event。当Sink成功将Event发送到下一跳的Channel或最终目的,Event从Channel移除。Sink发出去一条数据,就从Channel里删除一条数据,一个Channel可以和任何数量的Source和Sink工作,提供较弱的顺序保证,支持事务,具有并发控制,不会重复推送数据到一个Sink。
不同的Channel提供的持久化水平也是不一样的:
Memory Channel:
允许数据丢失,性能高,牺牲数据可靠性
代码示例
a1.channels = c1
a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
a1.channels.c1.transactionCapacity = 10000
a1.channels.c1.byteCapacityBufferPercentage = 20
a1.channels.c1.byteCapacity = 800000
容量是根据内存设置,不设置容量,当数据往Sink发送不及时,会撑爆内存。容量分条数(capacity)设置和容量(byte capacity)设置两部分,有一个先满足就不往里写了。
File Channel:
基于WAL(预写式日志Write-Ahead Logging)实现,数据不会丢失,牺牲性能,数据可靠性高
代码示例
a1.channels = c1
a1.channels.c1.type = file
a1.channels.c1.checkpointDir = /mnt/flume/checkpoint
a1.channels.c1.dataDirs = /mnt/flume/data
支付File的Channel支持断点续读,由checkpoint文件实现。
JDBC Channel:
基于嵌入Database实现,使用率不高
2、Sink
Sink负责将Event传输到下一跳或最终目的,成功完成后将Event从Channel移除。Sink必须作用于一个确切的channel。
Sink的类型:
1)存储Event到最终目的的终端Sink. 比如: HDFS, HBase
HDFS Sink
a1.channels = c1
a1.sinks = k1
a1.sinks.k1.type = hdfs
a1.sinks.k1.channel = c1
a1.sinks.k1.hdfs.path = /flume/events/%y-%m-%d/%H%M/%S
#会自动转换为当时的日期和时间,精确到秒
a1.sinks.k1.hdfs.filePrefix = events-
#设置使用什么作为文件前缀
a1.sinks.k1.hdfs.round = true #如果文件比较大,需要设置Round=true
a1.sinks.k1.hdfs.roundValue = 10
a1.sinks.k1.hdfs.roundUnit = minute
#10分钟写入一个文件,下一个10分钟写入另外一个文件
a1.sinks.k1.hdfs.codeC=gzip, bzip2, lzo, lzop, snappy
#codec是写入文件的压缩方式,使用哪个就选哪个,选一个就可以了,不要全选
a1.sinks.k1.hdfs.fileType=SequenceFile, DataStream,CompressedStream
#filetype是文件的存储格式,使用哪个就选哪个,选一个就可以了,不要全选
a1.sinks = k1
a1.sinks.k1.type = hbase
a1.sinks.k1.table = foo_table
a1.sinks.k1.columnFamily = bar_cf
a1.sinks.k1.serializer = org.apache.flume.sink.hbase.RegexHbaseEventSerializer a1.sinks.k1.channel = c1
Hive Sink
Hive 创建table weblogs
a1.channels = c1
a1.channels.c1.type = memory
a1.sinks = k1
a1.sinks.k1.type = hive
a1.sinks.k1.channel = c1
a1.sinks.k1.hive.metastore = thrift://127.0.0.1:9083 a1.sinks.k1.hive.database = logsdb
a1.sinks.k1.hive.table = weblogs
a1.sinks.k1.hive.partition = asia,%{country},%y-%m-%d-%H-%M a1.sinks.k1.useLocalTimeStamp = false
a1.sinks.k1.round = true
a1.sinks.k1.roundValue = 10
a1.sinks.k1.roundUnit = minute
a1.sinks.k1.serializer = DELIMITED
a1.sinks.k1.serializer.delimiter = "\t" a1.sinks.k1.serializer.serdeSeparator = '\t' a1.sinks.k1.serializer.fieldnames =id,,msg
2)自动消耗的Sink. 比如: Null Sink
3)用于Agent间通信的IPC Sink: Avro
实例1
场景:从四台机器的文件夹下取数据传输到HDFS的/data/logs目录下,Source使用pooldir,为了提高性能,使用Memory Channel,Sink使用HDFS Sink。
命名:
Agent: LogAgent
Source: mysource
Channel: mychannel
Sink: mysink
代码:
LogAgent.sources = mysource
LogAgent.channels = mychannel
LogAgent.sinks = mysink
LogAgent.sources.mysource.type = spooldir
LogAgent.sources.mysource.channels = mychannel LogAgent.sources.mysource.spoolDir =/tmp/logs
LogAgent.sinks.mysink.channel = mychannel
LogAgent.sinks.mysink.type = hdfs
LogAgent.sinks.mysink.hdfs.path = hdfs://master:8020/data/logs/%Y/%m/%d/ %H/
LogAgent.sinks.mysink.hdfs.batchSize = 1000
#设置一批写1000条数据
#文件滚动就是当源文件比较大的时候,如何生成新的文件,使得新的文件不要太大
#也不要太小,两种方式:1)按照大小;2)按照条数。
LogAgent.sinks.mysink.hdfs.rollSize = 0
#文件滚动大小,设置为0说明不按照文件大小滚动
LogAgent.sinks.mysink.hdfs.rollCount = 10000
#文件滚动的条数,这里设置按照一个文件超过10000行后生成另外一个新文件
LogAgent.sinks.mysink.hdfs.useLocalTimeStamp = true
LogAgent.channels.mychannel.type = memory LogAgent.channels.mychannel.capacity = 10000
进入Flume的安装文件夹运行:
1)调试模式:
bin/flume-ng agent -n LogAgent -c conf -f conf/ logagent.properties -Dflume.root.logger=DEBUG,console
2)运行模式:
bin/flume-ng agent -n LogAgent -c conf -f conf/ logagent.properties
3)后台模式:
bin/flume-ng agent -n LogAgent -c conf -f conf/ logagent.properties -Dflume.root.logger=DEBUG,console &
LogAgent是Agent的命名,-f标识是配置文件,conf/flume-conf.properties是配置文件。
a1.channels = c1
a1.sources = r1
a1.sinks = k1 k2
#配置两个SINK可以让下发速度更快一些
a1.sinkgroups = g1
a1.sinkgroups.g1.processor.type = LOAD_BALANCE
a1.sinkgroups.g1.processor.selector = ROUND_ROBIN
a1.sinkgroups.g1.processor.backoff = true
a1.channels.c1.type = FILE
a1.sources.r1.channels = c1
a1.sources.r1.type = AVRO
a1.sources.r1.bind = 0.0.0.0
a1.sources.r1.port = 41414
a1.sinks.k1.channel = c1
a1.sinks.k1.type = AVRO
a1.sinks.k1.hostname = a21.example.org
a1.sinks.k1.port = 41414
a1.sinks.k2.channel = c1
a1.sinks.k2.type = AVRO
a1.sinks.k2.hostname = a22.example.org
a1.sinks.k2.port = 41414
第二层(Tier 2)配置实例
a2.channels = c1
a2.sources = r1
a2.sinks = k1
a2.channels.c1.type = FILE
a2.sources.r1.channels = c1
a2.sources.r1.type = AVRO
a2.sources.r1.bind = 0.0.0.0
a2.sources.r1.port = 41414
a2.sinks.k1.channel = c1
a2.sinks.k1.type = HDFS
a2.sinks.k1.hdfs.path = hdfs://namenode.example.org
a2.sinks.k1.hdfs.fileType = DataStream
关系数据库与Hadoop之间传递数据使用Sqoop(SQL-to-Hadoop),它是连接传统关系型数据库和Hadoop 的桥梁,它可以把关系型数据库的数据导入到 Hadoop 系统 ( 如 HDFSHBase 和 Hive) 中,也可以把数据从 Hadoop 系统里抽取并导出到关系型数据库里。它使用批处理方式进行数据传输,并且利用MapReduce加快数据传输速度。它具有以下优势:
步骤1:Sqoop与数据库Server 通信,获取数据库表的元数据 信息;
步骤2:Sqoop启动一个Map- Only的MR作业,利用元数据信息并行将数据写入Hadoop。
sqoop import \
--connect jdbc:mysql://mysql.example.com/sqoop \
--username sqoop \
--password sqoop \
--table cities
–connnect: 指定JDBC URL
–username:mysql数据库的用户名
–password:mysql数据库的密码
–table:要读取的数据库表
执行后会在HDFS上创建一个和数据库表名一样的文件夹,拷贝到HDFS上的文件默认是文本文件,每列用逗号隔开。
例如:
bin/hadoop fs -cat cities/part-m-*
1,USA,Palo Alto
2,Czech Republic,Brno
3,USA,Sunnyvale
步骤1:Sqoop与数据库Server 通信,获取数据库表的元数据信息;
步骤2:并行导入数据:将Hadoop上文件划分成若干个split;每个split由一个Map Task进行数据导入。
sqoop export \
--connect jdbc:mysql://mysql.example.com/sqoop \
--username sqoop \
--password sqoop \
--table cities \
--export-dir cities
–connnect: 指定JDBC URL
–username:mysql数据库的用户名
–password:mysql数据库的密码
–table:要导入的数据库表
–export-dir:数据在HDFS上存放目录
sqoop import \
--connect jdbc:mysql://mysql.example.com/sqoop \
--username sqoop \
--password sqoop \
--table cities \
--hive-import
sqoop import \
--connect jdbc:mysql://mysql.example.com/sqoop \
--username sqoop \
--password sqoop \
--table cities \
--hbase-table cities \
--column-family world