前置知识: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
}
服务器和jdk准备
编写集群分发脚本xsync
需求:循环赋值文件到所有节点的相同目录下
需求分析:
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配置文件
可配置日志生成路径,修改内容如下
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_HOME" value="/opt/module/applog/log" />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/app.%d{yyyy-MM-dd}.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<!-- 将某一个包下日志单独打印日志 -->
<logger name="com.atgugu.gmall2020.mock.log.util.LogUtil"
level="INFO" additivity="false">
<appender-ref ref="rollingFile" />
<appender-ref ref="console" />
</logger>
<root level="error" >
<appender-ref ref="console" />
</root>
</configuration>
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
Hadoop安装
(1)集群规划
LZO压缩配置
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压缩
<configuration>
<property>
<name>io.compression.codecs</name>
<value>
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
</value>
</property>
<property>
<name>io.compression.codec.lzo.class</name>
<value>com.hadoop.compression.lzo.LzoCodec</value>
</property>
</configuration>
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
LZO创建索引
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服务器上,并重新启动集群
<!--是否启动一个线程检查每个任务正使用的虚拟内存量,如果任务超出分配值,则直接将其杀掉,默认是true -->
<property>
<name>yarn.nodemanager.vmem-check-enabled</name>
<value>false</value>
</property>
Hadoop参数调优
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。
<property>
<name>dfs.namenode.handler.count</name>
<value>10</value>
</property>
2)YARN参数调优yarn-site.yml
Zookeeper安装
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
集群规划
ZK集群启停脚本
(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
Kafka安装
Kafka集群安装
安装部署
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
集群规划
Kafka集群启动停止脚本
(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
Kafka常用命令
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机器数量计算
Kafka机器数量(经验公式)= 2 *(峰值生产速度 * 副本数 / 100)+ 1
先拿到峰值生产速度,再根据设定的副本数,就能预估出需要部署Kafka的数量
Kafka分区数计算
采集日志Flume
日志采集Flume安装
解压到/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
集群规划
Flume组件选型
1)Source
(1)Taildir Source相比Exec Source、Spooling Directory Source的优势
(2)batchSize大小如何设置
Event 1K左右时,500-1000合适(默认100)
2)Channel
日志采集Flume配置
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是自定义的拦截器的全类名。需要根据用户自定义的拦截器做相应修改
Flume拦截器
1)创建Maven工程flume-interceptor
2)创建包名:com.admin.flume.interceptor
是自定义的拦截器的全类名。需要根据用户自定义的拦截器做相应修改
3)在pom.xml文件中添加如下配置
<dependencies>
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-core</artifactId>
<version>1.9.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
注: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<Event> intercept(List<Event> list) {
Iterator<Event> 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 &
测试Flume-Kafka通道
(1)生成日志
lg.sh
(2)消费Kafka数据,观察控制台是否有数据获取到
[/opt/module/kafka] bin/kafka-console-consumer.sh \
--bootstrap-server hadoop102:9092 --from-beginning --topic topic_log
日志采集Flume启动停止脚本
(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
消费Kafka数据Flume
Flume组件选型
1)FileChannel和MemoryChannel区别
2)FileChannel优化
通过配置dataDirs指向多个路径,每个路径对应不同的硬盘,增大Flume吞吐量。
checkpointDir和backupCheckpointDir也尽量配置在不同硬盘对应的目录中,保证checkpoint坏掉后,可以快速使用backupCheckpointDir恢复数据。
3)Sink:HDFS Sink
(1)HDFS存入大量小文件,有什么影响?
元数据层面:每个小文件都有一份元数据,其中包括文件路径,文件名,所有者,所属组,权限,创建时间等。所以小文件过多,会占用NameNode服务器大量内存,影响Namenode性能和使用寿命
计算层面:默认情况下MR会对每一个小文件启用一个Map任务计算,非常影响计算性能。同时也影响磁盘寻址时间
(2)HDFS小文件处理
消费者Flume配置
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时间拦截器
由于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<Event> events = new ArrayList<>();
@Override
public void initialize() {
}
@Override
public Event intercept(Event event) {
Map<String, String> 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<Event> intercept(List<Event> 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/
消费者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
Flume内存优化
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();