本篇篇幅较长,有许多集群搭建干货,和枯燥乏味但是面试可能问到的理论知识。
思来想去不知道怎样才能鼓励自己加油学习,想想要面对的生活还是假吧意思打开学习视频吧。
目录
hdfs即Hadoop分布式文件系统,是指被设计成适合运行在通用硬件(commodity hardware)上的分布式文件系统(Distributed File System)。
它和现有的分布式文件系统有很多共同点。但同时,它和其他的分布式文件系统的区别也是很明显的。HDFS是一个高度容错性的系统,适合部署在廉价的机器上。HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。HDFS放宽了一部分POSIX约束,来实现流式读取文件系统数据的目的。HDFS在最开始是作为Apache Nutch搜索引擎项目的基础架构而开发的。HDFS是Apache Hadoop Core项目的一部分。
随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到更到的操作系统管理的磁盘中,但是管理不方便和维护,迫切需要一种系统来管理多台机器上的文件,这就是分布式文件管理系统(DFS),它能够统一协调各个地理位置不同的机器拆分存储文件或合并聚合文件。而HDFS只是DFS中的一种。
文件切分思想
源文件直接存放在一个磁盘上效率肯定很低
字节数组
切分数据
拼接数据
偏移量
数据存储原理
HDFS采用了主从(Master/Slave)结构模型,一个HDFS集群是由一个NameNode和若干个DataNode组成的。其中NameNode作为主服务器,管理文件系统的命名空间和客户端对文件的访问操作;集群中的DataNode管理存储的数据。

高容错性
适合批处理
适合大数据处理
流式数据访问
可构建在廉价机器上
不适合低延时数据访问;
无法高效的对大量小文件进行存储
并发写入、文件随机修改
拆分的数据快需要等大

数据块 Block
注意事项
如果数据文件的切割点正好是一个单词的中间部分,切分数据如何保证数据的完整性?


需要专门给节点进行分工

注:前提是完成大数据学前准备--zookeeper详解与集群搭建(保姆级教程),这里重复操作将不再演示。
| 主机名 | 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 | √ | √ |
本次以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
终端输入:vim /etc/profile
末行加入:
export HADOOP_HOME=/opt/hadoop-3.1.2
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin

对于Hadoop分布式集群模式的部署,常常需要配置的文件:
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-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集群前,需要先格式化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

关闭防火墙:
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
Hadoop集群对时间要求非常高,主节点与各从节点的时间都必须同步。NTP使用来使计算机时间同步的一种协议。配置时间同步服务器(NTP服务器)主要就是为了进行集群的时间同步。
这里主要以node001作为NTP服务器,从节点node002和node003每10分钟跟node001同步一次。
1 安装配置NTP服务器
查看是否安装NTP服务,如果出现
ntp-x.x.x和ntpdate-x.x.x则不需要再安装rpm -qa | grep ntp
安装命令:
yum install -y ntp # 使用yum在线安装
2 修改配置文件
ntp.confvim /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 node001vim /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' 测试三个节点时间是否一致。
文件有一个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


解决思路(日志edit+快照fsimage)
- 查看目录:cd /var/bdp/hadoop/full/dfs/name/current/
- 显示文件:ls

解决方案
dfs.namenode.checkpoint.period #每隔多久做一次checkpoint,默认3600s
dfs.namenode.checkpoint.txns #每隔多少次操作做一次checkpoint,默认100万次
dfs.namenode.checkpoint.check.period #每隔多久检查一次操作次数,默认60s 
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

在hadoop-env.sh中配置

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
写数据就是将客户端上的数据上传到HDFS
1.客户端向HDFS发送写数据请求
创建目录:hdfs dfs -mkdir -p /user/dfstest
上传文件(随便上传一个大文件方便测试):hdfs dfs -put test.rar /user/dfstest

2. filesystem通过rpc调用namenode的create方法
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)
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 重新挂载到 发送队列 , 重新发送






单 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(所有数据块的集合)。
再强调一遍: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 并没有完全解决单点故障问题。
虽然集群中有多个 NameNode(namespace),但是从单个 NameNode(namespace)看,仍然存在单点故障:
如果某个 NameNode 服务发生故障,其管理的文件便不能被访问。
Federation 架构中每个NameNode 同样配有一个 Secondary NameNode,用于协助 NameNode 管理元数据信息(FSImage 和 EditLog)。
所以超大规模的集群,一般都会采用 HA + Federation 的部署方案,也就是每个联合的 NameNode 都是 HA 的,这样就解决了 NameNode 的单点故障问题 和 横向扩容问题。
注:这里搭建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集群搭建完成!
有两个静态工厂方法来获取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数据校验文件
简单来说他就是做校验工作的,相当于小区保安,你不是业主就不让你进去是一个道理。

在右侧边栏打开big data tools

点击左上角+号,选择HDFS



本文较长,如果你能坚持看完,也能发现作者的用心。
如果文章有什么出错或者你对文章有何建议或意见都可以通过我的邮箱联系到我:
719167291@qq.com
我打算为ruby脚本创建一个安装程序,但我希望能够确保机器安装了RVM。有没有一种方法可以完全离线安装RVM并且不引人注目(通过不引人注目,就像创建一个可以做所有事情的脚本而不是要求用户向他们的bash_profile或bashrc添加一些东西)我不是要脚本本身,只是一个关于如何走这条路的快速指针(如果可能的话)。我们还研究了这个很有帮助的问题:RVM-isthereawayforsimpleofflineinstall?但有点误导,因为答案只向我们展示了如何离线在RVM中安装ruby。我们需要能够离线安装RVM本身,并查看脚本https://raw.github.com/wayn
我有一个涉及多台机器、消息队列和事务的问题。因此,例如用户点击网页,点击将消息发送到另一台机器,该机器将付款添加到用户的帐户。每秒可能有数千次点击。事务的所有方面都应该是容错的。我以前从未遇到过这样的事情,但一些阅读表明这是一个众所周知的问题。所以我的问题。我假设安全的方法是使用两阶段提交,但协议(protocol)是阻塞的,所以我不会获得所需的性能,我是否正确?我通常写Ruby,但似乎Redis之类的数据库和Rescue、RabbitMQ等消息队列系统对我的帮助不大——即使我实现某种两阶段提交,如果Redis崩溃,数据也会丢失,因为它本质上只是内存。所有这些让我开始关注erlang和
1.1.1 YARN的介绍 为克服Hadoop1.0中HDFS和MapReduce存在的各种问题⽽提出的,针对Hadoop1.0中的MapReduce在扩展性和多框架⽀持⽅⾯的不⾜,提出了全新的资源管理框架YARN. ApacheYARN(YetanotherResourceNegotiator的缩写)是Hadoop集群的资源管理系统,负责为计算程序提供服务器计算资源,相当于⼀个分布式的操作系统平台,⽽MapReduce等计算程序则相当于运⾏于操作系统之上的应⽤程序。 YARN被引⼊Hadoop2,最初是为了改善MapReduce的实现,但是因为具有⾜够的通⽤性,同样可以⽀持其他的分布式计算模
背景here.在上面的链接中,给出了以下示例:classauthor.id)endend除了这种语法对于像我这样的初学者来说很陌生——我一直认为类方法是用defself.my_class_method定义的——我在哪里可以找到关于类的文档RubyonRails中的方法?据我所知,类方法总是在类本身(MyClass.my_class_method)上调用,但如果Rails中的类方法是可链接的,似乎必须进行其他操作在这里!编辑:我想我通过对类方法的语法发表评论有点被骗了。我真的想问Rails如何使类方法可链接—我了解方法链接的工作原理,但不知道Rails如何允许您链接类方法而无需实际返
首先,关于我们系统的一些信息,它基本上是建筑行业的电子招标解决方案。所以:列表项我们的系统有多家公司每个公司都有多个用户每家公司可以创建多个拍卖然后其他公司可以为可用的拍卖提交他们的出价。一个出价包含数百或数千个单独的项目,我们只需要加密这些记录的“价格”部分。我们面临的问题是,我们的大客户不希望我们知道投标价格,至少在投标过程中是这样,这是完全可以理解的。现在,我们只是通过对称加密对价格进行加密,因此即使价格在数据库中有效加密,他们担心的是我们拥有解密价格的key。因此,我们正在研究某种形式的公钥加密系统。以下是我们对解决方案的初步想法:当一家公司注册时,我们会使用OpenSSL为其
Ruby是完全面向对象的语言。在ruby中,一切都是对象,因此属于某个类。例如5属于Objectclass1.9.3p194:001>5.class=>Fixnum1.9.3p194:002>5.class.superclass=>Integer1.9.3p194:003>5.class.superclass.superclass=>Numeric1.9.3p194:005>5.class.superclass.superclass.superclass=>Object1.9.3p194:006>5.class.superclass.superclass.superclass.su
一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su
TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是
我想生成一个包含数字、字母和特殊字符的给定(长度可能不同)长度的完全随机的“唯一”(我将确保使用我的模型)标识符例如:161551960578281|2.AQAIPhEcKsDLOVJZ.3600.1310065200.0-514191032|有人可以建议在RubyonRails中最有效的方法吗?编辑:重要:如果可能,请评论您提出的解决方案的效率,因为每次用户进入网站时都会使用它!谢谢 最佳答案 将其用于访问token与UUID不同。您不仅需要伪随机性,而且还需要加密安全PRNG.如果您真的不关心您使用的是什么字符(它们不会增加任何
在RSpec测试中,我创建了一个记录,其中包含多个内存值。foo.reload对对象的属性按预期工作,但内存的属性仍然存在。到目前为止,它通过完全重新创建对象来工作:foo=Foo.find(123)但在我的例子中,查找记录的逻辑实际上更复杂。什么是完全重新加载记录并删除所有内存值的好方法? 最佳答案 好的方法是您已有的方法:完全重新创建对象。您不能以任何简单的“Rails”方式“重新加载”对象的内存值,因为内存属性不是Rails或ActiveRecord的功能。两者都不知道您是如何内存方法的。