第一章 数据仓库概念
第二章 项目需求及架构设计
项目需求
项目框架
第三章 数据生成模块
目标数据
数据埋点
服务器和jdk准备
编写集群分发脚本xsync
模拟数据
集群日志生成脚本
第四章 数据采集模块
集群所有进程查看脚本
Hadoop安装
LZO压缩配置
LZO创建索引
Hadoop参数调优
Zookeeper安装
ZK集群启停脚本
Kafka安装
Kafka集群安装
安装部署
Kafka集群启动停止脚本
Kafka常用命令
Kafka机器数量计算
Kafka分区数计算
采集日志Flume
日志采集Flume安装
Flume组件选型
日志采集Flume配置
Flume拦截器
测试Flume-Kafka通道
日志采集Flume启动停止脚本
消费Kafka数据Flume
Flume组件选型
消费者Flume配置
Flume时间拦截器
消费者Flume启动停止脚本
Flume内存优化
采集通道启动/停止脚本
常见问题及解决方案
前置知识:linux基本用法并对hadoop生态的基础架构有一定了解
数据仓库( Data Warehouse ),是为企业制定决策,提供数据支持的。可以帮助企业,改进业务流程、提高产品质量等。
数据仓库的输入数据通常包括:业务数据、用户行为数据和爬虫数据等
业务数据:就是各行业在处理事务过程中产生的数据。比如用户在电商网站中登录、下单、支付等过程中,需要和网站后台数据库进行增删改查交互,产生的数据就是业务数据。业务数据通常存储在MySQL、Oracle等数据库中。
用户行为数据:用户在使用产品过程中,通过埋点收集与客户端产品交互过程中产生的数据,并发往日志服务器进行保存。比如页面浏览、点击、停留、评论、点赞、收藏等。用户行为数据通常存储在日志文件中。
爬虫数据:通常事通过技术手段获取其他公司网站的数据。不建议使用
用户行为数据采集平台搭建
业务数据采集频台搭建
数据仓库维度建模
分析,设备、会员、商品、地区、活动等电商核心主题,统计的报表指标近100个
采用即席查询工具,随时进行指标分析
对集群性能进行监控,发生异常需要报警
元数据管理
质量监控
权限管理
技术选型
主要考虑因素:数据量大小、业务需求、行业内经验、技术成熟度、开发维护成本、总成本预算
数据采集传输:Flume,Kafka,Sqoop,Logstash,DataX
数据存储:Mysql,HDFS,HBase,Redis,MongoDB
数据计算:Hive,Tez,Spark,Flink,Storm
数据查询:Presto,Kylin,Impala,Druid,ClickHouse,Doris
数据可视化:Echarts,Superset,QuickBI,Doris
任务调度:Azkaban,Oozie,DolphinScheduler,Airflow
集群监控:Zabbix,Prometheus
元数据管理:Atlas
权限管理:Ranger,Sentry
系统数据流程设计
框架版本选型
1)如何选择Apache/CDH/HDP版本
Apache:运维麻烦,组件兼容性需要自己调研(一般大厂使用,技术实例雄厚,有专业的运维人员)
CDH:国内使用最多的版本,但CM不开源
HDP:开源,可以进行二次开发,但没有CDH稳定,国内使用较少
要收集和分析的数据主要包括页面数据、时间数据、曝光数据、启动数据、错误数据
页面
页面数据主要记录一个页面的用户访问情况,包括访问时间、停留时间、页面路径等信息
事件
事件数据主要记录应用内一个具体操作行为,包括操作类型、操作对象、操作对象描述等信息
曝光
曝光数据主要记录页面所曝光的内容,包括曝光对象,曝光类型等信息
启动
错误
错误数据记录应用使用
过程中的的错误信息,包括错误编码及错误信息
主流埋点方式(了解)
目前主流的埋点方式,有代码埋点(前端/后端)、可视化埋点、全埋点三种。
代码埋点是通过调用埋点SDK函数,在需要埋点的业务逻辑功能位置调用接口,上报埋点数据。例如,我们对页面中的某个按钮埋点后,当这个按钮被点击时,可以在这个按钮对应的 OnClick 函数里面调用SDK提供的数据发送接口,来发送数据。
可视化埋点只需要研发人员集成采集 SDK,不需要写埋点代码,业务人员就可以通过访问分析平台的“圈选”功能,来“圈”出需要对用户行为进行捕捉的控件,并对该事件进行命名。圈选完毕后,这些配置会同步到各个用户的终端上,由采集 SDK 按照圈选的配置自动进行用户行为数据的采集和发送。
全埋点是通过在产品中嵌入SDK,前端自动采集页面上的全部用户行为事件,上报埋点数据,相当于做了一个统一的埋点。然后再通过界面配置哪些数据需要在系统里面进行分析。
埋点数据上报时机
买点数据上报时机包括两种方式
方式一,在离开该页面时上传在这个页面产生的所有数据(页面、事件、曝光、错误等)。优点:批处理,减少了服务器接收数据压力。缺点:不是特别及时
方式二:每个事件、动作、错误等,产生后立即发生。优点:响应及时。缺点:对服务器接收数据压力比较大
本次项目采用方式一埋点
埋点数据日志结构
我们的日志结构大致可分为两类,一是普通页面埋点日志,二是启动日志。
普通页面日志结构如下,每条日志包含了,当前页面的页面信息,所有事件(动作)、所有曝光信息以及错误信息。除此之外,还包含了一系列公共信息,包括设备信息,地理位置,应用信息等,即下边的common字段。
(1)普通页面埋点日志格式
{
"common": { -- 公共信息
"ar": "230000", -- 地区编码
"ba": "iPhone", -- 手机品牌
"ch": "Appstore", -- 渠道
"is_new": "1",--是否首日使用,首次使用的当日,该字段值为1,过了24:00,该字段置为0。
"md": "iPhone 8", -- 手机型号
"mid": "YXfhjAYH6As2z9Iq", -- 设备id
"os": "iOS 13.2.9", -- 操作系统
"uid": "485", -- 会员id
"vc": "v2.1.134" -- app版本号
},
"actions": [ --动作(事件)
{
"action_id": "favor_add", --动作id
"item": "3", --目标id
"item_type": "sku_id", --目标类型
"ts": 1585744376605 --动作时间戳
}
],
"displays": [
{
"displayType": "query", -- 曝光类型
"item": "3", -- 曝光对象id
"item_type": "sku_id", -- 曝光对象类型
"order": 1, --出现顺序
"pos_id": 2 --曝光位置
},
{
"displayType": "promotion",
"item": "6",
"item_type": "sku_id",
"order": 2,
"pos_id": 1
},
{
"displayType": "promotion",
"item": "9",
"item_type": "sku_id",
"order": 3,
"pos_id": 3
},
{
"displayType": "recommend",
"item": "6",
"item_type": "sku_id",
"order": 4,
"pos_id": 2
},
{
"displayType": "query ",
"item": "6",
"item_type": "sku_id",
"order": 5,
"pos_id": 1
}
],
"page": { --页面信息
"during_time": 7648, -- 持续时间毫秒
"item": "3", -- 目标id
"item_type": "sku_id", -- 目标类型
"last_page_id": "login", -- 上页类型
"page_id": "good_detail", -- 页面ID
"sourceType": "promotion" -- 来源类型
},
"err":{ --错误
"error_code": "1234", --错误码
"msg": "***********" --错误信息
},
"ts": 1585744374423 --跳入时间戳
}
(2)启动日志格式
启动日志结构相对简单,主要包含公共信息,启动信息和错误信息
{
"common": {
"ar": "370000",
"ba": "Honor",
"ch": "wandoujia",
"is_new": "1",
"md": "Honor 20s",
"mid": "eQF5boERMJFOujcp",
"os": "Android 11.0",
"uid": "76",
"vc": "v2.1.134"
},
"start": {
"entry": "icon", --icon手机图标 notice 通知 install 安装后启动
"loading_time": 18803, --启动加载时间
"open_ad_id": 7, --广告页ID
"open_ad_ms": 3449, -- 广告总共播放时间
"open_ad_skip_ms": 1989 -- 用户跳过广告时点
},
"err":{ --错误
"error_code": "1234", --错误码
"msg": "***********" --错误信息
},
"ts": 1585744304000
}
需求:循环赋值文件到所有节点的相同目录下
需求分析:
rsync命令原始拷贝
rsync -av /opt/module root@Hadoop103:/opt/
期望脚本
xsync 要同步的文件名
说明:在/home/admin/bin 这个目录下存放的脚本,admin用户可以在系统任何地方直接执行
脚本实现
在/home/admin/bin目录下创建xsync文件
cd /home/admin/bin
vim xsync
脚本中编写如下脚本
#!/bin/bash
#1. 判断参数个数
if [ $# -lt 1 ]
then
echo Not Enough Arguement!
exit;
fi
#2. 遍历集群所有机器
for host in hadoop102 hadoop103 hadoop104
do
echo ==================== $host ====================
#3. 遍历所有目录,挨个发送
for file in $@
do
#4. 判断文件是否存在
if [ -e $file ]
then
#5. 获取父目录
pdir=$(cd -P $(dirname $file); pwd)
#6. 获取当前文件的名称
fname=$(basename $file)
ssh $host "mkdir -p $pdir"
rsync -av $pdir/$fname $host:$pdir
else
echo $file does not exists!
fi
done
done
修改脚本xsync具有执行权限
chmod +x xsync
测试脚本
xsync /home/admin/bin
SSH无密登录配置
说明:这里面只配置了hadoop102、hadoop103到其他主机的无密登录;因为hadoop102未外配置的是NameNode,hadoop103配置的是ResourceManager,都要求对其他节点无密访问。
(1)hadoop102上生成公钥和私钥
在.ssh目录下
ssh-keygen -t rsa
然后敲三下回车,会生成两个文件私钥和公钥
如果没有.ssh,可以先ssh hadoop103 登录后exit退出
(2)将hadoop193公钥拷贝到要免密登录的目标主机上
ssh-copy-id hadoop102
ssh-copy-id hadoop103
ssh-copy-id hadoop104
(3)hadoop103上生成公钥和密钥
ssh-keygen -t rsa
(4)将hadoop103公钥拷贝到要免密登录的目标机器上
ssh-copy-id hadoop102
ssh-copy-id hadoop103
ssh-copy-id hadoop104
卸载现有jdk
安装jdk
配置环境变量
使用xsync脚本分发jdk
分发环境变量配置文件
在hadoop103和hadoop104上执行source
环境变量配置说明
linux的环境变量可在多个文件中配置,如/etc/prifile,/etc/profile.d/*.sh,~/.bashrc,~/.bash_profile等,下面说明上述几个文件之间的关系和区别。
bash的运行模式可以分为login shell 和non-login shell
例如,通过终端,输入用户名、密码,登录系统后,得到的就是一个login shell
执行 ssh hadoop103 command,在hadoop执行command的就是一个non-login shell
登录shell和分登录shell区别
这两种shell主要区别在于,它们启动时会加载不同的配置文件,login shell启动时会加载/etc/profile,~/.bash_profile,~/.bashrc。non-login shell启动时会加载~/.bashrc。
而在加载~/.bashrc(实际是~/.bashrc中加载的/etc/bashrc)或/etc/profile时,都会执行如下代码片段
因此不管是login shell还是non-login shell,启动时都会加载/etc/profile.d/*.sh中的环境变量
使用说明
1)将application.yml gmall2020-mock-log-2021-01-22.jar path.json logback.xml上传到hadoop102的/opt/module/applog目录下
配置文件
(1)application.yml文件
可以根据需求生成对应日期的用户行为日志
内容如下
# 外部配置打开
# 外部配置打开
logging.config: "./logback.xml"
#业务日期 注意:并不是Linux系统生成日志的日期,而是生成数据中的时间
mock.date: "2020-06-14"
#模拟数据发送模式
#mock.type: "http"
#mock.type: "kafka"
mock.type: "log"
#http模式下,发送的地址
mock.url: "http://hdp1/applog"
#kafka模式下,发送的地址
mock:
kafka-server: "hdp1:9092,hdp2:9092,hdp3:9092"
kafka-topic: "ODS_BASE_LOG"
#启动次数
mock.startup.count: 200
#设备最大值
mock.max.mid: 500000
#会员最大值
mock.max.uid: 100
#商品最大值
mock.max.sku-id: 35
#页面平均访问时间
mock.page.during-time-ms: 20000
#错误概率 百分比
mock.error.rate: 3
#每条日志发送延迟 ms
mock.log.sleep: 10
#商品详情来源 用户查询,商品推广,智能推荐, 促销活动
mock.detail.source-type-rate: "40:25:15:20"
#领取购物券概率
mock.if_get_coupon_rate: 75
#购物券最大id
mock.max.coupon-id: 3
#搜索关键词
mock.search.keyword: "图书,小米,iphone11,电视,口红,ps5,苹果手机,小米盒子"
(2)path.json 该文件用来配置访问路径
根据需求,可以灵活配置用户点击路径
[
{"path":["home","good_list","good_detail","cart","trade","payment"],"rate":20 },
{"path":["home","search","good_list","good_detail","login","good_detail","cart","trade","payment"],"rate":40 },
{"path":["home","mine","orders_unpaid","trade","payment"],"rate":10 },
{"path":["home","mine","orders_unpaid","good_detail","good_spec","comment","trade","payment"],"rate":5 },
{"path":["home","mine","orders_unpaid","good_detail","good_spec","comment","home"],"rate":5 },
{"path":["home","good_detail"],"rate":10 },
{"path":["home" ],"rate":10 }
]
(3)logback配置文件
可配置日志生成路径,修改内容如下
%msg%n
${LOG_HOME}/app.%d{yyyy-MM-dd}.log
%msg%n
3)生成日志
进入到/opt/module/applog执行以下命令
java -jar gmall2020-mock-log-2021-01-22.jar
在hadoop的/home/admin目录下创建bin目录,这样脚本可以在服务器的任何目录执行
(1)在/home/admin/bin目录下创建脚本 lg.sh
vim lg.sh
(2)脚本内容如下
#!/bin/bash
for i in hadoop102 hadoop103; do
echo "========== $i =========="
ssh $i "cd /opt/module/applog/; java -jar gmall2020-mock-log-2021-01-22.jar >/dev/null 2>&1 &"
done
注:
/opt/module/applog/为jar包及配置文件所在路径
/dev/null代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失
标准输入0:从键盘获取输入/proc/self/fd/0
标准输出1:输出到屏幕(即控制台)/proc/self/fd/1
错误输出2:输出到屏幕(即控制台)/proc/self/fd/2
(3)修改脚本执行权限
chmod u+x lg.sh
(4)将jar包及配置文件上传至hadoop103的/opt/module/applog/路径
(5)启动脚本
lg.sh
(6)分别在hadoop102、hadoop103的/opt/module/applog/log目录上查看生成的数据
ls app.2020-06-14.log
(1)在/home/admin/bin 目录下创建xcall.sh
脚本内容
#! /bin/bash
for i in hadoop102 hadoop103 hadoop104
do
echo --------- $i ----------
ssh $i "$*"
done
(2)修改脚本执行权限
chmod 777 xcall.sh
(3)启动脚本
xcall.sh jps
(1)集群规划
1)hadoop-lzo编译
hadoop本身并不支持lzo压缩,故需要使用twitter提供的hadoop-lzo开源组件。hadoop-lzo需依赖hadoop和lzo进行编译
2)将编译好后的hadoop-lzo-0.4.20.jar放入hadoop-3.1.3/share/hadoop/common/
cd /opt/module/hadoop-3.1.3/share/hadoop/common
3)同步hadoop-lzo-0.4.20.jar 到hadoop103、hadoop104
xsync hadoop-lzo-0.4.20.jar
4)core-site.xml增加配置支持LZO压缩
io.compression.codecs
org.apache.hadoop.io.compress.GzipCodec,
org.apache.hadoop.io.compress.DefaultCodec,
org.apache.hadoop.io.compress.BZip2Codec,
org.apache.hadoop.io.compress.SnappyCodec,
com.hadoop.compression.lzo.LzoCodec,
com.hadoop.compression.lzo.LzopCodec
io.compression.codec.lzo.class
com.hadoop.compression.lzo.LzoCodec
5)同步core-site.xml到hadoop103、hadoop104
xsync core-site.xml
6)启动及查看集群
[hadoop102] sbin/start-dfs.sh
[hadoop103] sbin/start-yarn.sh
7)测试-数据准备
hadoop fs -mkdir /input
hadoop fs -put README.txt /input
8)测试-压缩
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount -Dmapreduce.output.fileoutputformat.compress=true -Dmapreduce.output.fileoutputformat.compress.codec=com.hadoop.compression.lzo.LzopCodec /input /output
1)创建LZO文件的索引
LZO压缩文件的可切片特性依赖于其索引,故我们需要手动为LZO压缩文件创建索引。若无索引,则LZO文件的切片只有一个。
hadoop jar /path/to/your/hadoop-lzo.jar com.hadoop.compression.lzo.DistributedLzoIndexer big_file.lzo
2)测试
(1)将bigtable.lzo(200M)上传到集群的根目录
hadoop fs -mkdir /input
hadoop fs -put bigtable.lzo /input
(2)执行wordcount程序
hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount -Dmapreduce.job.inputformat.class=com.hadoop.mapreduce.LzoTextInputFormat /input /output1
(3)对上传的LZO文件建索引
hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/common/hadoop-lzo-0.4.20.jar com.hadoop.compression.lzo.DistributedLzoIndexer /input/bigtable.lzo
(4)再次执行WordCount程序
hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount -Dmapreduce.job.inputformat.class=com.hadoop.mapreduce.LzoTextInputFormat /input /output2
注:如果遇到如下错误
Container [pid=8468,containerID=container_1594198338753_0001_01_000002] is running 318740992B beyond the 'VIRTUAL' memory limit. Current usage: 111.5 MB of 1 GB physical memory used; 2.4 GB of 2.1 GB virtual memory used. Killing container.
Dump of the process-tree for container_1594198338753_0001_01_000002 :
解决方法:在hadoop102的/opt/module/hadoop-3.1.3/etc/hadoop/yarn-site.xml文件中增加如下配置,然后分发到hadoop103、hadoop104服务器上,并重新启动集群
yarn.nodemanager.vmem-check-enabled
false
1)HDFS参数调优hdfs-site.xml
The number of Namenode RPC server threads that listen to requests from clients. If dfs.namenode.servicerpc-address is not configured then Namenode RPC server threads listen to requests from all nodes.
NameNode有一个工作线程池,用来处理不同DataNode的并发心跳以及客户端并发的元数据操作。
对于大集群或者有大量客户端的集群来说,通常需要增大参数dfs.namenode.handler.count的默认值10。
dfs.namenode.handler.count
10
2)YARN参数调优yarn-site.yml
1)解压安装
解压安装包到/opt/module/目录下
[software] tar -zxvf zookeeper-3.5.7.tar.gz -C /opt/module/
修改名字
[module] mv apache-zookeeper-3.5.7-bin/ zookeeper-3.5.7
同步到hadoop103、hadoop104
[module] xsynczookeeper-3.5.7/
2)配置服务器编号
(1)在/opt/module/zookeeper-3.5.7/目录下创建zkData
mkdir zkData
(2)在zkData目录下创建一个myid的文件
vi myid
在文件中添加server对应的编号:2
(3)拷贝配置好的zookeeper到其他机器上
xsync myid
将内容分别修改为3、4
4)配置zoo.cfg文件
(1)重命名/opt/module/zookeeper-3.5.7/conf这个目录下的zoo_sample.cfg为zoo.cfg
mv zoo_sample.cfg zoo.cfg
(2)打开zoo.cfg文件,修改数据存储路径
vim zoo.cfg
dataDir=/opt/module/zookeeper-3.5.7/zkData
增加如下配置
#######################cluster##########################
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:3888
server.4=hadoop104:2888:3888
(3)同步zoo.cfg配置文件
xsync zoo.cfg
(4)配置参数解读
server.A=B:C:D。
5)集群操作
hadoop102,103,104分别启动Zookeeper
[zookeeper-3.5.7] bin/zkServer.sh start
集群规划
(1)在hadoop102的/home/admin/bin目录下创建脚本
vim zk.sh
脚本内容
#!/bin/bash
case $1 in
"start"){
for i in hadoop102 hadoop103 hadoop104
do
echo ---------- zookeeper $i 启动 ------------
ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh start"
done
};;
"stop"){
for i in hadoop102 hadoop103 hadoop104
do
echo ---------- zookeeper $i 停止 ------------
ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh stop"
done
};;
"status"){
for i in hadoop102 hadoop103 hadoop104
do
echo ---------- zookeeper $i 状态 ------------
ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh status"
done
};;
esac
(2)
增加脚本权限
chmod u+x zk.sh
Zookeeper集群启动脚本
zk.sh start
Zookeeper集群停止脚本
zk.sh stop
1)解压 修改文件名 创建logs文件夹
[software] tar -zxvf kafka_2.11-2.4.1.tgz -C /opt/module/
[module] mv kafka_2.11-2.4.1/ kafka
/opt/module/kafka 目录下创建logs文件夹
mkdir logs
2)修改配置文件
cd config/
vi server.properties
修改或添加以下内容
#broker的全局唯一编号,不能重复
broker.id=0
#删除topic功能使能
delete.topic.enable=true
#kafka运行日志存放的路径
log.dirs=/opt/module/kafka/data
#配置连接Zookeeper集群地址
zookeeper.connect=hadoop102:2181,hadoop103:2181,hadoop104:2181/kafka
3)配置环境变量
sudo vim /etc/profile.d/my_env.sh
#KAFKA_HOME
export KAFKA_HOME=/opt/module/kafka
export PATH=$PATH:$KAFKA_HOME/bin
source /etc/profile.d/my_env.sh
4)分发安装包(分发完记得配置其他机器的环境变量)
xsync kafka/
5)分别在hadoop103和hadoop104上修改配置文件/opt/module/kafka/config/server.properties中的broker.id=1、broker.id=2
6)启动集群
bin/kafka-server-start.sh -daemon /opt/module/kafka/config/server.properties
7)关闭集群
bin/kafka-server-stop.sh
集群规划
(1)在/home/admin/bin目录下创建脚本kf.sh
vim kf.sh
脚本内容:
#! /bin/bash
case $1 in
"start"){
for i in hadoop102 hadoop103 hadoop104
do
echo " --------启动 $i Kafka-------"
ssh $i "/opt/module/kafka/bin/kafka-server-start.sh -daemon /opt/module/kafka/config/server.properties"
done
};;
"stop"){
for i in hadoop102 hadoop103 hadoop104
do
echo " --------停止 $i Kafka-------"
ssh $i "/opt/module/kafka/bin/kafka-server-stop.sh stop"
done
};;
esac
(2)增加脚本执行权限
chmod u+x kf.sh
(3)kf集群启动和停止
kf.sh start
kf.sh stop
1)查看Kafka Topic列表
bin/kafka-topics.sh --zookeeper hadoop102:2181/kafka --list
2)创建Kafka Topic
进入到/opt/module/kafka/目录下创建日志主题
bin/kafka-topics.sh --zookeeper hadoop102:2181,hadoop103:2181,hadoop104:2181/kafka --create --replication-factor 1 --partitions 1 --topic topic_log
3)删除Kafka Topic
bin/kafka-topics.sh --delete --zookeeper hadoop102:2181,hadoop103:2181,hadoop104:2181/kafka --topic topic_log
4)Kafka生产消息
bin/kafka-console-producer.sh \
--broker-list hadoop102:9092 --topic topic_log
5)Kafka消费消息
bin/kafka-console-consumer.sh \
--bootstrap-server hadoop102:9092 --from-beginning --topic topic_log
6)查看Kafka Topic详情
bin/kafka-topics.sh --zookeeper hadoop102:2181/kafka \
--describe --topic topic_log
Kafka机器数量(经验公式)= 2 *(峰值生产速度 * 副本数 / 100)+ 1
先拿到峰值生产速度,再根据设定的副本数,就能预估出需要部署Kafka的数量
解压到/opt/module 名字改为flume
tar -zxvf apache-flume-1.9.0-bin.tar.gz -C /opt/module
cd /opt/module
mv apache-flume-1.9.0-bin.tar.gz flume
删除guava-11.0.2.jar以兼容hadoop 3.1.3
rm /opt/module/flume/lib/guava-11.0.2.jar
将flume/conf下的flume-env.sh.template文件修改为flume-env.sh 并配置flume-env.sh文件
mv flume-env.sh.template flume-env.sh
vi flume-env.sh
插入以下内容
exprot JAVA_HOME=/opt/module/jdk1.8.0_212
集群规划
1)Source
(1)Taildir Source相比Exec Source、Spooling Directory Source的优势
(2)batchSize大小如何设置
Event 1K左右时,500-1000合适(默认100)
2)Channel
1)Flume配置分析
Flume直接读log日志的数据,log日志的格式是app.yyyy-mm-dd.log
2)Flume具体配置如下:
在/opt/module/flume/conf目录下创建file-flume-kafka.conf文件
vim file-flume-kafka.conf
配置内容:
#为各组件命名
a1.sources = r1
a1.channels = c1
#描述source
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups = f1
a1.sources.r1.filegroups.f1 = /opt/module/applog/log/app.*
a1.sources.r1.positionFile = /opt/module/flume/taildir_position.json
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = com.admin.flume.interceptor.ETLInterceptor$Builder
#描述channel
a1.channels.c1.type = org.apache.flume.channel.kafka.KafkaChannel
a1.channels.c1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092
a1.channels.c1.kafka.topic = topic_log
a1.channels.c1.parseAsFlumeEvent = false
#绑定source和channel以及sink和channel的关系
a1.sources.r1.channels = c1
注:com.admin.flume.interceptor.ETLInterceptor$Builder是自定义的拦截器的全类名。需要根据用户自定义的拦截器做相应修改
1)创建Maven工程flume-interceptor
2)创建包名:com.admin.flume.interceptor
是自定义的拦截器的全类名。需要根据用户自定义的拦截器做相应修改
3)在pom.xml文件中添加如下配置
org.apache.flume
flume-ng-core
1.9.0
provided
com.alibaba
fastjson
1.2.62
maven-compiler-plugin
2.3.2
1.8
1.8
maven-assembly-plugin
jar-with-dependencies
make-assembly
package
single
注:scope中provided表示编译时使用该jar包,打包时不用。因为集群上已经有flume的jar包,只是在编译时用一下
4)在com.admin.flume.interceptor包下创建JSONUtils类
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
public class JSONUtils {
public static boolean isJSONValidate(String log){
try {
JSON.parse(log);
return true;
}catch (JSONException e){
return false;
}
}
}
5)在com.admin.flume.interceptor包下创建LogInterceptor类
import com.alibaba.fastjson.JSON;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
public class ETLInterceptor implements Interceptor {
@Override
public void initialize() {
}
@Override
public Event intercept(Event event) {
byte[] body = event.getBody();
String log = new String(body, StandardCharsets.UTF_8);
if (JSONUtils.isJSONValidate(log)) {
return event;
} else {
return null;
}
}
@Override
public List intercept(List list) {
Iterator iterator = list.iterator();
while (iterator.hasNext()){
Event next = iterator.next();
if(intercept(next)==null){
iterator.remove();
}
}
return list;
}
public static class Builder implements Interceptor.Builder{
@Override
public Interceptor build() {
return new ETLInterceptor();
}
@Override
public void configure(Context context) {
}
}
@Override
public void close() {
}
}
6)打包
7)先将打好的包放到hadoop102的/opt/module/flume/lib文件夹下
8)分发Flume到hadoop103、hadoop104
xsync flume/
9)分别在hadoop102、hadoop103上启动Flume
bin/flume-ng agent --name a1 --conf-file conf/file-flume-kafka.conf &
(1)生成日志
lg.sh
(2)消费Kafka数据,观察控制台是否有数据获取到
[/opt/module/kafka] bin/kafka-console-consumer.sh \
--bootstrap-server hadoop102:9092 --from-beginning --topic topic_log
(1)在/home/admin/bin目录下创建脚本f1.sh
vim f1.sh
脚本内容
#! /bin/bash
case $1 in
"start"){
for i in hadoop102 hadoop103
do
echo " --------启动 $i 采集flume-------"
ssh $i "nohup /opt/module/flume/bin/flume-ng agent --conf-file /opt/module/flume/conf/file-flume-kafka.conf --name a1 -Dflume.root.logger=INFO,LOGFILE >/opt/module/flume/log1.txt 2>&1 &"
done
};;
"stop"){
for i in hadoop102 hadoop103
do
echo " --------停止 $i 采集flume-------"
ssh $i "ps -ef | grep file-flume-kafka | grep -v grep |awk '{print \$2}' | xargs -n1 kill -9 "
done
};;
esac
说明:
nohup,该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。nohup就是不挂起的意思,不挂断地运行命令
awk 默认分隔符为空格
$2是在“”双引号内部会被解析为脚本的第二个参数,但是这里面想表达的含义是awk的第二个值,所以需要将他转义,用$2表示
xargs 表示取出前面命令运行的结果,作为后面命令的输入参数
(2)增加脚本权限 测试启动停止
chmod u+x f1.sh
f1.sh start
f1.sh stop
1)FileChannel和MemoryChannel区别
2)FileChannel优化
通过配置dataDirs指向多个路径,每个路径对应不同的硬盘,增大Flume吞吐量。
checkpointDir和backupCheckpointDir也尽量配置在不同硬盘对应的目录中,保证checkpoint坏掉后,可以快速使用backupCheckpointDir恢复数据。
3)Sink:HDFS Sink
(1)HDFS存入大量小文件,有什么影响?
元数据层面:每个小文件都有一份元数据,其中包括文件路径,文件名,所有者,所属组,权限,创建时间等。所以小文件过多,会占用NameNode服务器大量内存,影响Namenode性能和使用寿命
计算层面:默认情况下MR会对每一个小文件启用一个Map任务计算,非常影响计算性能。同时也影响磁盘寻址时间
(2)HDFS小文件处理
1)Flume配置分析
2)Flume的具体配置如下:
在hadoop104的/opt/module/flume/conf目录下创建kafka-flume-hdfs.conf文件
vim kafka-flume-hdfs.conf
配置内容如下:
## 组件
a1.sources=r1
a1.channels=c1
a1.sinks=k1
## source1
a1.sources.r1.type = org.apache.flume.source.kafka.KafkaSource
a1.sources.r1.batchSize = 5000
a1.sources.r1.batchDurationMillis = 2000
a1.sources.r1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092,hadoop104:9092
a1.sources.r1.kafka.topics=topic_log
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = com.atguigu.flume.interceptor.TimeStampInterceptor$Builder
## channel1
a1.channels.c1.type = file
a1.channels.c1.checkpointDir = /opt/module/flume/checkpoint/behavior1
a1.channels.c1.dataDirs = /opt/module/flume/data/behavior1/
## sink1
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = /origin_data/gmall/log/topic_log/%Y-%m-%d
a1.sinks.k1.hdfs.filePrefix = log-
a1.sinks.k1.hdfs.round = false
#控制生成的小文件
a1.sinks.k1.hdfs.rollInterval = 10
a1.sinks.k1.hdfs.rollSize = 134217728
a1.sinks.k1.hdfs.rollCount = 0
## 控制输出文件是原生文件。
a1.sinks.k1.hdfs.fileType = CompressedStream
a1.sinks.k1.hdfs.codeC = lzop
## 拼装
a1.sources.r1.channels = c1
a1.sinks.k1.channel= c1
由于Flume默认会用Linux系统时间,作为输出到HDFS路径的时间。如果数据是23:59分产生的。Flume消费Kafka里面的数据时,有可能已经是第二天了,那么这部分数据会被发往第二天的HDFS路径。我们希望的是根据日志里面的实际时间,发往HDFS的路径,所以下面拦截器作用是获取日志中的实际时间。
解决的思路:拦截json日志,通过fastjson框架解析json,获取实际时间ts。将获取的ts时间写入拦截器header头,header的key必须是timestamp,因为Flume框架会根据这个key的值识别为时间,写入到HDFS。
1)在com.admin.flume.interceptor包下创建TimeStampInterceptor类
import com.alibaba.fastjson.JSONObject;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class TimeStampInterceptor implements Interceptor {
private ArrayList events = new ArrayList<>();
@Override
public void initialize() {
}
@Override
public Event intercept(Event event) {
Map headers = event.getHeaders();
String log = new String(event.getBody(), StandardCharsets.UTF_8);
JSONObject jsonObject = JSONObject.parseObject(log);
String ts = jsonObject.getString("ts");
headers.put("timestamp", ts);
return event;
}
@Override
public List intercept(List list) {
events.clear();
for (Event event : list) {
events.add(intercept(event));
}
return events;
}
@Override
public void close() {
}
public static class Builder implements Interceptor.Builder {
@Override
public Interceptor build() {
return new TimeStampInterceptor();
}
@Override
public void configure(Context context) {
}
}
}
2)重新打包
3)将jar包放到Hadoop102的/opt/module/flume/lib文件夹下
4)分发到hadoop103和hadoop104
xsync flume/
(1)在/home/admin/bin目录下创建脚本f2.sh
vim f2.sh
脚本内容:
#! /bin/bash
case $1 in
"start"){
for i in hadoop104
do
echo " --------启动 $i 消费flume-------"
ssh $i "nohup /opt/module/flume/bin/flume-ng agent --conf-file /opt/module/flume/conf/kafka-flume-hdfs.conf --name a1 -Dflume.root.logger=INFO,LOGFILE >/opt/module/flume/log2.txt 2>&1 &"
done
};;
"stop"){
for i in hadoop104
do
echo " --------停止 $i 消费flume-------"
ssh $i "ps -ef | grep kafka-flume-hdfs | grep -v grep |awk '{print \$2}' | xargs -n1 kill"
done
};;
esac
(2)增加脚本执行权限
chmod u+x f2.sh
(3)f2集群启动停止
f2.sh start
f2.sh stop
1)问题描述:如果启动消费Flume抛出如下异常
ERROR hdfs.HDFSEventSink: process failed
java.lang.OutOfMemoryError: GC overhead limit exceeded
2)解决方案
(1)在hadoop102服务器/opt/module/flume/confflume-env.sh文件中增加如下配置
export JAVA_OPTS="-Xms100m -Xmx2000m -Dcom.sun.management.jmxremote"
(2)同步到hadoop103、hadoop104
3)Flume内存参数设置及优化
/home/admin/bin创建脚本cluster.sh
vim cluster.sh
脚本内容:
#!/bin/bash
case $1 in
"start"){
echo ================== 启动 集群 ==================
#启动 Zookeeper集群
zk.sh start
#启动 Hadoop集群
hdp.sh start
#启动 Kafka采集集群
kf.sh start
#启动 Flume采集集群
f1.sh start
#启动 Flume消费集群
f2.sh start
};;
"stop"){
echo ================== 停止 集群 ==================
#停止 Flume消费集群
f2.sh stop
#停止 Flume采集集群
f1.sh stop
#停止 Kafka采集集群
kf.sh stop
#停止 Hadoop集群
hdp.sh stop
#停止 Zookeeper集群
zk.sh stop
};;
esac
(2)增加脚本执行权限 启动 停止
chmod u+x cluster.sh
cluster.sh start
cluster.sh stop
kafka如果无法关闭可以在hdp.sh stop后sleep 几秒
访问2NN页面hadoop104:9868看不到详细信息
(1)F12在控制台查看错误信息
(2)找到要修改的文件
cd /opt/module/hadoop-3.1.3/share/hadoop/hdfs/webapps/static
vim dfs-dust.js
修改第61行
return new Date(Number(v)).toLocaleString();