第4 章集群搭建&数据采集模块
4.0 虚拟机准备
4.0.1 虚拟机内存分配
1)从hadoop100的快照中克隆出3台虚拟机,并分配好内存
建议内存不低于4G,尤其是hadoop102,作为主机,后续可能要8G才能较顺利运行
机子 | 建议内存 | |
---|---|---|
Hadoop102 | 8G | 作为主节点 |
Hadoop103 | 2G | |
Hadoop104 | 2G |
2)原始虚拟机的已经创建好了root和user的账号&密码如下:
root: xxxxxxxx
atguigu: 123456
3)开启虚拟机
4.0.2 修改虚拟机IP (ROOT 权限下)
1)修改静态IP,
vim /etc/sysconfig/network-scripts/ifcfg-eth0
=================================
DEVICE=eth0
TYPE=Ethernet
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.1.102
PREFIX=24
GATEWAY=192.168.1.2
DNS1=114.114.114.114
NAME=eth0
执行 service network restart 重启,查看ifconfig 是否已经更新
2)修改hostname
vim /etc/sysconfig/network
改HOSTNAME=hadoop102
分别配置其他机器
机子 | IPADDR | HOSTNAME |
---|---|---|
Hadoop102 | .102 | hadoop102 |
Hadoop103 | .103 | hadoop103 |
Hadoop104 | .104 | hadoop104 |
3)修改克隆后虚拟机的ip
[root@hadoop101 /]# vim /etc/udev/rules.d/70-persistent-net.rules
进入如下页面,删除eth0该行;将eth1修改为eth0,同时复制物理ip地址,如下图所示(这步是针对Centos 6.8的,Centos 7不用此步操作)
4.0.3配置分发脚本
1)在/home/atguigu目录下创建bin目录,并在bin目录下xsync创建文件,文件内容如下:
[atguigu@hadoop102 ~]$ mkdir bin
[atguigu@hadoop102 ~]$ cd bin/
[atguigu@hadoop102 bin]$ vim xsync
内容如下:
#!/bin/bash
#1 获取输入参数个数,如果没有参数,直接退出
pcount=$#
if ((pcount==0)); then
echo no args;
exit;
fi
#2 获取文件名称
p1=$1
fname=`basename $p1`
echo fname=$fname
#3 获取上级目录到绝对路径
pdir=`cd -P $(dirname $p1); pwd`
echo pdir=$pdir
#4 获取当前用户名称
user=`whoami`
#5 循环
for((host=103; host<105; host++)); do
echo ------------------- hadoop$host --------------
rsync -av $pdir/$fname $user@hadoop$host:$pdir
done
2)修改执行权限
chmod +x xsync
3)文档放在bin目录下,使其有全局运行的权限
sudo cp xsync /bin
4)分发到集群,yes,输如其他机器的root密码
sudo xsync /bin/xsync
4.0.4 配置免密
哪个机子上有NameNode 或resourcemanage,即会与其他机子产生交互的,就需要配置ssh免密登陆。
生成公钥和私钥:
[atguigu@hadoop102 .ssh]$ ssh-keygen -t rsa
然后敲(三个回车),就会生成两个文件id_rsa(私钥)、id_rsa.pub(公钥)
将公钥拷贝到要免密登录的目标机器上
[atguigu@hadoop102 .ssh]$ ssh-copy-id hadoop102
[atguigu@hadoop102 .ssh]$ ssh-copy-id hadoop103
[atguigu@hadoop102 .ssh]$ ssh-copy-id hadoop104
注意,
还需要在hadoop103上采用atguigu账号,配置一下无密登录到hadoop102、hadoop103、hadoop104;
还需要在hadoop104上采用atguigu账号,配置一下无密登录到hadoop102、hadoop103、hadoop104服务器上。
4.0.5 JDK
1)在hadoop102,hadoop103, hadoop104安装JDK配置环境变量
2)将JDk jar包传入,解压到 /opt/module目录下
[atguigu@hadoop102 jdk1.8.0_144]$ tar -zxvf jdk-8u144-linux-x64.tar.gz -C /opt/module/
3)获取路径
[atguigu@hadoop102 jdk1.8.0_144]$ pwd
/opt/module/jdk1.8.0_144
4)修改配置文件,注意采用新方式,这样可以避免本地或远程时总要source profile
[atguigu@hadoop102 jdk1.8.0_144]$ sudo vim /etc/profile.d/env.sh
#JAVA_HOME
export JAVA_HOME=/opt/module/jdk1.8.0_144
export PATH=$PATH:$JAVA_HOME/bin
5)source文件使之生效,并查看jdk是否成功
[atguigu@hadoop102 jdk1.8.0_144]$ source /etc/ profile.d/env.sh
[atguigu@hadoop102 jdk1.8.0_144]$ java
用法: java [-options] class [args…]
5)分发jdk
[atguigu@hadoop102 ~]$ xsync jdk1.8.0_144/
6)分发 /etc/profile文件
[atguigu@hadoop102 ~]$ sudo xsync /etc/profile.d/env.sh
Source profile文件,输入java命令看java是否成功运行
[atguigu@hadoop103 ~]$ source /etc/profile.d/env.sh
[atguigu@hadoop104 ~]$ source /etc/profile.d/env.sh
4.1 hadoop安装
4.1.0 hadoop安装配置
1)解压hadoop-2.7.2到/opt/module目录下
[atguigu@hadoop102 hadoop-2.7.2]$ pwd
/opt/module/hadoop-2.7.2
2)配置环境变量
[atguigu@hadoop102 jdk1.8.0_144]$ sudo vim /etc/profile.d/env.sh
##HADOOP_HOME
export HADOOP_HOME=/opt/module/hadoop-2.7.2
export PATH=$PATH:$HADOOP_HOME/bin
export PATH=$PATH:$HADOOP_HOME/sbin
3)source配置文件
[atguigu@hadoop102 jdk1.8.0_144]$ source /etc/ profile.d/env.sh
4)修改8个配置文件(所有配置文件都在$HADOOP_HOME/etc/hadoop中)
5)配置hadoop-env.sh,yarn-env.sh,mapred-env.sh文件,
在每个文件第二行添加Java_HOME,
export JAVA_HOME=/opt/module/jdk1.8.0_144
6)配置Core-site.xml
<!-- 指定HDFS中NameNode的地址 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://hadoop102:9000</value>
</property>
<!-- 指定Hadoop运行时产生文件的存储目录 -->
<property>
<name>hadoop.tmp.dir</name>
<value>/opt/module/hadoop-2.7.2/data/tmp</value>
</property>
7) 配置hdfs-site.xml
<!-- 数据的副本数量正常配置3个副本 -->
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<!-- 指定Hadoop辅助名称节点主机配置 -->
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>hadoop104:50090</value>
</property>
8) 配置yarn-site.xml
<!-- Site specific YARN configuration properties -->
<!-- Reducer获取数据的方式 -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<!-- 指定YARN的ResourceManager的地址 -->
<property>
<name>yarn.resourcemanager.hostname</name>
<value>hadoop103</value>
</property>
<!-- 日志聚集功能使能 -->
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<!-- 日志保留时间设置7天 -->
<property>
<name>yarn.log-aggregation.retain-seconds</name>
<value>604800</value>
</property>
9) 配置mapred-site.xml
先重命名
[atguigu@hadoop102 hadoop]$ mv mapred-site.xml.template mapred-site.xml
<!-- 指定MR运行在Yarn上 -->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<!-- 历史服务器端地址 -->
<property>
<name>mapreduce.jobhistory.address</name>
<value>hadoop102:10020</value>
</property>
<!-- 历史服务器web端地址 -->
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>hadoop102:19888</value>
</property>
注:启动历史服务器命令:
mr-jobhistory-daemon.sh start historyserver
后续要开启改服务,以便更好的收集集群运行日志。
10) 配置Slaves(文件中无空格,无空行)
hadoop102
hadoop103
hadoop104
11)集群首次启动前必须要要格式化Namenode
在hadoop102:bin/hdfs namenode -format
注意:除非是新集群,否则不要没事就格式化NameNode!!
因此操作会产生新的集群id,导致NameNode和DataNode的集群id不一致,集群找不到已往数据。
所以,如果后续非要格式NameNode不可,一定要先删除data数据和log日志,然后再格式化NameNode。
11)启动hadoop集群
[atguigu@hadoop102 hadoop-2.7.2]$ sbin/start-dfs.sh
12)启动resourcemanage (YARN服务)
[atguigu@hadoop103 hadoop-2.7.2]$ sbin/start-yarn.sh
4.1.1 项目经验之HDFS 存储多目录
若HDFS 存储空间紧张,需要对DataNode 进行磁盘扩展。
1) 在DataNode 节点增加磁盘并进行挂载。
2)在hdfs-site.xml 文件中配置多目录,注意新挂载磁盘的访问权限问题。
<property>
<name>dfs.datanode.data.dir</name>
<value>file:///${hadoop.tmp.dir}/dfs/data1,file:///hd2/dfs/data2,file:///hd3/dfs/data3,file:///hd4/dfs/data4</value>
</property>
3)增加磁盘后,保证每个目录数据均衡
开启数据均衡命令:
bin/start-balancer.sh –threshold 10
对于参数10,代表的是集群中各个节点的磁盘空间利用率相差不超过10%,可根据实际情况进行调整。
停止数据均衡命令:
bin/stop-balancer.sh
4.1.2 项目经验之支持 LZO 压缩配置
1)hadoop 本身并不支持 lzo 压缩,故需要使用 twitter 提供的 hadoop-lzo 开源组件。hadoop-lzo 需依赖 hadoop 和 lzo 进行编译,编译步骤如下。环境准备参考附录1
2)将编译好后的 hadoop-lzo-0.4.20.jar 放入 hadoop-2.7.2/share/hadoop/common/
[atguigu@hadoop102 common]$ pwd
/opt/module/hadoop-2.7.2/share/hadoop/common [atguigu@hadoop102 common]$ ls
hadoop-lzo-0.4.20.jar
3)同步 hadoop-lzo-0.4.20.jar 到 hadoop103、hadoop104
[atguigu@hadoop102 common]$ xsync hadoop-lzo-0.4.20.jar
4)core-site.xml 增加配置支持 LZO 压缩(仅需红色字体)
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<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
[atguigu@hadoop102 hadoop]$ xsync core-site.xml
6)启动及查看集群
[atguigu@hadoop102 hadoop-2.7.2]$ sbin/start-dfs.sh
[atguigu@hadoop103 hadoop-2.7.2]$ sbin/start-yarn.sh
4.1.3 项目经验之 LZO 创建索引
1)创建 LZO 文件的索引,LZO 压缩文件的可切片特性依赖于其索引,故我们需要手动为LZO 压缩文件创建索引。若无索引,则 LZO 文件的切片只有一个。
[atguigu@hadoop102 module]$ hadoop jar /opt/module/hadoop-2.7.2/share/hadoop/common/hadoop-lzo-0.4.20.jar com.hadoop.compression.lzo.DistributedLzoIndexer /input/bigtable.lzo
2)测试
(1)将bigtable.lzo(150M)上传到集群的根目录
[atguigu@hadoop102 module]$ hadoop fs -mkdir /input
[atguigu@hadoop102 module]$ hadoop fs -put bigtable.lzo /input
(2)执行wordcount 程序
[atguigu@hadoop102 module]$ hadoop jar /opt/module/hadoop-2.7.2/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.2.jar wordcount /input /output1
(3)对上传的LZO 文件建索引
[atguigu@hadoop102 module]$ hadoop jar /opt/module/hadoop-2.7.2/share/hadoop/common/hadoop-lzo-0.4.20.jar com.hadoop.compression.lzo.DistributedLzoIndexer /input/bigtable.lzo
(4)再次执行WordCount 程序
[atguigu@hadoop102 module]$ hadoop jar /opt/module/hadoop-2.7.2/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.2.jar wordcount /input /output2
4.1.4 项目经验之基准测试
1) 测试 HDFS 写性能
测试内容:向 HDFS 集群写 10 个 128M 的文件
[atguigu@hadoop102 mapreduce]$ hadoop jar /opt/module/hadoop-2.7.2/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-2.7.2-tests.jar TestDFSIO -write -nrFiles 10 -fileSize 128MB
19/05/02 11:45:23 INFO fs.TestDFSIO: ----- TestDFSIO ----- : write
19/05/02 11:45:23 INFO fs.TestDFSIO: Date & time: Thu May 02 11:45:23 CST 2019
19/05/02 11:45:23 INFO fs.TestDFSIO: Number of files: 10
19/05/02 11:45:23 INFO fs.TestDFSIO: Total MBytes processed: 1280.0
19/05/02 11:45:23 INFO fs.TestDFSIO: Throughput mb/sec: 10.69751115716984 吞吐量指标
19/05/02 11:45:23 INFO fs.TestDFSIO: Average IO rate mb/sec: 14.91699504852295
19/05/02 11:45:23 INFO fs.TestDFSIO: IO rate std deviation: 11.160882132355928
19/05/02 11:45:23 INFO fs.TestDFSIO: Test exec time sec: 52.315
2)测试 HDFS 读性能
测试内容:读取 HDFS 集群 10 个 128M 的文件
[atguigu@hadoop102 mapreduce]$ hadoop jar /opt/module/hadoop-2.7.2/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-2.7.2-tests.jar TestDFSIO -read -nrFiles 10 -fileSize 128MB
19/05/02 11:56:36 INFO fs.TestDFSIO: ----- TestDFSIO ----- : read
19/05/02 11:56:36 INFO fs.TestDFSIO: Date & time: Thu May 02 11:56:36 CST 2019
19/05/02 11:56:36 INFO fs.TestDFSIO: Number of files: 10
19/05/02 11:56:36 INFO fs.TestDFSIO: Total MBytes processed: 1280.0
19/05/02 11:56:36 INFO fs.TestDFSIO: Throughput mb/sec: 16.001000062503905
19/05/02 11:56:36 INFO fs.TestDFSIO: Average IO rate mb/sec: 17.202795028686523
19/05/02 11:56:36 INFO fs.TestDFSIO: IO rate std deviation: 4.881590515873911
19/05/02 11:56:36 INFO fs.TestDFSIO: Test exec time sec: 49.116
19/05/02 11:56:36 INFO fs.TestDFSIO:
3)删除测试生成数据
[atguigu@hadoop102 mapreduce]$ hadoop jar /opt/module/hadoop-2.7.2/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-2.7.2-tests.jar TestDFSIO -clean
4)使用 Sort 程序评测 MapReduce
(1)使用 RandomWriter 来产生随机数,每个节点运行 10 个 Map 任务,每个 Map 产 生大约 1G 大小的二进制随机数
[atguigu@hadoop102 mapreduce]$ hadoop jar /opt/module/hadoop-2.7.2/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.2.jar randomwriter random-data
(2)执行 Sort 程序
[atguigu@hadoop102 mapreduce]$ hadoop jar /opt/module/hadoop-2.7.2/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.2.jar sort random-data sorted-data
(3)验证数据是否真正排好序了
[atguigu@hadoop102 mapreduce]$ hadoop jar /opt/module/hadoop-2.7.2/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-2.7.2-tests.jar testmapredsort -sortInput random-data -sortOutput sorted-data
4.1.5 项目经验之Hadoop 参数调优
1)HDFS 参数调优hdfs-site.xml
dfs.namenode.handler.count=20 * log2(Cluster Size),比如集群规模为8 台时,此参数设置为60
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。设置该值的一般原则是将其设置为集群大小的自然对数乘以20,即20logN,N 为集群大小。
2)YARN 参数调优yarn-site.xml
(1)情景描述:总共7 台机器,每天几亿条数据,数据源->Flume->Kafka->HDFS->Hive
面临问题:数据统计主要用HiveSQL,没有数据倾斜,小文件已经做了合并处理,开
启的JVM 重用,而且IO 没有阻塞,内存用了不到50%。但是还是跑的非常慢,而且数据
量洪峰过来时,整个集群都会宕掉。基于这种情况有没有优化方案。
(2)解决办法:
内存利用率不够。这个一般是Yarn 的2 个配置造成的,单个任务可以申请的最大内存
大小,和Hadoop 单个节点可用内存大小。调节这两个参数能提高系统内存的利用率。
(a)yarn.nodemanager.resource.memory-mb
表示该节点上YARN 可使用的物理内存总量,默认是8192(MB),注意,如果你的节点
内存资源不够8GB,则需要调减小这个值,而YARN 不会智能的探测节点的物理内存总量。
(b)yarn.scheduler.maximum-allocation-mb
单个任务可申请的最多物理内存量,默认是8192(MB)。
3)Hadoop 宕机
(1)如果MR 造成系统宕机。此时要控制Yarn 同时运行的任务数,和每个任务申请的
最大内存。调整参数:yarn.scheduler.maximum-allocation-mb(单个任务可申请的最多物
理内存量,默认是8192MB)
(2)如果写入文件过量造成NameNode 宕机。那么调高Kafka 的存储大小,控制从Kafka到HDFS 的写入速度。高峰期的时候用Kafka 进行缓存,高峰期过去数据同步会自动跟上。
4.2 Zookeeper 安装
4.2.1 安装ZK
集群规划
服务器hadoop102 | 服务器hadoop103 | 服务器hadoop104 | |
---|---|---|---|
Zookeeper | Zookeeper | Zookeeper | Zookeeper |
1.集群规划
在hadoop102、hadoop103和hadoop104三个节点上部署Zookeeper。
2.解压安装
(1)解压Zookeeper安装包到/opt/module/目录下
[atguigu@hadoop102 software]$ tar -zxvf zookeeper-3.4.10.tar.gz -C /opt/module/
(2)同步/opt/module/zookeeper-3.4.10目录内容到hadoop103、hadoop104
[atguigu@hadoop102 module]$ xsync zookeeper-3.4.10/
3.配置服务器编号
(1)在/opt/module/zookeeper-3.4.10/这个目录下创建zkData
[atguigu@hadoop102 zookeeper-3.4.10]$ mkdir -p zkData
(2)在/opt/module/zookeeper-3.4.10/zkData目录下创建一个myid的文件
[atguigu@hadoop102 zkData]$ touch myid
添加myid文件,注意一定要在linux里面创建,在notepad++里面很可能乱码
(3)编辑myid文件
[atguigu@hadoop102 zkData]$ vi myid
在文件中添加与server对应的编号:
2
(4)拷贝配置好的zookeeper到其他机器上
[atguigu@hadoop102 zkData]$ xsync myid
并分别在hadoop103、hadoop104上修改myid文件中内容为3、4
4.配置zoo.cfg文件
(1)重命名/opt/module/zookeeper-3.4.10/conf这个目录下的zoo_sample.cfg为zoo.cfg
[atguigu@hadoop102 conf]$ mv zoo_sample.cfg zoo.cfg
(2)打开zoo.cfg文件
[atguigu@hadoop102 conf]$ vim zoo.cfg
修改数据存储路径配置
dataDir=/opt/module/zookeeper-3.4.10/zkData
增加如下配置
#######################cluster##########################
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:3888
server.4=hadoop104:2888:3888
(3)同步zoo.cfg配置文件
[atguigu@hadoop102 conf]$ xsync zoo.cfg
(4)配置参数解读
server.A=B:C:D。
A是一个数字,表示这个是第几号服务器;
集群模式下配置一个文件myid,这个文件在dataDir目录下,这个文件里面有一个数据就是A的值,Zookeeper启动时读取此文件,拿到里面的数据与zoo.cfg里面的配置信息比较从而判断到底是哪个server。
B是这个服务器的地址;
C是这个服务器Follower与集群中的Leader服务器交换信息的端口;
D是万一集群中的Leader服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口。
(5) 配置Zookeeper的LogDIR:
配置bin/zkEnv.sh文件
ZOO_LOG_DIR="."改为/opt/module/zookeeper-3.4.10/logs
5.集群操作
(1)分别启动Zookeeper
[atguigu@hadoop102 zookeeper-3.4.10]$ bin/zkServer.sh start
[atguigu@hadoop103 zookeeper-3.4.10]$ bin/zkServer.sh start
[atguigu@hadoop104 zookeeper-3.4.10]$ bin/zkServer.sh start
(2)查看状态
[atguigu@hadoop102 zookeeper-3.4.10]# bin/zkServer.sh status
JMX enabled by default
Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg
Mode: follower
[atguigu@hadoop103 zookeeper-3.4.10]# bin/zkServer.sh status
JMX enabled by default
Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg
Mode: leader
[atguigu@hadoop104 zookeeper-3.4.5]# bin/zkServer.sh status
JMX enabled by default
Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg
Mode: follower
4.2.2 ZK 集群启动停止脚本
1)在hadoop102 的/home/atguigu/bin 目录下创建脚本
[atguigu@hadoop102 bin]$ vim zk.sh
在脚本中编写如下内容
#! /bin/bash
case $1 in
"start"){
for i in hadoop102 hadoop103 hadoop104
do
ssh $i "/opt/module/zookeeper-3.4.10/bin/zkServer.sh start"
done
};;
"stop"){
for i in hadoop102 hadoop103 hadoop104
do
ssh $i "/opt/module/zookeeper-3.4.10/bin/zkServer.sh stop"
done
};;
"status"){
for i in hadoop102 hadoop103 hadoop104
do
ssh $i "/opt/module/zookeeper-3.4.10/bin/zkServer.sh status"
done
};;
esac
2)增加脚本执行权限
[atguigu@hadoop102 bin]$ chmod 777 zk.sh
3)Zookeeper 集群启动脚本
[atguigu@hadoop102 module]$ zk.sh start
4)Zookeeper 集群停止脚本
[atguigu@hadoop102 module]$ zk.sh stop
4.2.3 项目经验之Linux 环境变量
1)修改/etc/profile 文件:用来设置系统环境参数,比如$PATH. 这里面的环境变量是对系统内所有用户生效。使用bash 命令,需要source /etc/profile 一下。
2)修改~/.bashrc 文件:针对某一个特定的用户,环境变量的设置只对该用户自己有效。
使用bash 命令,只要以该用户身份运行命令行就会读取该文件。
3)把/etc/profile 里面的环境变量追加到~/.bashrc 目录
[atguigu@hadoop102 ~]$ cat /etc/profile >> ~/.bashrc
[atguigu@hadoop103 ~]$ cat /etc/profile >> ~/.bashrc
[atguigu@hadoop104 ~]$ cat /etc/profile >> ~/.bashrc
4)说明
登录式Shell,采用用户名比如atguigu 登录,会自动加载/etc/profile
非登录式Shell,采用ssh 比如ssh hadoop103 登录,不会自动加载/etc/profile,会自动加载~/.bashrc
[atguigu@hadoop102 ~]$ vim .bashrc
# .bashrc
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# User specific aliases and functions
[atguigu@hadoop102 ~]$ vim /etc/bashrc
可以看到会遍历执行/ect/profile.d/下的sh命令,而我们前面的环境变量就是配置在profile.d/下的,这样会更专业!
4.3 日志生成
4.3.1 日志启动
1)代码参数说明
// 参数一:控制发送每条的延时时间,默认是0
Long delay = args.length > 0 ? Long.parseLong(args[0]) : 0L;
// 参数二:循环遍历次数
int loop_len = args.length > 1 ? Integer.parseInt(args[1]) : 1000;
2 ) 将生成的jar 包log-collector-0.0.1-SNAPSHOT-jar-with-dependencies.jar 拷贝到hadoop102 服务器/opt/module 上,并同步到hadoop103 的/opt/module 路径下,
[atguigu@hadoop102 module]$ xsync log-collector-1.0-SNAPSHOT-jar-with-dependencies.jar
3)在hadoop102 上执行jar 程序
[atguigu@hadoop102 module]$ java -classpath log-collector-1.0-SNAPSHOT-jar-with-dependencies.jar com.atguigu.appclient.AppMain >/opt/module/test.log
说明1:
java -classpath 需要在jar 包后面指定全类名;
java -jar 需要查看一下解压的jar 包META-INF/ MANIFEST.MF 文件中,Main-Class 是否有全类名。如果有可以用java -jar,如果没有就需要用到java -classpath
已经在创建Maven是打包好了
说明2:/dev/null 代表linux 的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”。
标准输入0:从键盘获得输入/proc/self/fd/0
标准输出1:输出到屏幕(即控制台) /proc/self/fd/1
错误输出2:输出到屏幕(即控制台) /proc/self/fd/2
[atguigu@hadoop102 module]$ java -classpath log-collector-1.0-SNAPSHOT-jar-with-dependencies.jar com.atguigu.appclient.AppMain >/dev/null 2>&1
或者
[atguigu@hadoop102 module]$ java -classpath log-collector-1.0-SNAPSHOT-jar-with-dependencies.jar com.atguigu.appclient.AppMain 1>dev/null 2>/dev/null
4)在/tmp/logs 路径下查看生成的日志文件
[atguigu@hadoop102 module]$ cd /tmp/logs/
[atguigu@hadoop102 logs]$ ls app-2020-03-10.log
4.3.2 集群日志生成启动脚本
1)在/home/atguigu/bin 目录下创建脚本lg.sh
[atguigu@hadoop102 bin]$ vim lg.sh
2)在脚本中编写如下内容
#! /bin/bash
for i in hadoop102 hadoop103
do
echo ---------------$i 生成日志----------------
ssh $i "java -classpath /opt/module/log-collector-1.0-SNAPSHOT-jar-with-dependencies.jar com.atguigu.appclient.AppMain $1 $2 >/dev/null 2>&1 &"
done
# &1 ,&2 是两个参数 ,末尾的&是保持后台运行
# &1 ,&2 是两个参数 ,末尾的&是保持后台运行
3)修改脚本执行权限
[atguigu@hadoop102 bin]$ chmod 777 lg.sh
4)启动脚本
[atguigu@hadoop102 module]$ lg.sh
5)分别在hadoop102、hadoop103 的/tmp/logs 目录上查看生成的数据
[atguigu@hadoop102 logs]$ ls app-2020-03-10.log
[atguigu@hadoop103 logs]$ ls app-2020-03-10.log
4.3.3 集群时间同步修改脚本(非正规临时脚本)
企业开发时,参考hadoop 集群时间同步。
1)在/home/atguigu/bin 目录下创建脚本dt.sh
[atguigu@hadoop102 bin]$ vim dt.sh
2)在脚本中编写如下内容
#!/bin/bash
for i in hadoop102 hadoop103 hadoop104
do
echo "========== $i 同步时间=========="
ssh -t $i "sudo date -s $1"
done
注意:ssh -t 通常用于ssh 远程执行sudo 命令
3)修改脚本执行权限
[atguigu@hadoop102 bin]$ chmod 777 dt.sh
4)启动脚本
[atguigu@hadoop102 bin]$ dt.sh 2020-03-10
4.3.4 集群所有进程查看脚本
1)在/home/atguigu/bin 目录下创建脚本xcall.sh
[atguigu@hadoop102 bin]$ vim xcall.sh
2)在脚本中编写如下内容
#! /bin/bash
for i in hadoop102 hadoop103 hadoop104
do
echo --------- $i ----------
ssh $i "$*"
done
#$*传入的参数全部执行
3)修改脚本执行权限
[atguigu@hadoop102 bin]$ chmod 777 xcall.sh
4)启动脚本
[atguigu@hadoop102 bin]$ xcall.sh jps
4.4 采集日志Flume
集群规划:
服务器hadoop102 | 服务器hadoop103 | 服务器hadoop104 | |
---|---|---|---|
Flume(采集日志) | Flume | Flume |
Flume的安装部署
1)将apache-flume-1.7.0-bin.tar.gz上传到linux的/opt/software目录下
2)解压apache-flume-1.7.0-bin.tar.gz到/opt/module/目录下
[atguigu@hadoop102 software]$ tar -zxf apache-flume-1.7.0-bin.tar.gz -C /opt/module/
3)修改apache-flume-1.7.0-bin的名称为flume
[atguigu@hadoop102 module]$ mv apache-flume-1.7.0-bin flume
4) 将flume/conf下的flume-env.sh.template文件修改为flume-env.sh,并配置flume-env.sh文件
[atguigu@hadoop102 conf]$ mv flume-env.sh.template flume-env.sh
[atguigu@hadoop102 conf]$ vi flume-env.sh
Echo $JAVA_HOME显示Java_home路径,拷贝
export JAVA_HOME=/opt/module/jdk1.8.0_144
5)分发
4.4.2 项目经验之Flume 组件
1)Source
(1)Taildir Source 相比Exec Source、Spooling Directory Source 的优势
TailDir Source:断点续传、多目录。Flume1.6 以前需要自己自定义Source 记录每次读取文件位置,实现断点续传。
Exec Source 可以实时搜集数据,但是在Flume 不运行或者Shell 命令出错的情况下,数据将会丢失。
Spooling Directory Source 监控目录,不支持断点续传。
(2)batchSize 大小如何设置?
答:Event 1K 左右时,500-1000 合适(默认为100)
2)Channel
**采用Kafka Channel,省去了Sink,提高了效率。KafkaChannel 数据存储在**Kafka 里面,所以数据是存储在磁盘中。
注意在Flume1.7 以前,Kafka Channel 很少有人使用,因为发现parseAsFlumeEvent 这个配置起不了作用。也就是无论parseAsFlumeEvent 配置为true 还是false,都会转为Flume Event。这样的话,造成的结果是,会始终都把Flume 的headers 中的信息混合着内容一起写入Kafka的消息中,这显然不是我所需要的,我只是需要把内容写入即可。
4.4.3 日志采集Flume 配置
1)Flume 配置分析
Flume 直接读log 日志的数据,log 日志的格式是app-yyyy-mm-dd.log。
2)Flume 的具体配置如下:
(1)在/opt/module/flume/conf 目录下创建file-flume-kafka.conf 文件
[atguigu@hadoop102 conf]$ vim file-flume-kafka.conf
在文件配置如下内容
a1.sources=r1
a1.channels=c1 c2
# configure source
a1.sources.r1.type = TAILDIR
a1.sources.r1.positionFile =/opt/module/flume/test/log_position.json
a1.sources.r1.filegroups = f1
#app.+ 正则,表示app开头的都匹配
a1.sources.r1.filegroups.f1 = /tmp/logs/app.+
a1.sources.r1.fileHeader = true
a1.sources.r1.channels = c1 c2
#interceptor
a1.sources.r1.interceptors = i1 i2
a1.sources.r1.interceptors.i1.type =com.atguigu.flume.interceptor.LogETLInterceptor$Builder
a1.sources.r1.interceptors.i2.type =com.atguigu.flume.interceptor.LogTypeInterceptor$Builder
a1.sources.r1.selector.type = multiplexing
a1.sources.r1.selector.header = topic
a1.sources.r1.selector.mapping.topic_start = c1
a1.sources.r1.selector.mapping.topic_event = c2
# configure channel
a1.channels.c1.type =org.apache.flume.channel.kafka.KafkaChannel
a1.channels.c1.kafka.bootstrap.servers =hadoop102:9092,hadoop103:9092,hadoop104:9092
a1.channels.c1.kafka.topic = topic_start
a1.channels.c1.parseAsFlumeEvent = false
a1.channels.c1.kafka.consumer.group.id = flume-consumer
a1.channels.c2.type =org.apache.flume.channel.kafka.KafkaChannel
a1.channels.c2.kafka.bootstrap.servers =hadoop102:9092,hadoop103:9092,hadoop104:9092
a1.channels.c2.kafka.topic = topic_event
a1.channels.c2.parseAsFlumeEvent = false
a1.channels.c2.kafka.consumer.group.id = flume-consumer
注意:com.atguigu.flume.interceptor.LogETLInterceptor 和com.atguigu.flume.interceptor.LogTypeInterceptor 是自定义的拦截器的全类名。需要根据用户自定义的拦截器做相应修改
4.4.4 Flume 的ETL 和分类型拦截器
本项目中自定义了两个拦截器,分别是:ETL 拦截器、日志类型区分拦截器。
ETL 拦截器主要用于,过滤时间戳不合法和Json 数据不完整的日志
日志类型区分拦截器主要用于,将启动日志和事件日志区分开来,方便发往Kafka 的不同Topic。
自定义拦截器步骤:
- 定义一个类,实现Interceptor接口
- 重写4个方法
初始化
单event
Event能获取到body header
ETL =>body=> 主要判断的就是json数据是否已{开头,以}结尾 服务器时间(长度13,且全部为数字)
分类型拦截器=> body, header => 根据body区分类型,把相应数据添加到头里面 topic, start_topic/ event_topic
多event
关闭资源 - 静态内部类
- 打包上传
1)创建Maven 工程flume-interceptor
2)创建包名:com.atguigu.flume.interceptor
3)在pom.xml 文件中添加如下配置
<dependencies>
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-core</artifactId>
<version>1.7.0</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>
4)在com.atguigu.flume.interceptor 包下创建LogETLInterceptor 类名
Flume ETL 拦截器LogETLInterceptor
package com.atguigu.flume.interceptor;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
public class LogETLInterceptor implements Interceptor {
//初始化
@Override
public void initialize() {
}
//单Event处理
@Override
public Event intercept(Event event) {
//清洗数据 ETL 去除脏数据
//{"action":"1","ar":"MX","ba":"HTC","d
//1.获取日志
byte[] body = event.getBody();
String log = new String(body, Charset.forName("UTF-8"));
//2.区分类型处理
if (log.contains("start")){
//验证启动日志的逻辑
if (LogUtils.validateStart(log)){
return event;
}
}else{
//验证事件日志的逻辑
if (LogUtils.validateEvent(log)){
return event;
}
}
return null;
}
//多event处理
@Override
public List<Event> intercept(List<Event> events) {
ArrayList<Event> interceptors = new ArrayList<>();
//取出校验合格的数据并返回
for (Event event : events) {
Event intercept1 = intercept(event);
if (intercept1 != null){
interceptors.add(intercept1);
}
}
return interceptors;
}
//关闭资源
@Override
public void close() {
}
public static class Builder implements Interceptor.Builder{
@Override
public Interceptor build() {
return new LogETLInterceptor();
}
@Override
public void configure(Context context) {
}
}
}
4)Flume 日志过滤工具类
package com.atguigu.flume.interceptor;
import org.apache.commons.lang.math.NumberUtils;
public class LogUtils {
//验证启动日志
public static boolean validateStart(String log) {
//{"action":"1","ar":"MX","ba":"HTC","detail":"325","en":"start","}
if (log == null){
return false;
}
//判断数据是否是{开头 是否是}结尾
if (!log.trim().startsWith("{") || !log.trim().endsWith("}")){
return false;
}
return true;
}
//验证事件日志
// {"action":"1","ar":"MX","ba":"HTC","detail":"325","en":"start
public static boolean validateEvent(String log) {
//服务器事件|日志内容
if ( log == null){
return false;
}
//切割
String[] logContents = log.split("\\|");//正则里用\|表示|,故再加上\表示转义。。
if (logContents.length != 2){
return false;
}
//校验服务器时间(长度必须是13位,必须全部是数字
if (logContents[0].length()!= 13 || !NumberUtils.isDigits(logContents[0])){
return false;
}
//校验日志格式
if (!logContents[1].trim().startsWith("{") || !logContents[1].trim().endsWith("}")){
return false;
}
return true;
}
}
5) Flume 日志类型区分拦截器LogTypeInterceptor
6) package com.atguigu.flume.interceptor;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class LogTypeInterceptor implements Interceptor {
@Override
public void initialize() {
}
@Override
public Event intercept(Event event) {
//区分类型 Start event
//body header
//获取body
byte[] body = event.getBody();
String log = new String(body, Charset.forName("UTF-8"));
//获取头信息
Map<String, String> headers = event.getHeaders();
//业务逻辑判断
if (log.contains("start")){
headers.put("topic","topic_start" );//不产生新对象
}else{
headers.put("topic","topic_event" );
}
return event;
}
@Override
public List<Event> intercept(List<Event> events) {
ArrayList<Event> interceptors = new ArrayList<>();
for (Event event : events) {
Event intercept1 = intercept(event);
interceptors.add(intercept1);
}
return interceptors;
}
@Override
public void close() {
}
public static class Builder implements Interceptor.Builder{
@Override
public Interceptor build() {
return new LogTypeInterceptor();
}
@Override
public void configure(Context context) {
}
}
}
6)打包
拦截器打包之后,只需要单独包,不需要将依赖的包上传。打包之后要放入Flume 的
lib 文件夹下面。
注意:为什么不需要依赖包?因为依赖包在flume 的lib 目录下面已经存在了。
7)需要先将打好的包放入到hadoop102 的/opt/module/flume/lib 文件夹下面。
[atguigu@hadoop102 lib]$ ls | grep interceptor flume-interceptor-1.0-SNAPSHOT.jar
8)分发Flume 到hadoop103、hadoop104
[atguigu@hadoop102 module]$ xsync flume/
[atguigu@hadoop102 flume]$ bin/flume-ng agent --name a1 --conf-file conf/file-flume-kafka.conf &
4.4.5 日志采集Flume 启动停止脚本
1)在/home/atguigu/bin 目录下创建脚本f1.sh
[atguigu@hadoop102 bin]$ 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/test1 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 kill"
done
};;
esac
说明1:nohup,该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。nohup
就是不挂起的意思,不挂断地运行命令。
说明2:awk 默认分隔符为空格 {print $2},\表示awk的参数,转义
说明3:xargs 表示取出前面命令运行的结果,作为后面命令的输入参数。
2)增加脚本执行权限
[atguigu@hadoop102 bin]$ chmod 777 f1.sh
3)f1 集群启动脚本
[atguigu@hadoop102 module]$ f1.sh start
4)f1 集群停止脚本
[atguigu@hadoop102 module]$ f1.sh stop
4.5 Kafka 安装
4.5.1 Kafka 集群安装
集群规划:
服务器hadoop102 | 服务器hadoop103 | 服务器hadoop104 | |
---|---|---|---|
Kafka | Kafka | Kafka | Kafka |
集群部署
1)解压安装包
[atguigu@hadoop102 software]$ tar -zxvf kafka_2.11-0.11.0.0.tgz -C /opt/module/
2)修改解压后的文件名称
[atguigu@hadoop102 module]$ mv kafka_2.11-0.11.0.0/ kafka
3)在/opt/module/kafka目录下创建logs文件夹
[atguigu@hadoop102 kafka]$ mkdir logs
4)修改配置文件
[atguigu@hadoop102 kafka]$ cd config/
[atguigu@hadoop102 config]$ vi server.properties
输入以下内容:
#broker的全局唯一编号,不能重复
broker.id=0
#删除topic功能使能
delete.topic.enable=true
#处理网络请求的线程数量
num.network.threads=3
#用来处理磁盘IO的现成数量
num.io.threads=8
#发送套接字的缓冲区大小
socket.send.buffer.bytes=102400
#接收套接字的缓冲区大小
socket.receive.buffer.bytes=102400
#请求套接字的缓冲区大小
socket.request.max.bytes=104857600
#kafka运行日志存放的路径
log.dirs=/opt/module/kafka/logs
#topic在当前broker上的分区个数
num.partitions=1
#用来恢复和清理data下数据的线程数量
num.recovery.threads.per.data.dir=1
#segment文件保留的最长时间,超时将被删除
log.retention.hours=168
#配置连接Zookeeper集群地址
zookeeper.connect=hadoop102:2181,hadoop103:2181,hadoop104:2181
5)配置环境变量
[atguigu@hadoop102 module]$ sudo vi /etc/profile.d/env.sh
#KAFKA_HOME
export KAFKA_HOME=/opt/module/kafka
export PATH=$PATH:$KAFKA_HOME/bin
[atguigu@hadoop102 module]$ source /etc/profile.d/env.sh
6)分发安装包
[atguigu@hadoop102 module]$ xsync kafka/
**注意:分发之后记得配置其他机器的环境变量 source**
7)分别在hadoop103和hadoop104上修改配置文件/opt/module/kafka/config/server.properties中的broker.id=1、broker.id=2
注:broker.id不得重复
7-1)启动zookeeper
[atguigu@hadoop102 zookeeper-3.4.10]$ bin/zkServer.sh start
8)启动集群
依次在hadoop102、hadoop103、hadoop104节点上启动kafka
[atguigu@hadoop102 kafka]$ bin/kafka-server-start.sh -daemon config/server.properties
[atguigu@hadoop103 kafka]$ bin/kafka-server-start.sh -daemon config/server.properties
[atguigu@hadoop104 kafka]$ bin/kafka-server-start.sh -daemon config/server.properties
9)关闭集群
[atguigu@hadoop102 kafka]$ bin/kafka-server-stop.sh stop
[atguigu@hadoop103 kafka]$ bin/kafka-server-stop.sh stop
[atguigu@hadoop104 kafka]$ bin/kafka-server-stop.sh stop
4.5.2 Kafka 集群启动停止脚本
1)在/home/atguigu/bin 目录下创建脚本kf.sh
[atguigu@hadoop102 bin]$ 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)增加脚本执行权限
[atguigu@hadoop102 bin]$ chmod 777 kf.sh
3)kf 集群启动脚本
[atguigu@hadoop102 module]$ kf.sh start
4)kf 集群停止脚本
[atguigu@hadoop102 module]$ kf.sh stop
4.5.3 查看Kafka Topic 列表
[atguigu@hadoop102 kafka]$ bin/kafka-topics.sh --zookeeper hadoop102:2181 --list
4.5.4 创建Kafka Topic
进入到/opt/module/kafka/目录下分别创建:启动日志主题、事件日志主题。
1)创建启动日志主题
[atguigu@hadoop102 kafka]$ bin/kafka-topics.sh –zookeeper hadoop102:2181,hadoop103:2181,hadoop104:2181 –create --replication-factor 1 --partitions 1 --topic topic_start
2)创建事件日志主题
[atguigu@hadoop102 kafka]$ bin/kafka-topics.sh –zookeeper hadoop102:2181,hadoop103:2181,hadoop104:2181 –create --replication-factor 1 --partitions 1 --topic topic_event
4.5.5 删除Kafka Topic
1)删除启动日志主题
[atguigu@hadoop102 kafka]$ bin/kafka-topics.sh –delete --zookeeper hadoop102:2181,hadoop103:2181,hadoop104:2181 --topic topic_start
2)删除事件日志主题
[atguigu@hadoop102 kafka]$ bin/kafka-topics.sh –delete --zookeeper hadoop102:2181,hadoop103:2181,hadoop104:2181 --topic topic_event
4.5.6 Kafka 生产消息
[atguigu@hadoop102 kafka]$ bin/kafka-console-producer.sh --broker-list hadoop102:9092 --topic topic_start
\>hello world
\>atguigu atguigu
4.5.7 Kafka 消费消息
[atguigu@hadoop102 kafka]$ bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --from-beginning --topic topic_start
[atguigu@hadoop102 kafka]$ bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --from-beginning --topic topic_event
–from-beginning:会把主题中以往所有的数据都读取出来。根据业务场景选择是否增加该配置。
4.5.8 查看Kafka Topic 详情
[atguigu@hadoop102 kafka]$ bin/kafka-topics.sh –zookeeper hadoop102:2181 \
--describe --topic topic_start
**排错小技巧
\1. 先查看kafka中个的Topic,是否有我们需要的
\2. 如果有,直接消费对应的消息, 如果没有,尝试重启flume在查看topic
\3. 一般自定义interceptor会出问题,必要时检查自己写的程序。
一定要先起zookeeper,后起kafka
先关zookeeper的时候,kafka无法温柔关闭,只能强杀进程 kill -9 [进程号]
4.5.9 项目经验之Kafka 压力测试
1)Kafka 压测
用Kafka 官方自带的脚本,对Kafka 进行压测。Kafka 压测时,可以查看到哪个地方出
现了瓶颈(CPU,内存,网络IO)。一般都是网络IO 达到瓶颈。
kafka-consumer-perf-test.sh
kafka-producer-perf-test.sh
2)Kafka Producer 压力测试
(1)在/opt/module/kafka/bin 目录下面有这两个文件。我们来测试一下
[atguigu@hadoop102 kafka]$ bin/kafka-producer-perf-test.sh --topic test --record-size 100 --num-records 100000 --throughput -1 --producer-props bootstrap.servers=hadoop102:9092,hadoop103:9092,hadoop104:9092
说明:
record-size 是一条信息有多大,单位是字节。
num-records 是总共发送多少条信息。
throughput 是每秒多少条信息,设成-1,表示不限流,可测出生产者最大吞吐量。
(2)Kafka 会打印下面的信息
别人家的电脑
100000 records sent, 95877.277085 records/sec (**9.14 MB/sec**),187.68 ms avg latency, 424.00 ms max latency, 155 ms 50th, 411 ms95th, 423 ms 99th, 424 ms 99.9th.
我的电脑
100000 records sent, 57273.768614 records/sec (5.46 MB/sec), 589.37 ms avg latency, 893.00 ms max latency, 626 ms 50th, 875 ms 95th, 889 ms 99th, 892 ms 99.9th
参数解析:本例中一共写入10w 条消息,吞吐量为9.14 MB/sec,每次写入的平均延迟
为187.68 毫秒,最大的延迟为424.00 毫秒。
3)Kafka Consumer 压力测试
Consumer 的测试,如果这四个指标(IO,CPU,内存,网络)都不能改变,考虑增加
分区数来提升性能。
[atguigu@hadoop102 kafka]$ bin/kafka-consumer-perf-test.sh --zookeeper hadoop102:2181 --topic test --fetch-size 10000 --messages 10000000 --threads 1
参数说明:
–zookeeper 指定zookeeper 的链接信息
–topic 指定topic 的名称
–fetch-size 指定每次fetch 的数据的大小
–messages 总共要消费的消息个数
测试结果说明:
别人家电脑
start.time, **end.time,** data.consumed.in.MB, **MB.sec,** data.consumed.in.nMsg**, nMsg.sec**
2019-02-19 20:29:07:566, **2019-02-19 20:29:12:170,** 9.5368, **2.0714,** 100010, **21722.4153**
开始测试时间,测试结束数据,共消费数据9.5368MB,吞吐量**2.0714MB/s**,共消费
100010 条,平均每秒消费**21722.4153** 条。_
我的电脑
start.time, end.time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec
2020-03-10 01:24:46:109, 2020-03-10 01:24:49:266, 19.0735, 6.0416, 200000, 63351.2829
4.5.10 项目经验之Kafka 机器数量计算
Kafka 机器数量(经验公式)=2*(峰值生产速度*副本数/100)+1
先拿到峰值生产速度,再根据设定的副本数,就能预估出需要部署Kafka 的数量。
比如我们的峰值生产速度是50M/s。副本数为2。
Kafka 机器数量=2*(50*2/100)+ 1=3 台
如果峰值生产速度大于 50 M/s,则要考虑增加kafka机器数量
4.6 消费Kafka 数据Flume
集群规划
服务器hadoop102 | 服务器hadoop103 | 服务器hadoop104 | |
---|---|---|---|
Flume(消费Kafka) | Flume |
4.6.1 日志消费Flume 配置
1)Flume 配置分析
2)Flume 的具体配置如下:
(1)在hadoop104 的/opt/module/flume/conf 目录下创建kafka-flume-hdfs.conf 文件
[atguigu@hadoop104 conf]$ vim kafka-flume-hdfs.conf
在文件配置如下内容,注意单条配置没写完不要回车换行,建议在notepad++里查看
尽量把中文注解删掉后拷贝进配置文件,避免产生一些莫名其妙的错误。
## 组件
## 组件
a1.sources=r1 r2
a1.channels=c1 c2
a1.sinks=k1 k2
## source1
a1.sources.r1.type = org.apache.flume.source.kafka.KafkaSource
##5000条数据当一个批次
a1.sources.r1.batchSize = 5000
##2秒钟来一个批次与上面的协作
a1.sources.r1.batchDurationMillis = 2000
a1.sources.r1.kafka.bootstrap.servers =hadoop102:9092,hadoop103:9092,hadoop104:9092
a1.sources.r1.kafka.topics=topic_start
## source2
a1.sources.r2.type = org.apache.flume.source.kafka.KafkaSource
a1.sources.r2.batchSize = 5000
a1.sources.r2.batchDurationMillis = 2000
a1.sources.r2.kafka.bootstrap.servers =hadoop102:9092,hadoop103:9092,hadoop104:9092
a1.sources.r2.kafka.topics=topic_event
## channel1
a1.channels.c1.type = file
a1.channels.c1.checkpointDir =/opt/module/flume/checkpoint/behavior1
a1.channels.c1.dataDirs = /opt/module/flume/data/behavior1/
a1.channels.c1.maxFileSize = 2146435071
a1.channels.c1.capacity = 1000000
a1.channels.c1.keep-alive = 6
## channel2
a1.channels.c2.type = file
a1.channels.c2.checkpointDir =/opt/module/flume/checkpoint/behavior2
a1.channels.c2.dataDirs = /opt/module/flume/data/behavior2/
a1.channels.c2.maxFileSize = 2146435071
##file channels 能存1百万个events VS memory channel 最大100个events
a1.channels.c2.capacity = 1000000
##flume put事务的数据存活的时间,超过就把数据退了,然后再从kafka重新取
a1.channels.c2.keep-alive = 6
## sink1
a1.sinks.k1.type = hdfs
##每天一个文件夹
a1.sinks.k1.hdfs.path =/origin_data/gmall/log/topic_start/%Y-%m-%d
a1.sinks.k1.hdfs.filePrefix = logstart-
##sink2
a1.sinks.k2.type = hdfs
a1.sinks.k2.hdfs.path =/origin_data/gmall/log/topic_event/%Y-%m-%d
a1.sinks.k2.hdfs.filePrefix = logevent-
## 不要产生大量小文件,flume到hdfs里面,空值落盘文件的大小,一小时|128M先到先滚
a1.sinks.k1.hdfs.rollInterval = 3600
a1.sinks.k1.hdfs.rollSize = 134217728
a1.sinks.k1.hdfs.rollCount = 0
a1.sinks.k2.hdfs.rollInterval = 3600
a1.sinks.k2.hdfs.rollSize = 134217728
a1.sinks.k2.hdfs.rollCount = 0
## 控制输出文件是原生文件。
a1.sinks.k1.hdfs.fileType = CompressedStream
a1.sinks.k2.hdfs.fileType = CompressedStream
a1.sinks.k1.hdfs.codeC = lzop
a1.sinks.k2.hdfs.codeC = lzop
## 拼装
a1.sources.r1.channels = c1
a1.sinks.k1.channel= c1
a1.sources.r2.channels = c2
a1.sinks.k2.channel= c2
4.6.2 项目经验之Flume 组件
1)FileChannel 和MemoryChannel 区别
MemoryChannel 传输数据速度更快,但因为数据保存在JVM 的堆内存中,Agent 进程
挂掉会导致数据丢失,适用于对数据质量要求不高的需求。
FileChannel 传输速度相对于Memory 慢,但数据安全保障高,Agent 进程挂掉也可以从失败中恢复数据。
选型:
金融类公司、对钱要求非常准确的公司通常会选择FileChannel
传输的是普通日志信息(京东内部一天丢100 万-200 万条,这是非常正常的),通常选择MemoryChannel。
2)FileChannel 优化
通过配置dataDirs 指向多个路径,每个路径对应不同的硬盘,增大Flume 吞吐量。
官方说明如下:
Comma separated list of directories for storing log files. Using
multiple directories on separate disks can improve file channel
peformance
checkpointDir 和backupCheckpointDir 也尽量配置在不同硬盘对应的目录中,保证checkpoint 坏掉后,可以快速使用backupCheckpointDir 恢复数据
3)Sink:HDFS Sink
(1)HDFS 存入大量小文件,有什么影响?
**元数据层面:**每个小文件都有一份元数据,其中包括文件路径,文件名,所有者,所属
组,权限,创建时间等,这些信息都保存在Namenode 内存中。所以小文件过多,会占用Namenode 服务器大量内存,影响Namenode 性能和使用寿命(Namenode通常存9亿个)
**计算层面:**默认情况下MR 会对每个小文件启用一个Map 任务计算,非常影响计算性
能。同时也影响磁盘寻址时间。
(2)HDFS 小文件处理
官方默认的这三个参数配置写入HDFS 后会产生小文件,hdfs.rollInterval、hdfs.rollSize、hdfs.rollCount
基于以上hdfs.rollInterval=3600,hdfs.rollSize=134217728,hdfs.rollCount =0 几个参数综合作用,效果如下:
(1)文件在达到128M 时会滚动生成新文件
(2)文件创建超3600 秒时会滚动生成新文件
4.6.3 日志消费Flume 启动停止脚本
1)在/home/atguigu/bin 目录下创建脚本f2.sh
[atguigu@hadoop102 bin]$ 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/log.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 kill"
done
};;
esac
2)增加脚本执行权限
[atguigu@hadoop102 bin]$ chmod 777 f2.sh
3)f2 集群启动脚本
[atguigu@hadoop102 module]$ f2.sh start
4)f2 集群停止脚本
[atguigu@hadoop102 module]$ f2.sh stop
查看日志落盘情况
日志生成
[atguigu@hadoop102 bin]$ cd /tmp/logs/
[atguigu@hadoop102 logs]$ ll
-rw-rw-r--. 1 atguigu atguigu 1360231 3月 10 01:08 app-2020-03-10.log
[atguigu@hadoop102 logs]$ rm -rf app-2020-03-10.log
[atguigu@hadoop102 logs]$ lg.sh
在HDFS的目录下,应该能看到日志已经存储中。
/origin_data/gmall/log/topic_start/%Y-%m-%d
如果没有打通数据流,大概率是 Flume 的conf文件没有写对(当拷贝别处的代码过来是,一定要注意是否拷贝了多余的换行符!!!)
另如果lzo.tmp的tmp后缀迟迟不消失,可以修改a1.sinks.k1.hdfs.rollInterval = 3600
把参数较小,加速其落盘;
或者可以直接关闭flume进程,这样也会强制数据flush,完成数据落盘写入。。
4.6.4 项目经验之Flume 内存优化
1)问题描述:如果启动消费Flume 抛出如下异常
ERROR hdfs.HDFSEventSink: process failed
java.lang.OutOfMemoryError: GC overhead limit exceeded
2)解决方案步骤:
(1)在hadoop102 服务器的/opt/module/flume/conf/flume-env.sh 文件中增加如下配置
export JAVA_OPTS="-Xms100m -Xmx2000m -Dcom.sun.management.jmxremote"
(2)同步配置到hadoop103、hadoop104 服务器
[atguigu@hadoop102 conf]$ xsync flume-env.sh
3)Flume 内存参数设置及优化
JVM heap 一般设置为4G 或更高,部署在单独的服务器上(4 核8 线程16G 内存)
-Xmx 与-Xms 最好设置一致,减少内存抖动带来的性能影响,如果设置不一致容易导致频繁fullgc。
-Xms 表示JVM Heap(堆内存)最小尺寸,初始分配;
-Xmx 表示JVM Heap(堆内存)最大允许的尺寸,按需分配。
如果不设置一致,容易在初始化时,由于内存不够,频繁触发fullgc。建议设置到4 到 6G,如果还是.OutofMemoryError,建议增加机器数量。
4.7 采集通道启动/停止脚本
1)在/home/atguigu/bin 目录下创建脚本cluster.sh
[atguigu@hadoop102 bin]$ vim cluster.sh
在脚本中填写如下内容
#!/bin/bash
case $1 in
"start"){
echo " -------- 启动集群-------"
echo " -------- 启动hadoop 集群-------"
/opt/module/hadoop-2.7.2/sbin/start-dfs.sh
ssh hadoop103 "/opt/module/hadoop-2.7.2/sbin/start-yarn.sh"
#启动Zookeeper 集群
zk.sh start
sleep 10s;
#启动Flume 采集集群
f1.sh start
#启动Kafka 采集集群
kf.sh start
# Kafka重型框架,启动,关闭都比较慢,因此需要睡一下
sleep 15s;
#启动Flume 消费集群
f2.sh start
};;
"stop"){
echo " -------- 停止集群-------"
#停止Flume 消费集群
f2.sh stop
#停止Kafka 采集集群
kf.sh stop
sleep 6s;
#停止Flume 采集集群
f1.sh stop
#停止Zookeeper 集群
zk.sh stop
echo " -------- 停止hadoop 集群-------"
ssh hadoop103 "/opt/module/hadoop-2.7.2/sbin/stop-yarn.sh"
/opt/module/hadoop-2.7.2/sbin/stop-dfs.sh
};;
esac
2)增加脚本执行权限
[atguigu@hadoop102 bin]$ chmod 777 cluster.sh
3)cluster集群启动脚本
[atguigu@hadoop102 module]$ cluster.sh start
4)cluster集群停止脚本
[atguigu@hadoop102 module]$ cluster.sh stop
注意:
对于此项目,如果要用dt.sh 修改时间,并用lg.sh生成伪造日期的数据,
记住要先停止集群,完成修改时间操作后,再重新启动集群。
然后再生成数据lg.sh
mp的tmp后缀迟迟不消失,可以修改a1.sinks.k1.hdfs.rollInterval = 3600
把参数较小,加速其落盘;
或者可以直接关闭flume进程,这样也会强制数据flush,完成数据落盘写入。。