前言
本篇篇幅较长,有许多集群搭建干货,和枯燥乏味但是面试可能问到的理论知识。
思来想去不知道怎样才能鼓励自己加油学习,想想要面对的生活还是假吧意思打开学习视频吧。
目录
一、引入
hdfs是什么
hdfs的由来
hdfs架构体系
hdfs的优缺点
优点
缺点
二、HDFS_block简介和注意事项
Block拆分标准
三、HDFS_block安全与管理
Block数据安全
Block 的管理效率
四、HDFS_Hadoop3完全分布式集群的搭建
1.集群的网络和节点规划
网络规划
节点规划
2.Hadoop下载与安装
3.配置Hadoop集群
配置环境变量
配置分布式集群环境
分发Hadoop集群安装目录及文件
启动和停止Hadoop集群
验证
Web端访问
4.时间同步
安装NTP服务器
配置其他机器的时间同步
五、HDFS_文件数据类型和启动检查脚本编写
文件数据类型
启动检查脚本编写
六、HDFS_NameNode(NN)
功能
性能
七、HDFS_DataNode(DN)
功能
八、HDFS_SNN与测试
SecondaryNameNode
传统解决方案
SNN解决方案
SecondaryNameNode测试
SNN数据恢复
九、HDFS_安全模式和权限
安全模式
权限
十、HDFS_机架感知与副本存放策略
节点距离
机架感知
十一、HDFS_数据写流程
宏观过程
微观过程
十二、HDFS_数据读流程
十三、Hadoop1 的困境
十四、HDFS_高可用Hadoop-HA
设计思想
ANN
SNN
DataNode(DN)
QJM
ZKFC
Zookeeper
十五、HDFS_高可用脑裂
脑裂 brain-split
十六、HDFS_水平扩展联邦机制
为什么需要联邦
Federation 架构设计
HDFS Federation 的不足
十七、HDFS_HA搭建
初始化
角色分配
配置文件
启动
十八、HDFS_java访问hdfs集群
HDFS的Java访问接口
Java访问HDFS主要编程步骤
使用FileSystem API读取数据文件
编程实现
环境介绍
代码实操
十九、HDFS_IDEA插件访问hdfs集群
安装插件
使用
结语
参考资料
一、引入
hdfs是什么
hdfs即Hadoop分布式文件系统,是指被设计成适合运行在通用硬件(commodity hardware)上的分布式文件系统(Distributed File System)。
它和现有的分布式文件系统有很多共同点。但同时,它和其他的分布式文件系统的区别也是很明显的。HDFS是一个高度容错性的系统,适合部署在廉价的机器上。HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。HDFS放宽了一部分POSIX约束,来实现流式读取文件系统数据的目的。HDFS在最开始是作为Apache Nutch搜索引擎项目的基础架构而开发的。HDFS是Apache Hadoop Core项目的一部分。
hdfs的由来
随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到更到的操作系统管理的磁盘中,但是管理不方便和维护,迫切需要一种系统来管理多台机器上的文件,这就是分布式文件管理系统(DFS),它能够统一协调各个地理位置不同的机器拆分存储文件或合并聚合文件。而HDFS只是DFS中的一种。
文件切分思想
源文件直接存放在一个磁盘上效率肯定很低
- 读取效率低
- 如果文件太大会超出单机存储的范围
字节数组
- 文件在磁盘上真实存储文件的抽象概念
- 数组可以进行拆分合并,源文件不会收到影响
切分数据
- 对数组进行拆分
拼接数据
- 按照数组偏移量将字节数组拼接到一起
偏移量
- 当前数据在数组中的相对位置,可以理解为下标
- 数组都有对应的下标,可以快速的定位数据
数据存储原理
- 不管文件的的大小,所有的文件都是由字节数组构成
- 如果我们要切分文件 , 就是将一个字节数组分成多份
- 我们将切分后的数据拼接到一起,数据可以继续使用
- 我们需要根据数据的偏移量将他们重新拼接到一起
hdfs架构体系
HDFS采用了主从(Master/Slave)结构模型,一个HDFS集群是由一个NameNode和若干个DataNode组成的。其中NameNode作为主服务器,管理文件系统的命名空间和客户端对文件的访问操作;集群中的DataNode管理存储的数据。
hdfs的优缺点
优点
高容错性
- 数据自动保存多个副本。它通过增加副本的形式,提高容错性
- 某一个副本丢失以后,它可以自动恢复,这是由 HDFS 内部机制实现的,我们不必关心
适合批处理
- 它是通过移动计算而不是移动数据
- 它会把数据位置暴露给计算框架
适合大数据处理
- 数据规模:能够处理数据规模达到 GB、TB、甚至PB级别的数据
- 文件规模:能够处理百万规模以上的文件数量,数量相当之大
- 节点规模:能够处理10K节点的规模
流式数据访问
- 一次写入,多次读取,不能修改,只能追加
- 它能保证数据的一致性
可构建在廉价机器上
- 它通过多副本机制,提高可靠性
- 它提供了容错和恢复机制。比如某一个副本丢失,可以通过其它副本来恢复
缺点
不适合低延时数据访问;
- 比如毫秒级的来存储数据,这是不行的,它做不到
- 它适合高吞吐率的场景,就是在某一时间内写入大量的数据。但是它在低延时的情况 下是不行的,比如毫秒级以内读取数据,这样它是很难做到的
无法高效的对大量小文件进行存储
- 存储大量小文件的话,它会占用 NameNode大量的内存来存储文件、目录和块信息。这样是不可取的,因为NameNode的内存总是有限的
- 小文件存储的寻道时间会超过读取时间,它违反了HDFS的设计目标
并发写入、文件随机修改
- 一个文件只能有一个写,不允许多个线程同时写
- 仅支持数据 append(追加),不支持文件的随机修改
二、HDFS_block简介和注意事项
Block拆分标准
拆分的数据快需要等大
- 数据计算的时候简化问题的复杂度
- 进行分布式算法设计的时候,数据不统一,算法很难设计
- 数据拉取的时候时间相对一致
- 通过偏移量就知道这个块的位置
- 相同文件,分成的数据块大小应该相等
数据块 Block
- 数据被切分后的一个整体称之为块
- 在 H1 默认大小为 64M, 在 H2 及其以后默认大小为 128M
- 同一个文件中,每个数据块大小要一致除了最后一个节点外
- 不同文件中,块的大小可以不一致
- 文件大小不同可以设置不同的块的数量
- 真实情况下,会根据文件大小和集群节点的数量综合考虑块的大小
- 数据块的个数 =Ceil (文件大小 / 每个块的大小)
注意事项
- HDFS 中一旦文件被存储,数据不允许被修改
- 修改会影响偏移量
- 修改会导致数据倾斜
- 修改数据会导致蝴蝶效益
- 但是可以被追加,但是不推荐
- 追加设置需要手动打开
- 一般 HDFS 存储的都是历史数据。所以 将来 Hadoop 的 mr 都用来进行离线数据的处理
- 块的大小一旦文件上传之后就不允许被修改(128m -512M)
如果数据文件的切割点正好是一个单词的中间部分,切分数据如何保证数据的完整性?
三、HDFS_block安全与管理
Block数据安全
- 肯定要对存储数据做备份
- 备份的数据肯定不能存放在一个节点上
- 使用数据的时候可以就近获取数据
- 所以备份的数量要小于等于节点的数量
- 每个数据块会有 3 个副本,相同副本是不会存放在同一个节点上
- 副本的数量可以变更
- 可能近期的数据被分析的可能性跟大,副本数可以多设置几个
- 后期数据很少被分析,可以减少副本数
Block 的管理效率
需要专门给节点进行分工
-
存储 DataNode
- Slave,NameNode 下达命令,DataNode 执行实际的操作
- 存储实际的数据块
- 执行数据块的读/写操作
-
记录 NameNode
- master,一个管理者,不实际存储数据
- 管理 HDFS 的名称空间,维护着文件系统树以及整个树的所有文件和目录(fsimage+edits)
- 管理数据块(Block)映射信息
- 配置副本策略
- 处理客户端读写请求
-
日志 secondaryNameNode
- 辅助NameNode,分担其工作量
- 定期合并fsimage和edits,并推送给NameNode,紧急情况下,可辅助恢复NameNode
四、HDFS_Hadoop3完全分布式集群的搭建
注:前提是完成大数据学前准备--zookeeper详解与集群搭建(保姆级教程),这里重复操作将不再演示。
1.集群的网络和节点规划
网络规划
主机名 | IP地址 | 节点类型 |
node001 | 192.168.1.101 | master |
node002 | 192.168.1.102 | slave1 |
node003 | 192.168.1.103 | slave2 |
节点规划
主机/服务 | NameNode | SecondaryNameNode | DataNode | ResourceManager | NodeManager |
node001 | √ | √ | √ | √ | |
node002 | √ | √ | √ | ||
node003 | √ | √ |
2.Hadoop下载与安装
- 官网:官网下载
- 旧版本下载(官方的archive地址):旧版本下载,示例版本可在这里下载
- 清华大学开源软件镜像站下载(速度较快,只有新版本):清华大学开源软件镜像站
本次以hadoop-3.1.2.tar.gz为例搭建集群。
上传hadoop-3.1.2.tar.gz到node001;
执行解压命令:tar -zxvf hadoop-3.1.2.tar.gz -C /opt/hadoop-3.1.2
3.配置Hadoop集群
配置环境变量
终端输入:vim /etc/profile
末行加入:
export HADOOP_HOME=/opt/hadoop-3.1.2
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
配置分布式集群环境
对于Hadoop分布式集群模式的部署,常常需要配置的文件:
- 环境变量配置文件:hadoop-env.sh、yarn-env.sh、mapred-env.sh
- 全局核心配置文件:core-site.xml
- HDFS配置文件:hdfs-site.xml
- YARN配置文件:yarn-site.xml
- MapReduce配置文件:mapred-site.xml、slaves
hadoop-env.sh配置
终端输入:vim /opt/hadoop-3.1.2/etc/hadoop/hadoop-env.sh
末行加入:
export JAVA_HOME=/usr/java/jdk1.8.0_231-amd64
export HDFS_NAMENODE_USER=root
export HDFS_DATANODE_USER=root
export HDFS_SECONDARYNAMENODE_USER=root
export YARN_RESOURCEMANAGER_USER=root
export YARN_NODEMANAGER_USER=root
注意:yarn-env.sh
、mapred-env.sh
两个文件可以不用配置,如果要配置,可以在首次出现“export JAVA_HOME=……”
处配置为“export JAVA_HOME=/usr/java/jdk1.8.0_231-amd64”
core-site.xml配置
终端输入:vim /opt/hadoop-3.1.2/etc/hadoop/core-site.xml
加入:
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://node001:9000</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/var/bdp/hadoop/full</value>
</property>
</configuration>
参数说明:
fs.defaultFS:该参数是配置指定HDFS的通信地址。其值为hdfs://node001:9000,9000为端口号。
hadoop.tmp.dir:该参数配置的是Hadoop临时目录,即指定Hadoop运行时产生文件的存储路径,其值可以自行设置,不能设置为/tmp(/tmp是Linux的临时目录)。
注意:如果在普通用户配置临时目录/var/bdp/hadoop/full,需要手动创建及修改权限。
hdfs-site.xml配置
终端输入:vim /opt/hadoop-3.1.2/etc/hadoop/hdfs-site.xml
加入:
<configuration>
<property>
<name>dfs.namenode.http-address</name>
<value>node001:50070</value>
</property>
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>node002:50090</value>
</property>
<property>
<name>dfs.replication</name>
<value>2</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>/opt/hadoopTmp/dfs/name</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>/opt/hadoopTmp/dfs/data</value>
</property>
</configuration>
参数说明:
dfs.namenode.http-address:该参数是配置NameNode的http访问地址和端口号。因为在集群规划中指定node001设为NameNode的服务器,故设置为node001:50070。
dfs.namenode.secondary.http-address:该参数是配置SecondaryNameNode的http访问地址和端口号。在集群规划中指定node002设为SecondaryNameNode的服务器,故设置为node002:50090。
dfs.replication:该参数是配置HDFS副本数量。
dfs.namenode.name.dir:该参数是设置NameNode存放的路径。
dfs.datanode.data.dir:该参数是设置DataNode存放的路径。
yarn-site.xml配置(这里可以暂时不配置,将在yarn篇专门介绍)
终端输入:vim /opt/hadoop-3.1.2/etc/hadoop/yarn-site.xml
<configuration>
<property>
<name>yarn.resourcemanager.hostsname</name>
<value>node001</value>
</property>
<property>
<name>yarn.resourcemanager.webapp.address</name>
<value>node001:8088</value>
</property>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
<value>org.apache.hadoop.mapred.ShuffleHandler</value>
</property>
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<property>
<name>yarn.log-aggregation.retain-seconds</name>
<value>106800</value>
</property>
<property>
<name>yarn.nodemanager.remote-app-log-dir</name>
<value>/user/container/logs</value>
</property>
</configuration>
参数说明:
yarn.resourcemanager.hostsname:该参数是指定ResourceManager运行在那个节点上。
yarn.resourcemanager.webapp.address:该参数是指定ResourceManager服务器的web地址和端口。
yarn.nodemanager,aux-services:该参数是指定NodeManager启动时加载server的方式。
yarn.nodemanager.aux-services.mapreduce.shuffle.class:该参数是指定使用mapreduce_shuffle中的类。
yarn.log-aggregation-enable:该参数是配置是否启用日志聚集功能。
yarn.log-aggregation.retain-seconds:该参数是配置聚集的日志在HDFS上保存的最长时间。
yarn.nodemanager.remote-app-log-dir:该参数是指定日志聚合目录。
mapred-site.xml配置
终端输入:vim /opt/hadoop-3.1.2/etc/hadoop/mapred-site.xml
加入:
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.jobhistory.address</name>
<value>node003:10020</value>
</property>
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>node003:19888</value>
</property>
<property>
<name>mapreduce.jobhistory.intermediate-done-dir</name>
<value>${hadoop.tmp.dir}/mr-history/tmp</value>
</property>
<property>
<name>mapreduce.jobhistory.done-dir</name>
<value>${hadoop.tmp.dir}/mr-history/done</value>
</property>
</configuration>
参数说明:
mapreduce.framework.name:该参数是指定MapReduce框架运行在YARN上。
mapreduce.jobhistory.address:该参数是设置MapReduce的历史服务器安装的位置及端口号。
mapreduce.jobhistory.webapp.address:该参数是设置历史服务器的web页面地址和端口。
mapreduce.jobhistory.intermediate-done-dir:该参数是设置存放日志文件的临时目录。
mapreduce.jobhistory.done-dir:该参数是设置存放运行日志文件的最终目录。
slaves配置
终端输入:vim /opt/hadoop-3.1.2/etc/hadoop/slaves
添加所有节点主机名:
node001
node002
node003
分发Hadoop集群安装目录及文件
将处理好的hadoop-3.1.2分发到node002和node003(时间有些许长~)
分发到node002:scp -r /opt/hadoop-3.1.2 root@node002:/opt/
分发到node003:scp -r /opt/hadoop-3.1.2 root@node003:/opt/
启动和停止Hadoop集群
在启动hadoop集群前,需要先格式化NameNode,在Master主机下操作:
格式化命令:hdfs namenode -format 或者:hadoop namenode -format
格式化后即可
格式化后即可启动集群的节点,可以分别启动HDFS和YARN,也可以一起启动:
全部启动命令:
start-all.sh
如果启动报错:node001: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).
请参考《搭建hadoop报错》进行改错
启动和停止HDFS:
start-dfs.sh #启动HDFS
stop-dfs.sh #停止HDFS启动和停止YARN:
start-yarn.sh #启动YARN
stop-yarn.sh #停止YARN全部启动和停止:
start-all.sh #启动HDFS和YARN
stop-all.sh #停止HDFS和YARN启动和停止历史(日志)服务器:
mr-jobhistory-daemon.sh start historyserver #启动historyserver
mr-jobhistory-daemon.sh start historyserver #停止historyserver
验证
命令:jps
成功开启后,会看到下图所示的节点显示:
Master: NameNode、DataNode、ResourceManager、NodeManager
slave1: SecondaryNameNode、DataNode、NodeManager
slave2: DataNode、NodeManager
Web端访问
关闭防火墙:
service iptables stop
访问HDFS:50070192.168.1.101:50070 #访问HDFS,50070是端口
访问YARN:8088192.168.1.101:8088 #访问YARN,8088是端口
注意:根据自己配置的IP地址查看,必须关闭虚拟机里的防火墙,不然访问不了。
到这,HDFS完全分布式集群搭建完毕!
集群常用命令如下:
hdfs dfs -ls xxx
hdfs dfs -mkdir -p /xxx/xxx
hdfs dfs -cat xxx
hdfs dfs -put local cluster
hdfs dfs -get cluster local
hdfs dfs -cp /xxx/xxx /xxx/xxx
hdfs dfs -chmod -R 777 /xxx
hdfs dfs -chown -R zyh:zyh /xxx
4.时间同步
Hadoop集群对时间要求非常高,主节点与各从节点的时间都必须同步。NTP使用来使计算机时间同步的一种协议。配置时间同步服务器(NTP服务器)主要就是为了进行集群的时间同步。
这里主要以node001作为NTP服务器,从节点node002和node003每10分钟跟node001同步一次。
安装NTP服务器
1 安装配置NTP服务器
查看是否安装NTP服务,如果出现
ntp-x.x.x
和ntpdate-x.x.x
则不需要再安装rpm -qa | grep ntp
安装命令:
yum install -y ntp # 使用yum在线安装
2 修改配置文件
ntp.conf
vim /etc/ntp.conf
① 启用restrice,修改网段
打开restrice的注释(删除前面的#号),修改为自己的网段
② 注释掉四行server域名,再添加两行:
server 127.127.1.0 fudge 127.127.1.0 stratum 10
如图所示:
③ 修改配置文件ntpd
命令:
vim /etc/sysconfig/ntpd
在此文件内,添加一行
SYNC_HWCLOCK=yes
,将同步后的时间写入CMOS里。④ 启动NTP服务
service ntpd start # 启动NTP服务
chkconfig ntpd on # 开机自启动,永久启动两个命令可以连用:service ntpd start & chkconfig ntpd on
配置其他机器的时间同步
首先要保证从节点(其他机器)安装有NTP,然后开始配置从节点跟主节点同步时间。以下操作是在node002和node003机器上配置(剩下的这两台都要配置):
① 注释掉四行server域名配置,其后添加一行:
server node001
vim /etc/ntp.conf
② 修改配置文件ntpd,此操作和前面的NTP服务器中配置一样
vim /etc/sysconfig/ntpd
添加一行
SYNC_HWCLOCK=yes
③ 启动时间同步
启动NTP服务:
service ntpd start & chkconfig ntpd on
也可以执行命令:
ntpdate node001
完成同步node003节点与node002操作一致,不再演示。
最后通过:date '+%Y-%m-%d %H:%M:%S' 测试三个节点时间是否一致。
五、HDFS_文件数据类型和启动检查脚本编写
文件数据类型
文件有一个stat命令
数据信息-->描述文件的属性
文件有一个vim命令
分类
元数据
查看文件的数据信息
File 文件名
Size 文件大小(字节)
Blocks 文件使用的数据块总数
IO Block 数据块的大小
regular file:文件类型(常规文件)
Device 设备编号
Inode 文件所在的Inode
Links 硬链接次数
Access 权限
Uid 属主id/用户
Gid 属组id/组名
Access Time:简写为atime,表示文件的访问时间。当文件内容被访问时,更新这个时间
Modify Time:简写为mtime,表示文件内容的修改时间。当文件的数据内容被修改时,更新这个时间
Change Time:简写为ctime,表示文件的状态时间,当文件的状态被修改时,更新这个时间。例如文件的链接数,大小,权限,Blocks数
启动检查脚本编写
终端输入:vim start-bdp.sh
加入:
#!/bin/bash
echo "$(date)======启动node001的zookeeper========" >> /root/logs/start-bdp.log
zkServer.sh start >> /root/logs/start-bdp.log
echo "$(date)======启动node002的zookeeper========" >> /root/logs/start-bdp.log
ssh root@node002 "/opt/zookeeper-3.4.5/bin/zkServer.sh start" >> /root/logs/start-bdp.log
echo "$(date)======启动node003的zookeeper========" >> /root/logs/start-bdp.log
ssh root@node003 "/opt/zookeeper-3.4.5/bin/zkServer.sh start" >> /root/logs/start-bdp.log
echo " " >> /root/logs/start-bdp.log
echo " " >> /root/logs/start-bdp.log
echo " " >> /root/logs/start-bdp.log
echo " " >> /root/logs/start-bdp.log
echo -e "======启动hdfs完全分布式集群========" >> /root/logs/start-bdp.log
start-all.sh >> /root/logs/start-bdp.log
echo "$(date)-->启动hdfs完全分布式集群完成" >> /root/logs/start-bdp.log
echo " " >> /root/logs/start-bdp.log
echo " " >> /root/logs/start-bdp.log
echo " " >> /root/logs/start-bdp.log
echo " " >> /root/logs/start-bdp.log
echo "======当前各个节点状态为========" >> /root/logs/start-bdp.log
echo "$(date)======node001的zookeeper状态为========" >> /root/logs/start-bdp.log
zkServer.sh status >> /root/logs/start-bdp.log
echo "$(date)======node002的zookeeper的状态为========" >> /root/logs/start-bdp.log
ssh root@node002 "/opt/zookeeper-3.4.5/bin/zkServer.sh status" >> /root/logs/start-bdp.log
echo "$(date)======node003的zookeeper状态为========" >> /root/logs/start-bdp.log
ssh root@node003 "/opt/zookeeper-3.4.5/bin/zkServer.sh status" >> /root/logs/start-bdp.log
echo "$(date)======node001的HDFS状态========" >> /root/logs/start-bdp.log
jps >> /root/logs/start-bdp.log
echo "$(date)======node002的HDFS的状态为========" >> /root/logs/start-bdp.log
ssh root@node002 "jps" >> /root/logs/start-bdp.log
echo "$(date)======node003的HDFS状态为========" >> /root/logs/start-bdp.log
ssh root@node003 "jps" >> /root/logs/start-bdp.log
移动到根节点创建存放日志的目录:mkdir logs
执行脚本:sh start-bdp.sh
查看日志:cat logs/start-bdp.log
日志如下:
Thu Sep 15 11:49:48 CST 2022======启动node001的zookeeper========
Starting zookeeper ... STARTED
Thu Sep 15 11:49:49 CST 2022======启动node002的zookeeper========
Starting zookeeper ... STARTED
Thu Sep 15 11:49:50 CST 2022======启动node003的zookeeper========
Starting zookeeper ... STARTED
======启动hdfs完全分布式集群========
Starting namenodes on [node001]
Last login: Thu Sep 15 11:46:48 CST 2022 on pts/0
Starting datanodes
Last login: Thu Sep 15 11:49:53 CST 2022 on pts/0
Starting secondary namenodes [node002]
Last login: Thu Sep 15 11:49:56 CST 2022 on pts/0
Starting resourcemanager
Last login: Thu Sep 15 11:50:03 CST 2022 on pts/0
Starting nodemanagers
Last login: Thu Sep 15 11:50:10 CST 2022 on pts/0
Thu Sep 15 11:50:21 CST 2022-->启动hdfs完全分布式集群完成
======当前各个节点状态为========
Thu Sep 15 11:50:21 CST 2022======node001的zookeeper状态为========
Mode: follower
Thu Sep 15 11:50:21 CST 2022======node002的zookeeper的状态为========
Mode: leader
Thu Sep 15 11:50:22 CST 2022======node003的zookeeper状态为========
Mode: follower
Thu Sep 15 11:50:22 CST 2022======node001的HDFS状态========
16160 NameNode
15937 QuorumPeerMain
16854 NodeManager
17222 Jps
16711 ResourceManager
16317 DataNode
Thu Sep 15 11:50:23 CST 2022======node002的HDFS的状态为========
8933 QuorumPeerMain
9222 NodeManager
9020 DataNode
9133 SecondaryNameNode
9375 Jps
Thu Sep 15 11:50:23 CST 2022======node003的HDFS状态为========
8195 NodeManager
7993 QuorumPeerMain
8073 DataNode
8348 Jps
六、HDFS_NameNode(NN)
功能
- 接受客户端的读写服务
- NameNode 存放文件与 Block 的映射关系
- NameNode 会记录 Block 与 DataNode 的映射关系,但是不会持久化
- 保存文件的元数据信息
- 文件的归属
- 文件的权限
- 文件的大小时间
- Block 信息,但是 block 的位置信息不会持久化 , 需要每次开启集群的时候 DN 上报
- 收集 Block 的信息
- 系统启动时
- NN 关机的时候是不会存储任意的 Block 与 DN 的映射信息
- DN 启动的时候,会将自己节点上存储的 Block 信息汇报给 NN
- NN 接受请求之后重新生成映射关系
- Block--DN3
- 如果某个数据块的副本数小于设置数,那么 NN 会将这个副本拷贝到其他节点
- 集群运行中
- NN 与 DN 保持心跳机制 , 三秒钟发送一次
- 如果客户端需要读取或者上传数据的时候, NN 可以知道 DN 的健康情况
- 可以让客户端读取存活的 DN 节点
- 系统启动时
- 如果 DN 超过三秒没有心跳,就认为 DN 出现异常
- 不会让新的数据读写到 DataNode
- 客户访问的时候不提供异常结点的地址
- 如果 DN 超过 10 分钟 +30 秒没有心跳,那么 NN 会将当前 DN 存储的数据转存到其他节点
- 超时时长的计算公式为: timeout = 2 * heartbeat.recheck.interval + 10 *
- dfs.heartbeat.interval 。 而默认的 heartbeat.recheck.interval 大小为 5分钟, dfs.heartbeat.interval 默认为 3 秒。
性能
- NameNode 为了效率,将所有的操作都在内存中完成
- NameNode 不会和磁盘进行任何的数据交换
- 问题 :
- 数据的持久化
- 数据保存在内存中,掉电易失
- 小文件太多,作用内存大
七、HDFS_DataNode(DN)
功能
- 存放的是文件的数据信息和验证文件完整性的校验信息
- 数据会存放在硬盘上
- 1m=1 条元数据 1G=1 条元数据
- NameNode 非常排斥存储小文件,一般小文件在存储之前需要进行压缩
- 汇报
- 启动时
- 汇报之前先验证 Block 文件是否被损坏
- 向 NN 汇报当前 DN 上 block 的信息
- 运行中
- 向 NN 保持心跳机制
- 客户可以向 DN 读写数据,
- 启动时
- 当客户端读写数据的时候,首先去 NN 查询 file 与 block 与 dn 的映射
- 然后客户端直接与 dn 建立连接,然后读写数据
- 然后客户端直接与 dn 建立连接,然后读写数据
八、HDFS_SNN与测试
SecondaryNameNode
传统解决方案
- 日志机制
- 做任何操作之前先记录日志
- 当 NN 下次启动的时候,只需要重新按照以前的日志 “ 重做 ” 一遍即可
- 缺点
- edits 文件大小不可控,随着时间的发展,集群启动的时间会越来越长
- 有可能日志中存在大量的无效日志
- 优点
- 绝对不会丢失数据
- 拍摄快照
- 我们可以将内存中的数据写出到硬盘上
- 序列化
- 启动时还可以将硬盘上的数据写回到内存中
- 反序列化
- 缺点
- 关机时间过长
- 如果是异常关机,数据还在内存中,没法写入到硬盘
- 如果写出频率过高,导致内存使用效率低( stop the world ) JVM
- 优点
- 启动时间较短
SNN解决方案
解决思路(日志edit+快照fsimage)
- 让日志大小可控
- 快照定时保存
- NameNode文件目录
- 查看目录:cd /var/bdp/hadoop/full/dfs/name/current/
- 显示文件:ls
- edits_0000000000000000001-000000000000000005
- edits_inprogress_0000000000000000020
- 当前正在执行操作的日志信息
- 这些日志信息还没有被合并到镜像中
- fsimage_0000000000000000002
- fsimage_0000000000000000002.md5
- fsimage_0000000000000000005
- fsimage_0000000000000000005.md5
- 完整性校验规则
- seen_txid -->5
- VERSION
解决方案
- 当我们启动一个集群的时候,会产生四个文件
- edits_0000000000000000001
- fsimage_ edits_000000000000000000
- seen_txid
- VERSION
- 我们每次操作都会记录日志-->edits-inprogress- edits_00000001
- 随着时间的推移,日志文件会越来越大-当达到阈值的时候(64M或3600秒)
-
dfs.namenode.checkpoint.period #每隔多久做一次checkpoint,默认3600s dfs.namenode.checkpoint.txns #每隔多少次操作做一次checkpoint,默认100万次 dfs.namenode.checkpoint.check.period #每隔多久检查一次操作次数,默认60s
- 会生成新的日志文件
- edits_inprogress-000000001 -->edits_0000001
- 创建新的日志文件 edits_inprogress-0000000016
SecondaryNameNode测试
SNN数据恢复
1.强行杀死NameNode节点
kill -9 进程号
2.清空namenode下name中的fsimage和edits文件
rm -rf /var/bdp/hadoop/full/dfs/name/current/*
3.secondarynamenode下的name中的fsimage和edits复制到namenode对应文件夹中
scp -r root@node002:/var/bdp/hadoop/full/dfs/namesecondary/current/* /var/bdp/hadoop/full/dfs/name/current
4.启动namenode
hadoop-daemon.sh start namenode
5.访问namenode节点页面
九、HDFS_安全模式和权限
安全模式
- 集群启动时的一个状态
- 安全模式是 HDFS 的一种工作状态,处于安全模式的状态下,只向客户端提供文件的只读视图,不接受对命名空间的修改;同时 NameNode 节点也不会进行数据块的复制或者删除
- NameNode 启动时
- 首先将镜像文件( fsimage )载入内存,并执行编辑日志( edits )中的各项操作
- 一旦在内存中成功建立文件系统元数据的映像,则创建一个新的 fsimage 文件和一个空的编辑日志
- NameNode 开始监听 RPC 和 Http 请求。
- 此时 NameNode 处于安全模式,只接受客户端的读请求。
- 系统中的数据块的位置并不是由 NameNode 维护的,而是以块列表的形式存储在 DataNode 中。
- 安全模式下
- 安全模式下,各个 DataNode 会向 NameNode 发送自身的数据块列表
- 当 NameNode 有足够的数据块信息后,便在 30 秒后退出安全模式
- NameNode 发现数据节点过少会启动数据块复制过程
- 如果 NN 收集的 Block 信息没有达到最少副本数,就会将缺失的副本 , 从有的 DN 上拷贝到其他 DN
- dfs.replication.min=2
- 但是默认最低副本数为 1
- 在拷贝的过程中系统还是处于安全模式
- 安全模式相关命令
- hadoop dfsadmin -safemode leave 强制 NameNode 退出安全模式
- hadoop dfsadmin -safemode enter 进入安全模式
- hadoop dfsadmin -safemode get 查看安全模式状态
- hadoop dfsadmin -safemode wait 等待一直到安全模式结束
权限
在hadoop-env.sh中配置
- HDFS 对权限的控制
- 只能防止好人做错事
- 不能防止坏人做坏事
- 你告诉他你是谁,他就认为你是谁!
十、HDFS_机架感知与副本存放策略
节点距离
distance(/D1/R1/H1,/D1/R1/H1)=0 相同的 datanode
distance(/D1/R1/H1,/D1/R1/H3)=2 同一 rack 下的不同 datanode
distance(/D1/R1/H1,/D1/R2/H4)=4 同一 IDC 下的不同 datanode
distance(/D1/R1/H1,/D2/R3/H7)=6 不同 IDC 下的 datanode
机架感知
- 机架感知是为了保证副本在集群中的安全性
- 我们需要将节点放在不同的DN节点上,节点也需要一定的考量
- 可靠性,可用性,带宽消耗
- 第一个节点:
- 集群内部(优先考虑和客户端相同的节点作为第一个节点)
- 集群外部(选择资源丰富且不繁忙的节点作为第一个节点)
- 第二个节点:
- 第二个节点选择与第一个节点不同机架的其他节点
- 第三个节点:
- 与第二个相同机架相同的其他节点
- 第N个节点:
- 与前面节点不重复的其他节点
十一、HDFS_数据写流程
写数据就是将客户端上的数据上传到HDFS
宏观过程
1.客户端向HDFS发送写数据请求
创建目录:hdfs dfs -mkdir -p /user/dfstest
上传文件(随便上传一个大文件方便测试):hdfs dfs -put test.rar /user/dfstest
2. filesystem通过rpc调用namenode的create方法
- nn首先检查是否有足够的空间权限等条件创建这个文件,或者这个路径是否已经存在,权限
- 有:NN会针对这个文件创建一个空的Entry对象,并返回成功状态给DFS
- 没有:直接抛出对应的异常,给予客户端错误提示信息
3.如果DFS接收到成功的状态,会创建一个FSDataOutputStream的对象给客户端使用
4.客户端要向nn询问第一个Block存放的位置
NN通过机架感知策略 (node1 node 2 node8)
5.需要将客户端和DN节点创建连接
pipeline(管道)
客户端和node1 创建连接 socket
node1和 node2 创建连接 socket
node2 和 Node8 创建连接 socket
6.客户端按照文件块切分数据,但是按照packet发送数据
默认一个packet大小为64K,Block128M为2048个packet
7.客户端通过pipeline管道开始使用FDSOutputStream对象将数据输出
1. 客户端首先将一个 packet 发送给 node1, 同时给予 node1 一个 ack 状态
2. node1接受数据后会将数据继续传递给 node2, 同时给予 node2 一个 ack 状态
3. node2接受数据后会将数据继续传递给 node8, 同时给予 node8 一个 ack 状态
4. node8将这个 packet 接受完成后,会响应这个 ack 给 node2 为 true
5. node2会响应给 node1 , 同理 node1 响应给客户端
8. 客户端接收到成功的状态 , 就认为某个 packet 发送成功了,直到当前块所有的 packet 都发送完成
9. 如果客户端接收到最后一个 pakcet 的成功状态 , 说明当前 block 传输完成,管道就会被撤销
10. 客户端会将这个消息传递给 NN , NN 确认传输完成
1. NN会将 block 的信息记录到 Entry, 客户端会继续向 NN 询问第二个块的存储位置 , 依次类推
2. block1 (node1 node2 node8)
3. block2 (node1 node8 node9)
4. ....
5. blockn(node1 node7 node9)
11. 当所有的 block 传输完成后, NN 在 Entry 中存储所有的 File 与 Block 与 DN 的映射关系关闭
FsDataOutPutStream
微观过程
1.客户端首先从自己的硬盘中以流的形式将自己的数据读取到缓存中
2.然后将缓存(buffer)中的数据以chunk(512B)和checksum(4B)的方式放入到packet(64k)
- chunk:checksum=128:1
- checksum:在数据处理和数据通信领域中,用于校验目的的一组数据项的和
- Packet中的数据分为两类,一类是实际数据包,另一类是 header 包。
- 一个 Packet 数据包的组成结构
3.当packet满的时候添加到dataqueue
4.datastreamer开始从dataqueue队列上读取一个packet,通过FDSDataOPS发送到Poepleline
在取出的时候,也会将 packet 加入到 ackQueue, 典型的生产者消费者模式
5. 客户端发送一个 Packet 数据包以后开始接收 ack ,会有一个用来接收 ack 的 ResponseProcessor 进程,如果收到成功的 ack
如果某一个 packet 的 ack 为 true, 那么就从 ackqueue 删除掉这个 packet
如果某一个 packet 的 ack 为 false, 将 ackqueue 中所有的 packet 重新挂载到 发送队列 , 重新发送
十二、HDFS_数据读流程
- 首先客户端发送请求到 DFS ,申请读取某一个文件
- hdfs dfs -get /user/dfstest/test.rar /opt
- DFS 去 NN 查找这个文件的信息 ( 权限 , 文件是否存在 )
- 如果文件不存在,抛出指定的错误
- 如果文件存在,返回成功状态
- DFS 创建 FSDataInputStream 对象,客户端通过这个对象读取数据
- 客户端获取文件第一个 Block 信息 , 返回 DN1 DN2 DN8
- 客户端直接就近原则选择 DN1 对应的数据即可
- 依次类推读取其他块的信息,直到最后一个块 , 将 Block 合并成一个文件
- 关闭 FSDataInputStream
十三、Hadoop1 的困境
- 单点故障
- 每个群集只有一个 NameNode , NameNode 存在单点故障( SPOF )。
- 如果该计算机或进程不可用,则整个群集在整个 NameNode 重新启动或在另一台计算机上启动之前将不可用
- 如果发生意外事件(例如机器崩溃),则在操作员重新启动 NameNode 之前,群集将不可用。
- 计划内的维护事件,例如 NameNode 计算机上的软件或硬件升级,将导致群集停机时间的延长。
- 水平扩展
- 将来服务器启动的时候,启动速度慢
- namenode 随着业务的增多,内存占用也会越来越多
- 如果 namenode 内存占满,将无法继续提供服务
- 业务隔离性差
- 存储:有可能我们需要存储不同部门的数据
- 计算:有可能存在不同业务的计算流程
- 存储:有可能我们需要存储不同部门的数据
- 项目后期 namenode 的吞吐量将会是集群的瓶颈
- 客户端所有的请求都会先访问 NameNode
- Hadoop2.x
- NameNode 节点的高可用
- HA--high availability
- NameNode 业余的水平扩展
- Federation
- NameNode 节点的高可用
十四、HDFS_高可用Hadoop-HA
设计思想
- hadoop2.x 启用了主备节点切换模式(1主 1 备)
- 当主节点出现异常的时候,集群直接将备用(standby)节点切换成主节点
- 要求备用节点马上就要工作
- 主备节点内存几乎同步
- 有独立的线程对主备节点进行监控健康状态
- 需要有一定的选举机制,帮助我们确定主从关系
- 我们需要实时存储日志的中间件
ANN
- Active NameNode 的功能和原理与 NN 的功能是一样的
- 接受客户端请求,查询数据块 DN 信息
- 存储数据的元数据信息
- 数据文件: Block : DN 的映射关系
- 工作
- 启动时:接受 DN 的 block 汇报
- 运行时:和 DN 保持心跳 (3s,10m30s)
- 存储介质
- 完全基于内存
- 优点:数据处理效率高
- 缺点:数据的持久化 ( 日志 edits+ 快照 fsimage)
SNN
- Standby NameNode : NN 的备用节点
- 他和主节点做同样的工作,但是它不会发出任何指令
- 存储:数据的元数据信息
- 数据文件: Block : DN 的映射关系
- 它的内存数据和主节点内存数据几乎是一致的
- 工作:
- 启动时:
- 接受 DN 的 block 汇报
- 运行时:
- 和 DN 保持心跳 (3s,10m30s)
- 启动时:
- 存储介质
- 完全基于内存
- 优点:数据处理效率高
- 缺点:数据的持久化
- 合并日志文件和镜像
- 当搭建好集群的时候,格式化主备节点的时候, ANN 和 SNN 都会会默认创建
- fsimage_000000000000000
- 当我们操作 HDFS 的时候 ANN 会产生日志信息
- edits_inprogress_0000000000001
- 主节点会将日志文件中新增的数据同步到 JournalNode 集群上
- 所以只需要 snn 有操作的日志信息,就可以合并 fsImage 与 edits 信息 , 理论上是一直在合并数据
- fsimage --> 初始化创建
- edits--> 从 JournalNode 集群上定时同步
- 只要同步到 edits 文件,就开始于 fsimage 合并
- 当达到阈值的时候,直接拍摄快照即可
- SNN 将合并好的 Fsimage 发送给 ANN,ANN 验证无误后,存放到自己的目录中
- 当搭建好集群的时候,格式化主备节点的时候, ANN 和 SNN 都会会默认创建
DataNode(DN)
- 存储
- 文件的 Block 数据
- 介质
- 硬盘
- 启动时:
- 同时向两个 NN 汇报 Block 信息
- 运行中
- 同时和两个 NN 节点保持心跳机制
- 同时和两个 NN 节点保持心跳机制
QJM
- Quorum JournalNode Manager 共享存储系统, NameNode 通过共享存储系统实现日志数据同步。
- JournalNode 是一个独立的小集群,它的实现原理和 Zookeeper 的一致 ( Paxos)
- ANN 产生日志文件的时候,就会同时发送到 JournalNode 的集群中每个节点上
- JournalNode 不要求所有的 jn 节点都接收到日志,只要有半数以上的( n/2+1 )节点接受收到日志,那么本条日志就生效
- SNN 每间隔一段时间就去 QJM 上面取回最新的日志
- SNN 上的日志有可能不是最新的
- HA 集群的状态正确至关重要,一次只能有一个 NameNode 处于活动状态。
- JournalNode 只允许单个 NameNode 成为作者。在故障转移期间,将变为活动状态的 NameNode将承担写入 JournalNodes 的角色,这将有效地防止另一个 NameNode 继续处于活动状态,从而使新的 Active 节点可以安全地进行故障转移。
ZKFC
- Failover Controller( 故障转移控制器 )
- 对 NameNode 的主备切换进行总体控制,能及时检测到 NameNode 的健康状况
- 在主 NameNode 故障时借助 Zookeeper 实现自动的主备选举和切换
- 为了防止因为 NN 的 GC 失败导致心跳受影响, ZKFC 作为一个 deamon 进程从 NN 分离出来
- 启动时:
- 当集群启动时,主备节点的概念是很模糊的
- 当 ZKFC 只检查到一个节点是健康状态,直接将其设置为主节点
- 当 zkfc 检查到两个 NN 节点是的健康状态,发起投票机制
- 选出一个主节点,一个备用节点,并修改主备节点的状态
- 运行时:
- 由 ZKFailoverController 、 HealthMonitor 和 ActiveStandbyElector 这 3 个组件来协同实现主备切换
- ZKFailoverController 启动的时候会创建 HealthMonitor 和 ActiveStandbyElector 这两个主要的内部组件
- HealthMonitor 主要负责检测 NameNode 的健康状态
- ActiveStandbyElector 主要负责完成自动的主备选举,内部封装了 Zookeeper 的处理逻辑
- 由 ZKFailoverController 、 HealthMonitor 和 ActiveStandbyElector 这 3 个组件来协同实现主备切换
- 主备节点正常切换
- NameNode 在选举成功后, ActiveStandbyElector 会在 zk 上创建一个ActiveStandbyElectorLock 临时节点,而没有选举成功的备 NameNode 中的ActiveStandbyElector 会监控这个节点
- 如果 Active NameNode 对应的 HealthMonitor 检测到 NameNode 的状态异常时,ZKFailoverController 会主动删除当前在 Zookeeper 上建立的临时节点ActiveStandbyElectorLock
- 如果是 Active NameNode 的机器整个宕掉的话,那么跟 zookeeper 连接的客户端线程也挂了 , 会话结束 , 那么根据 Zookeepe 的临时节点特性, ActiveStandbyElectorLock 节点会自动被删除,从而也会自动进行一次主备切换
- 处于 Standby 状态的 NameNode 的 ActiveStandbyElector 注册的监听器就会收到这个节点的 NodeDeleted 事件,并创建 ActiveStandbyElectorLock 临时节点,本来处于 Standby 状态的 NameNode 就选举为 Active NameNode 并随后开始切换为 Active 状态。
Zookeeper
- 为主备切换控制器提供主备选举支持。
- 辅助投票
- 和 ZKFC 保持心跳机制,确定 ZKFC 的存活
十五、HDFS_高可用脑裂
脑裂 brain-split
- 定义
- 脑裂是 Hadoop2.X 版本后出现的全新问题,实际运行过程中很有可能出现两个 namenode 同时服务于整个集群的情况,这种情况称之为脑裂。
- 原因
- 脑裂通常发生在主从 namenode 切换时,由于 ActiveNameNode 的网络延迟、设备故障等问题,另一个 NameNode 会认为活跃的 NameNode 成为失效状态,此时StandbyNameNode 会转换成活跃状态,此时集群中将会出现两个活跃的 namenode 。因此,可能出现的因素有网络延迟、心跳故障、设备故障等。
- 脑裂场景
- NameNode 可能会出现这种情况, NameNode 在垃圾回收( GC )时,可能会在长时间内整个系统无响应
- zkfc 客户端也就无法向 zk 写入心跳信息,这样的话可能会导致临时节点掉线,备 NameNode会切换到 Active 状态
- 这种情况可能会导致整个集群会有同时有两个 Active NameNode
- 脑裂问题的解决方案是隔离( Fencing )
- 1. 第三方共享存储:任一时刻,只有一个 NN 可以写入;
- 2.DataNode :需要保证只有一个 NN 发出与管理数据副本有关的命令;
- 3.Client 需要保证同一时刻只有一个 NN 能够对 Client 的请求发出正确的响应。
- (a) 每个 NN 改变状态的时候,向 DN 发送自己的状态和一个本次选举对应的序列号。
- (b) DN 在运行过程中维护此序列号,当 failover 时,新的 NN 在返回 DN 心跳时会返回自己的 active 状态和一个更大的序列号。DN 接收到这个返回是认为该 NN 为新的 active 。
- (c) 如果这时原来的 active (比如 GC )恢复,返回给 DN 的心跳信息包含 active 状态和原来的序列号,这时 DN 就会拒绝这个 NN 的命令。
- 解决方案
- ActiveStandbyElector 为了实现 fencing ,当 NN 成为 ANN 之后创建 Zookeeper 临时节点ActiveStandbyElectorLock ,创建 ActiveBreadCrumb 的持久节点,这个节点里面保存了这个Active NameNode 的地址信息 (node-01)
- Active NameNode 的 ActiveStandbyElector 在正常的状态下关闭 Zookeeper Session 的时候,会一起删除这个持久节点
- 但如果 ActiveStandbyElector 在异常的状态下关闭,那么由于 /hadoopha/${dfs.nameservices}/ActiveBreadCrumb 是持久节点,会一直保留下来,后面当另一个NameNode 选主成功之后,会注意到上一个 Active NameNode 遗留下来的这个节点,从而会回调 ZKFailoverController 的方法对旧的 Active NameNode 进行 fencing 。
- 首先尝试调用这个旧 Active NameNode 的 HAServiceProtocol RPC 接口的transitionToStandby 方法,看能不能把它转换为 Standby 状态;
- 如果 transitionToStandby 方法调用失败,那么就执行 Hadoop 配置文件之中预定义的隔离措施。
- 1. sshfence :通过 SSH 登录到目标机器上,执行命令 fuser 将对应的进程杀死
- 2. shellfence :执行一个用户自定义的 shell 脚本来将对应的进程隔离
- 在成功地执行完成 fencing 之后,选主成功的 ActiveStandbyElector 才会回调ZKFailoverController 的 becomeActive 方法将对应的 NameNode 转换为 Active 状态,开始对外提供服务。
- 新的主创建临时节点 ActiveStandbyElectorLock ,创建持久化节点 ActiveBreadCrumb ,并将自己的主机地址 Node02 赋值给初始化节点
十六、HDFS_水平扩展联邦机制
为什么需要联邦
单 NameNode 的架构存在的问题:当集群中数据增长到一定规模后,NameNode 进程占用的内存可能会达到成百上千 GB(调大 NameNode 的 JVM 堆内存已无可能),此时,NameNode 成了集群的性能瓶颈。
为了提高 HDFS 的水平扩展能力,提出了Federation(联邦,联盟)机制。
Federation 是 NameNode 的 Federation,也就是会有多个 NameNode,而多个 NameNode 也就意味着有多个 namespac(命名空间),不同于 HA 模式下 Active 和 Standby 有各自的命名空间,联邦环境下的多 NameNode 共享同一个 namespace。
来看一下命名空间在 HDFS 架构中的位置:
现有的 HDFS 可以简单分为 数据管理 和 数据存储 2层:
所有关于存储数据的信息和管理,都是由 NameNode 负责;
而真实数据的存储则是在各个 DataNode 下完成。
这些被同一个 NameNode 所管理的数据都在同一个 namespace 下,一个 namespace 对应一个Block Pool(所有数据块的集合)。
Federation 架构设计
再强调一遍:HDFS Federation 是用来解决 NameNode 内存瓶颈问题的横向扩展方案。
Federation 意味着在集群中将会有多个 NameNode,这些 NameNode 相互独立且不需要协调,它们只需要管理自己所属的数据块即可。
分布式的 DataNode 作为公共的数据块存储设备,被所有的 NameNode 共用:每个 DataNode 都要向集群中所有的 NameNode 注册,且周期性地向所有 NameNode 发送心跳和块报告,并执行所有 NameNode 下发的命令。
Federation 架构中,DataNode上 会有多个 Block Pool 下,在 DataNode 的 datadir 目录下能看到以 BP-xx.xx.xx.xx
开头的目录。
从上图可以看出来:
多个 NameNode 共用一个集群里的所有存储资源,每个 NameNode 都可以单独对外提供服务;
每个 NameNode 都会定义一个 Block Pool,有单独的 id,每个 DataNode 都为所有 Block Pool 提供存储。
DataNode 会按照存储池 id 向其对应的 NameNode 汇报块信息,同时,DataNode 会向所有 NameNode 汇报本地存储可用资源情况。
HDFS Federation 的不足
HDFS Federation 并没有完全解决单点故障问题。
虽然集群中有多个 NameNode(namespace),但是从单个 NameNode(namespace)看,仍然存在单点故障:
如果某个 NameNode 服务发生故障,其管理的文件便不能被访问。
Federation 架构中每个NameNode 同样配有一个 Secondary NameNode,用于协助 NameNode 管理元数据信息(FSImage 和 EditLog)。
所以超大规模的集群,一般都会采用 HA + Federation 的部署方案,也就是每个联合的 NameNode 都是 HA 的,这样就解决了 NameNode 的单点故障问题 和 横向扩容问题。
十七、HDFS_HA搭建
注:这里搭建HA与上文搭建完全分布式集群不是串行而是并行,故其所有配置文件应重新编写,不能套用完全分布式集群的配置文件。即:从头再来
初始化
将hadoop-3.1.2.tar.gz安装包重新解压覆盖原先文件即可。注意:操作之前记得拍摄快照。
tar -zxvf hadoop-3.1.2.tar.gz -C /opt/
角色分配
节点\角色 | ZK | NN | DN | JN | ZKFC |
node001 | √ | √ | √ | √ | |
node002 | √ | √ | √ | √ | √ |
node003 |
√ | √ | √ |
配置文件
hadoop-env.sh
终端输入:vim /opt/hadoop-3.1.2/etc/hadoop/hadoop-env.sh
末行添加:
export JAVA_HOME=/usr/java/jdk1.8.0_231-amd64
export HDFS_NAMENODE_USER=root
export HDFS_DATANODE_USER=root
export HDFS_ZKFC_USER=root
export HDFS_JOURNALNODE_USER=root
export HDFS_SECONDARYNAMENODE_USER=root
export YARN_RESOURCEMANAGER_USER=root
export YARN_NODEMANAGER_USER=root
core-site.xml
终端输入:vim /opt/hadoop-3.1.2/etc/hadoop/core-site.xml
添加:
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://bdp</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/var/bdp/hadoop/ha</value>
</property>
<property>
<name>fs.trash.interval</name>
<value>1440</value>
</property>
<property>
<name>ha.zookeeper.quorum</name>
<value>node001:2181,node002:2181,node003:2181</value>
</property>
<property>
<name>hadoop.http.staticuser.user</name>
<value>root</value>
</property>
</configuration>
hdfs-site.xml
终端输入:vim /opt/hadoop-3.1.2/etc/hadoop/hdfs-site.xml
添加:
<configuration>
<property>
<name>dfs.nameservices</name>
<value>bdp</value>
</property>
<!-- 指定cluster的两个namenode的名称分别为nn1,nn2 -->
<property>
<name>dfs.ha.namenodes.bdp</name>
<value>nn1,nn2</value>
</property>
<!-- 配置nn1,nn2的rpc通信端口 -->
<property>
<name>dfs.namenode.rpc-address.bdp.nn1</name>
<value>node001:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.bdp.nn2</name>
<value>node002:8020</value>
</property>
<!-- 配置nn1,nn2的http通信端口 -->
<property>
<name>dfs.namenode.http-address.bdp.nn1</name>
<value>node001:9870</value>
</property>
<property>
<name>dfs.namenode.http-address.bdp.nn2</name>
<value>node002:9870</value>
</property>
<!-- 指定namenode元数据存储在journalnode中的路径 -->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://node001:8485;node002:8485;node003:8485/cluster</value>
</property>
<!-- 指定journalnode日志文件存储的路径 -->
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/var/bdp/hadoop/qjm</value>
</property>
<!-- 指定HDFS客户端连接active namenode的java类 -->
<property>
<name>dfs.client.failover.proxy.provider.bdp</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- 配置隔离机制为ssh -->
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
<value>shell(true)</value>
</property>
<!-- 指定秘钥的位置 -->
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/root/.ssh/id_rsa</value>
</property>
<!-- 指定秘钥的位置 -->
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/root/.ssh/id_rsa</value>
</property>
<!-- 开启自动故障转移 -->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
</configuration>
workers
终端输入:vim /opt/hadoop-3.1.2/etc/hadoop/workers
删除:localhost
添加:
node001
node002
node003
分发
将修改好的配置文件发送到其他节点(时间较长)
传到node002:scp -r /opt/hadoop-3.1.2/ node002:/opt/
传到node003:scp -r /opt/hadoop-3.1.2/ node003:/opt/
启动
启动zookeeper,三台都需要启动
三台机器终端输入:zkServer.sh start
启动JN(journalnode),三台都需启动
hadoop-daemon.sh start journalnode
格式化,在一台NN上执行(这里选择node001)
#格式化
hdfs namenode -format
#启动当前NN
hadoop-daemon.sh start namenode
执行同步,没有格式化的NN上执行,在另外一台namenode上面执行(这里选择node002)
hdfs namenode -bootstrapStandby
格式化ZK(在node001上执行)
hdfs zkfc -formatZK
启动hdfs集群(在node001上执行)
start-dfs.sh
到此HA集群搭建完成!
十八、HDFS_java访问hdfs集群
HDFS的Java访问接口
- 1)org.apache.hadoop.fs.FileSystem
- 是一个通用的文件系统API,提供了不同文件系统的统一访问方式。
- 2)org.apache.hadoop.fs.Path
- 是Hadoop文件系统中统一的文件或目录描述,类似于java.io.File对本地文件系统的文件或目录描述。
- 3)org.apache.hadoop.conf.Configuration
- 读取、解析配置文件(如core-site.xml/hdfs-default.xml/hdfs-site.xml等),或添加配置的工具类
- 4)org.apache.hadoop.fs.FSDataOutputStream
- 对Hadoop中数据输出流的统一封装
- 5)org.apache.hadoop.fs.FSDataInputStream
- 对Hadoop中数据输入流的统一封装
Java访问HDFS主要编程步骤
- 1)构建Configuration对象,读取并解析相关配置文件
- Configuration conf=new Configuration();
- 2)设置相关属性
- conf.set("fs.defaultFS","hdfs://IP:9000");
- 3)获取特定文件系统实例fs(以HDFS文件系统实例)
- FileSystem fs=FileSystem.get(new URI("hdfs://IP:9000"),conf,“hdfs");
- 4)通过文件系统实例fs进行文件操作(以删除文件实例)
- fs.delete(new Path("/user/liuhl/someWords.txt"));
使用FileSystem API读取数据文件
有两个静态工厂方法来获取FileSystem实例文件系统。
常用的是第二个和第四个
编程实现
环境介绍
使用的是IDEA+Maven来进行测试
Maven的pom.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.libing</groupId>
<artifactId>hdfa</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-auth</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
</project>
代码实操
上传文件到HA集群
package com.libing.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import java.net.URI;
/**
* @author liar
* @version 1.0
* @date 2022/9/17 21:26
*/
public class UploadTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//构建Configuration对象,读取并解析相关配置文件
Configuration conf = new Configuration();
/**
* 这些都是HA集群中配置文件的信息
*/
//设置相关属性
conf.set("fs.defaultFS", "hdfs://bdp");
conf.set("dfs.nameservices", "bdp");
conf.set("dfs.ha.namenodes.bdp", "nn1,nn2");
conf.set("dfs.namenode.rpc-address.bdp.nn1", "192.168.1.101:8020");
conf.set("dfs.namenode.rpc-address.bdp.nn2", "192.168.1.102:8020");
conf.set("dfs.client.failover.proxy.provider.bdp",
"org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider");
try {
//获取特定文件系统实例fs
FileSystem fs = FileSystem.get(new URI("hdfs://bdp"), conf,"root");
//创建/user/hdfs/test文件目录
boolean b=fs.mkdirs(new Path("/user/hdfs/test"));
if (b){
//将本地目录中的文件上传到集群中
//通过文件系统实例fs进行文件操作
fs.copyFromLocalFile(new Path("D:\\test\\test.txt"), new Path("/user/hdfs/test/test-1.txt"));
}else {
System.out.println("创建文件夹失败");
}
fs.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
下载集群中文件
package com.libing.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import java.net.URI;
/**
* @author liar
* @version 1.0
* @date 2022/9/17 21:36
*/
public class DownloadTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://bdp");
conf.set("dfs.nameservices", "bdp");
conf.set("dfs.ha.namenodes.bdp", "nn1,nn2");
conf.set("dfs.namenode.rpc-address.bdp.nn1", "192.168.1.101:8020");
conf.set("dfs.namenode.rpc-address.bdp.nn2", "192.168.1.102:8020");
conf.set("dfs.client.failover.proxy.provider.bdp",
"org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider");
//这之上的代码基本不变
try {
FileSystem fs = FileSystem.get(new URI("hdfs://bdp"), conf,"root");
//基本上 代码的变化在这里,主要为对集群中文件的增删改查
fs.copyToLocalFile(new Path("/user/hdfs/test/test-1.txt"),new Path("D:\\test\\test-local.txt"));
fs.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
这里浅谈一下这个.crc文件的由来:
CRC数据校验文件
- Hadoop系统为了保证数据的一致性,会对文件生成相应的校验文件(.crc文件),并在读写的时候进行校验,确保数据的准确性。
- hdfs为了保证数据的一致性,每次写文件时,针对数据的io.bytes.per.checksum字节,都会创建一个单独的校验和。默认值为512字节,因为crc-32校验是4字节,存储开销小于1%。而客户端读取数据时,默认会验证数据的crc校验和。除此之外,每个数据节点还会在后台线程运行一个数据块检测程序,定期检查存储在数据节点上的所有块。当块和对应的crc校验匹配不上,由于hdfs存储着块的副本,它可以复制正确的副本替换出错的副本。
简单来说他就是做校验工作的,相当于小区保安,你不是业主就不让你进去是一个道理。
十九、HDFS_IDEA插件访问hdfs集群
安装插件
使用
在右侧边栏打开big data tools
点击左上角+号,选择HDFS
结语
本文较长,如果你能坚持看完,也能发现作者的用心。
如果文章有什么出错或者你对文章有何建议或意见都可以通过我的邮箱联系到我:
719167291@qq.com
参考资料
hdfs - 百度百科
Hadoop-HDFS文章来源:https://www.toymoban.com/news/detail-417952.html
Hadoop集群完全分布式的搭建文章来源地址https://www.toymoban.com/news/detail-417952.html
到了这里,关于Hadoop-HDFS详解与HA,完全分布式集群搭建(细到令人发指的教程)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!