Hadoop学习笔记01

官方文档:

https://hadoop.apache.org/docs/r2.10.0/hadoop-project-dist/hadoop-common/ClusterSetup.html

1 编译

一些软件的官网提供了 source 和 binary 两个版本
source是源码,需要手动编译成可执行文件
binary是可执行版,是已经编译好的

以hadoop2.7来说,官网的可执行版不支持snappy和bzip2两种压缩算法,且没有提供带C程序访问的接口。所以这里对source版本重新编译。

编译所需准备:
版本:Hadoop 2.7.5
Linux环境
jdk1.7
maven3.x
findbugs
依赖包
protobuf
snappy

可以通过以下命令检查本地库

# 检查本地库
bin/hadoop checknative

2 安装

2.0 免密登录

通过ssh命令生成公私密钥对,公钥加密,私钥解密,实现免密登录。

2.1 解压

# 解压安装包
tar -zxvf hadoop-2.7.5.tar.gz -C /export/soft
# 创建软连接,方便使用
ln -s hadoop-2.7.5 hadoop

2.2 修改配置文件

配置文件位于hadoop/etc/hadoop中。
方便起见,可以通过editplus、notepad++或者其他Windows宿主机的软件,通过ftp的方式远程修改这些配置文件。
注意,utf-8编码。

2.2.1 core-site.xml

<configuration>

	<!-- 指定集群的文件系统类型:分布式文件系统 -->
	<property>
		<name>fs.default.name</name>
		<value>hdfs://node01:8020</value>
	</property>
 
	<!-- 指定临时文件存储目录 -->
	<property>
		<name>hadoop.tmp.dir</name>
		<value>/export/soft/hadoop-2.7.5/hadoopDatas/tempDatas</value>
	</property>
 
	<!-- 缓冲区大小,实际工作中根据服务器性能动态调整 -->
	<property>
		<name>io.file.buffer.size</name>
		<value>4096</value>
	</property>

	<!-- 开启hdfs的垃圾桶机制,删除掉的数据可以从垃圾桶中回收,单位分钟 -->
	<property>
		<name>fs.trash.interval</name>
		<value>10080</value>
	</property>
 
</configuration>

2.2.2 hdfs-site.xml

<configuration>

	<property>
		<name>dfs.namenode.secondary.http-address</name>
		<value>node01:50090</value>
	</property>

	<!-- 指定namenode的访问地址和端口 -->
	<property>
		<name>dfs.namenode.http-address</name>
		<value>node01:50070</value>
	</property>

	<!-- 指定namenode元数据的存放位置 -->
	<property>
		<name>dfs.namenode.name.dir</name>
		<value>file:///export/soft/hadoop-2.7.5/hadoopDatas/namenodeDatas,file:///export/soft/hadoop-2.7.5/hadoopDatas/namenodeDatas2</value>
	</property>

	<!-- 定义dataNode数据存储的节点位置,实际工作中,一般先确定磁盘的挂载目录,然后多个目录用,进行分割  -->
	<property>
		<name>dfs.datanode.data.dir</name>
		<value>file:///export/soft/hadoop-2.7.5/hadoopDatas/datanodeDatas,file:///export/soft/hadoop-2.7.5/hadoopDatas/datanodeDatas2</value>
	</property>
	
	<!-- 指定namenode日志文件的存放目录 -->
	<property>
		<name>dfs.namenode.edits.dir</name>
		<value>file:///export/soft/hadoop-2.7.5/hadoopDatas/nn/edits</value>
	</property>
	
	<property>
		<name>dfs.namenode.checkpoint.dir</name>
		<value>file:///export/soft/hadoop-2.7.5/hadoopDatas/snn/name</value>
	</property>

	<property>
		<name>dfs.namenode.checkpoint.edits.dir</name>
		<value>file:///export/soft/hadoop-2.7.5/hadoopDatas/dfs/snn/edits</value>
	</property>

	<!-- 文件切片的副本个数-->
	<property>
		<name>dfs.replication</name>
		<value>3</value>
	</property>

	<!-- 设置HDFS的文件权限-->
	<property>
		<name>dfs.permissions</name>
		<value>false</value>
	</property>

	<!-- 设置一个文件切片的大小:128M-->
	<property>
		<name>dfs.blocksize</name>
		<value>134217728</value>
	</property>

</configuration>

2.2.3 hadoop-env.sh

hadoop2.7.5版本是第25行

export JAVA_HOME=/export/soft/jdk

2.2.4 mapred-site.xml

这一文件目录中没有,有一个mapred-site.xml.template,将它复制改名即可

<configuration>

	<!-- 开启MapReduce小任务模式 -->
	<property>
		<name>mapreduce.job.ubertask.enable</name>
		<value>true</value>
	</property>
	
	<!-- 设置历史任务的主机和端口 -->
	<property>
		<name>mapreduce.jobhistory.address</name>
		<value>node01:10020</value>
	</property>

	<!-- 设置网页访问历史任务的主机和端口 -->
	<property>
		<name>mapreduce.jobhistory.webapp.address</name>
		<value>node01:19888</value>
	</property>

</configuration>

2.2.5 yarn-site.xml

<configuration>

	<!-- 配置yarn主节点的位置 -->
	<property>
		<name>yarn.resourcemanager.hostname</name>
		<value>node01</value>
	</property>

	<property>
		<name>yarn.nodemanager.aux-services</name>
		<value>mapreduce_shuffle</value>
	</property>
	
	<!-- 开启日志聚合功能 -->
	<property>
		<name>yarn.log-aggregation-enable</name>
		<value>true</value>
	</property>

	<!-- 设置聚合日志在hdfs上的保存时间 -->
	<property>
		<name>yarn.log-aggregation.retain-seconds</name>
		<value>604800</value>
	</property>

	<!-- 设置yarn集群的内存分配方案 -->
	<property>    
		<name>yarn.nodemanager.resource.memory-mb</name>    
		<value>20480</value>
	</property>

	<property>  
        <name>yarn.scheduler.minimum-allocation-mb</name>
        <value>2048</value>
	</property>

	<property>
		<name>yarn.nodemanager.vmem-pmem-ratio</name>
		<value>2.1</value>
	</property>

</configuration>

2.2.6 mapred-env.sh

hadoop2.7.5版本是第16行

export JAVA_HOME=/export/soft/jdk

2.2.7 slaves
这个文件是节点的名单
官方的英文说明是:

List all slave hostnames or IP addresses in your etc/hadoop/slaves
file, one per line. Helper scripts (described below) will use the
etc/hadoop/slaves file to run commands on many hosts at once. It is
not used for any of the Java-based Hadoop configuration. In order to
use this functionality, ssh trusts (via either passphraseless ssh or
some other means, such as Kerberos) must be established for the
accounts used to run Hadoop.

翻译一下:

列出etc/hadoop/slaves文件中的所有从属主机名或IP地址,每行一个。Helper脚本(如下所述)将使用etc/hadoop/slaves文件一次在许多主机上运行命令。它不用于任何基于Java的Hadoop配置。为了使用此功能,必须为用于运行Hadoop的帐户建立ssh信任(通过无密码ssh或其他一些方法,如Kerberos)。

由于已经在三台机器上设置了免密登录,且在/etc/hosts文件中设置了主机与ip地址之间的映射关系。
所以,slaves文件如下:

node01
node02
node03

2.2.8 创建需要的目录

需要创建以下目录:

mkdir -p /export/soft/hadoop-2.7.5/hadoopDatas/tempDatas
mkdir -p /export/soft/hadoop-2.7.5/hadoopDatas/namenodeDatas
mkdir -p /export/soft/hadoop-2.7.5/hadoopDatas/namenodeDatas2
mkdir -p /export/soft/hadoop-2.7.5/hadoopDatas/datanodeDatas
mkdir -p /export/soft/hadoop-2.7.5/hadoopDatas/datanodeDatas2
mkdir -p /export/soft/hadoop-2.7.5/hadoopDatas/nn/edits
mkdir -p /export/soft/hadoop-2.7.5/hadoopDatas/snn/name
mkdir -p /export/soft/hadoop-2.7.5/hadoopDatas/dfs/snn/edits

注:这些目录都是前面配置文件时声明的,这里需要手动创建

2.2.9 分发

在第一台机器上做好以上操作后,就可以把整个hadoop文件分发到另外两台机器上了。

cd  /export/soft/
scp -r hadoop-2.7.5 node02:$PWD
scp -r hadoop-2.7.5 node03:$PWD

2.3 配置环境变量

配置环境变量的操作需要在所有的节点上都执行这样的操作

1.路径是:

vim /etc/profile

2.加上前面配置的命令行显示绝对路径、jdk环境变量等,该文件配置了以下内容:

# Command prompt shows absolute path
export PS1='[\u@\h `pwd`]\$'

# jdk
export JAVA_HOME=/export/soft/jdk
export PATH=$PATH:$JAVA_HOME/bin
# mysql
export MySQL_HOME=/export/soft/mysql
export PATH=$PATH:$MySQL_HOME/bin
# hadoop
export HADOOP_HOME=/export/soft/hadoop
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin

3.通过source命令使修改后的环境变量生效:

source /etc/profile

4.接着,可以验证hadoop是否成功:

hadoop version
# 或
hdfs version

2.4 启动集群

首次启动 HDFS 时,必须对其进行格式化操作。 本质上是一些清理和准备工作,因为此时的 HDFS 在物理上还是不存在的。
1.进入路径:

cd /export/soft/hadoop-2.7.5/

2.格式化操作:

bin/hdfs namenode -format

3.分别启动hdfsyarn两个模块:

# 启动hdfs
sbin/start-dfs.sh
# 启动yarn
sbin/start-yarn.sh
# 启动历史记录
sbin/mr-jobhistory-daemon.sh start historyserver

3 HDFS

3.1 介绍

HDFS(Hadoop Distributed File System):分布式文件系统
适合的应用场景:

  1. 存储非常大的文件:这里非常大指的是几百M、G、或者TB级别,需要高吞吐量,对延时没有要求。
  2. 采用流式的数据访问方式: 即一次写入、多次读取,数据集经常从数据源生成或者拷贝一次,然后在其上做很多分析工作 。
  3. 运行于商业硬件上: Hadoop不需要特别贵的机器,可运行于普通廉价机器,可以处节约成本。
  4. 需要高容错性。
  5. 为数据存储提供所需的扩展能力。
    不适合的应用场景:
  6. 低延时的数据访问:对延时要求在毫秒级别的应用,不适合采用HDFS。HDFS是为高吞吐数据传输设计的,因此可能牺牲延时。
  7. 大量小文件:文件的元数据保存在NameNode的内存中, 整个文件系统的文件数量会受限于NameNode的内存大小。
  8. 多方读写,需要任意的文件修改:HDFS采用追加(append-only)的方式写入数据。不支持文件任意offset的修改。不支持多个写入器(writer)。
    hdfs的架构:
    HDFS是一个主/从(Mater/Slave)体系结构。
    HDFS由四部分组成,HDFS Client、NameNode、DataNode和Secondary NameNode。
  9. Client:客户端
  • 文件切分。文件上传 HDFS 的时候,Client 将文件切分成一个一个的Block,然后进行存储。
  • 与 NameNode 交互,获取文件的位置信息。
  • 与 DataNode 交互,读取或者写入数据。
  • Client 提供一些命令来管理和访问HDFS,比如启动或者关闭HDFS。
  1. NameNode:master,它是一个主管、管理者
  • 管理 HDFS 的名称空间。
  • 管理数据块(Block)映射信息。
  • 配置副本策略。
  • 处理客户端读写请求。
  1. DataNode:slave。NameNode 下达命令,DataNode 执行实际的操作
  • 存储实际的数据块。
  • 执行数据块的读/写操作。
  1. Secondary NameNode:并非 NameNode 的热备。当NameNode 挂掉的时候,它并不能马上替换 NameNode 并提供服务。
  • 辅助 NameNode,分担其工作量。
  • 定期合并 fsimage和fsedits,并推送给NameNode。
  • 在紧急情况下,可辅助恢复 NameNode。

文件副本机制
所有的文件都是以 block 块的方式存放在 HDFS 文件系统当中,作用如下:

  1. 一个文件有可能大于集群中任意一个磁盘,引入块机制,可以很好的解决这个问题
  2. 使用块作为文件存储的逻辑单位可以简化存储子系统
  3. 块非常适合用于数据备份进而提供数据容错能力
    文件的 block 块大小默认是 128M,可以通过hdfs-site.xml当中的配置文件进行指定:
<property>
    <name>dfs.block.size</name>
    <value>块大小 以字节为单位</value>
</property>

机架感知
HDFS分布式文件系统的内部有一个副本存放策略:以默认的副本数=3为例:

  1. 第一个副本块存本机
  2. 第二个副本块存跟本机同机架内的其他服务器节点
  3. 第三个副本块存不同机架的一个服务器节点上

3.2 命令行使用

  1. ls
格式:hdfs dfs -ls URI
作用:类似于Linux的ls命令,显示文件列表
hdfs dfs -ls /
  1. lsr
格式  : hdfs dfs -lsr URI
作用  : 在整个目录下递归执行ls, 与UNIX中的ls-R类似
hdfs dfs -lsr /
  1. mkdir
格式 :hdfs dfs [-p] -mkdir <paths>
作用 :<paths>中的URI作为参数,创建目录。使用-p参数可以递归创建目录
  1. put
格式 :hdfs dfs -put <localsrc> ... <dst>
作用 :将单个的源文件src或者多个源文件srcs从本地文件系统拷贝到目标文件系统中(<dst>对应的路径)。也可以从标准输入中读取输入,写入目标文件系统中
hdfs dfs -put /root/a.txt /dir1
  1. moveFromLocal
格式:hdfs dfs -moveFromLocal <localsrc> <dst>
作用: 和put命令类似,但是源文件localsrc拷贝之后自身被删除
  1. moveToLocal
    未实现
  2. get
格式:hdfs dfs -get [-ignorecrc] [-crc] <src> <localdst>
作用:将文件拷贝到本地文件系统。CRC校验失败的文件通过-ignorecrc选项拷贝。文件和CRC校验和可以通过-CRC选项拷贝。
hdfs dfs -mv /dir1/a.txt /dir2
  1. mv
格式:hdfs dfs -mv URI <dest>
作用:将hdfs上的文件从原路径移动到目标路径(移动之后文件删除),该命令不能跨文件系统
hdfs dfs -mv /dir1/a.txt /dir2
  1. rm
格式:hdfs dfs -rm [-r] [-skipTrash] URI [URI 。。。]
作用:删除参数指定的文件,参数可以有多个。此命令只删除文件和非空目录。
如果指定-skipTrash选项,那么在回收站可用的情况下,该选项将跳过回收站而直接删除文件;
否则,在回收站可用时,在HDFS Shell 中执行此命令,会将文件暂时放到回收站中。
hdfs dfs -rm -r /dir1
  1. cp
格式:hdfs dfs -cp URI [URI ...] <dest>
作用:将文件拷贝到目标路径中。如果<dest>为目录的话,可以将多个文件拷贝到该目录下。
-f
选项将覆盖目标,如果它已经存在。
-p
选项将保留文件属性(时间戳、所有权、许可、ACL、XAttr)。
hdfs dfs -cp /dir1/a.txt /dir2/b.txt
  1. cat
格式:hdfs dfs -cat URI [uri  ...]
作用:将参数所指示的文件内容输出到stdout
hdfs dfs -cat /install.log
  1. chmod
格式:hdfs dfs -chmod [-R] URI[URI  ...]
作用:改变文件权限。如果使用-R选项,则对整个目录有效递归执行。使用这一命令的用户必须是文件的所属用户,或者超级用户。
hdfs dfs -chmod -R 777 /install.log
  1. chown
格式:hdfs dfs -chmod [-R] URI[URI  ...]
作用:改变文件的所属用户和用户组。如果使用 -R选项,则对整个目录有效递归执行。用这一命令的用户必须是文件的所属用户,或者超级用户。
hdfs dfs -chown -R hadoop:hadoop /install.log
  1. appendToFile
格式:hdfs dfs -appendToFile <localsrc> ... <dst>
作用:追加一个或者多个文件到hdfs指定文件中。也可以从命令行读取输入。
hdfs dfs -appendToFile a.xml b.xml /big.xml

3.3 高级使用命令

3.3.1 文件限额配置

在多人共用HDFS的环境下,配置设置非常重要。特别是在Hadoop处理大量资料的环境,如果没有配额管理,很容易把所有的空间用完造成别人无法存取。Hdfs的配额设定是针对目录而不是针对账号,可以 让每个账号仅操作某一个目录,然后对目录设置配置。
hdfs文件的限额配置允许我们以文件个数,或者文件大小来限制我们在某个目录下上传的文件数量或者文件内容总量,以便达到我们类似百度网盘网盘等限制每个用户允许上传的最大的文件的量。
首先可以查看配额信息:

hdfs dfs -count -q -h /user/root/dir1
-q
表示显示配额信息
-h
以人类可读的格式显示大小
  1. 数量限额
# 首先,创建hdfs文件夹
hdfs dfs -mkdir -p /user/root/dir
# 给该文件夹下面设置最多上传两个文件,只能上传一个文件,因为它把文件夹本身算做了一个文件
hdfs dfsadmin -setQuota 2  dir
# 清除文件数量限制
hdfs dfsadmin -clrQuota /user/root/dir
  1. 空间大小限额
    设置的空间至少是block_size * 3大小。
# 限制空间大小384M
hdfs dfsadmin -setSpaceQuota 384m /user/root/dir
# 生成任意大小文件的命令
# 生成2M的文件
dd if=/dev/zero of=1.txt  bs=1M count=2
# 清除空间配额限制
hdfs dfsadmin -clrSpaceQuota /user/root/dir

3.3.2 安全模式

安全模式是hadoop的一种保护机制,用于保证集群中的数据块的安全性。当集群启动的时候,会首先进入安全模式。当系统处于安全模式时会检查数据块的完整性。
假设我们设置的副本数(即参数dfs.replication)是3,那么在datanode上就应该有3个副本存在,假设只存在2个副本,那么比例就是2/3=0.666。hdfs默认的副本率0.999。我们的副本率0.666明显小于0.999,因此系统会自动的复制副本到其他dataNode,使得副本率不小于0.999。如果系统中有5个副本,超过我们设定的3个副本,那么系统也会删除多于的2个副本。
在安全模式状态下,文件系统只接受读数据请求,而不接受删除、修改等变更请求。当整个系统达到安全标准时,HDFS自动离开安全模式。
安全模式操作命令:

# 查看安全模式状态
hdfs dfsadmin -safemode get
# 进入安全模式
hdfs dfsadmin -safemode enter
# 离开安全模式
hdfs dfsadmin -safemode leave

4 HDFS基准测试

实际生产环境当中,hadoop的环境搭建完成之后,第一件事情就是进行压力测试,测试我们的集群的读取和写入速度,测试我们的网络带宽是否足够等一些基准测试。

4.1 测试写入速度

# 向HDFS文件系统中写入数据,10个文件,每个文件10MB,文件存放到/benchmarks/TestDFSIO中
hadoop jar /export/soft/hadoop-2.7.5/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-2.7.5.jar TestDFSIO -write -nrFiles 10 -fileSize 10MB

查看写入速度结果

hdfs dfs -text  /benchmarks/TestDFSIO/io_write/part-00000
-text
获取源文件并以文本格式输出该文件

也可以通过vim的方式查看生成的log文件,该文件会生成在当前目录下

4.2 测试读取速度

# 在HDFS文件系统中读入10个文件,每个文件10M
hadoop jar /export/soft/hadoop-2.7.5/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-2.7.5.jar TestDFSIO -read -nrFiles 10 -fileSize 10MB

查看读取速度结果

hdfs dfs -text /benchmarks/TestDFSIO/io_read/part-00000

4.3 清除测试数据

hadoop jar /export/soft/hadoop-2.7.5/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-2.7.5.jar TestDFSIO -clean

5 HDFS文件写入过程

  1. Client 发起文件上传请求,通过 RPC 与 NameNode 建立通讯,NameNode 检查目标文件是否已存在,父目录是否存在,返回是否可以上传。
  2. Client 请求第一个 block 该传输到哪些 DataNode 服务器上。
  3. NameNode 根据配置文件中指定的备份数量及机架感知原理进行文件分配,返回可用的 DataNode 的地址如:A, B, C。
    • Hadoop 在设计时考虑到数据的安全与高效,数据文件默认在 HDFS 上存放三份,存储策略为本地一份,同机架内其它某一节点上一份,不同机架的某一节点上一份。
  4. Client 请求 3 台 DataNode 中的一台 A 上传数据(本质上是一个 RPC 调用,建立 pipeline ),A 收到请求会继续调用 B,然后 B 调用 C,将整个 pipeline 建立完成,后逐级返回 client。
  5. Client 开始往 A 上传第一个 block(先从磁盘读取数据放到一个本地内存缓存),以 packet 为单位(默认64K),A 收到一个 packet 就会传给 B,B 传给 C。A 每传一个 packet 会放入一个应答队列等待应答。
  6. 数据被分割成一个个 packet 数据包在 pipeline 上依次传输,在 pipeline 反方向上,逐个发送 ack(命令正确应答),最终由 pipeline 中第一个 DataNode 节点 A 将 pipelineack 发送给 Client。
  7. 当一个 block 传输完成之后,Client 再次请求 NameNode 上传第二个 block 到服务 1。

6 HDFS文件读取过程

  1. Client向NameNode发起RPC请求,来确定请求文件block所在的位置。
  2. NameNode会视情况返回文件的部分或者全部block列表,对于每个block,NameNode 都会返回含有该 block 副本的 DataNode 地址;这些返回的 DN 地址,会按照集群拓扑结构得出 DataNode 与客户端的距离,然后进行排序,排序两个规则:网络拓扑结构中距离 Client 近的排靠前;心跳机制中超时汇报的 DN 状态为 STALE,这样的排靠后。
  3. Client 选取排序靠前的 DataNode 来读取 block,如果客户端本身就是DataNode,那么将从本地直接获取数据(短路读取特性)。
  4. 底层上本质是建立 Socket Stream(FSDataInputStream),重复的调用父类 DataInputStream 的 read 方法,直到这个块上的数据读取完毕。
  5. 当读完列表的 block 后,若文件读取还没有结束,客户端会继续向NameNode 获取下一批的 block 列表。
  6. 读取完一个 block 都会进行 checksum 验证,如果读取 DataNode 时出现错误,客户端会通知 NameNode,然后再从下一个拥有该 block 副本的DataNode 继续读。
  7. read 方法是并行的读取 block 信息,不是一块一块的读取;NameNode 只是返回Client请求包含块的DataNode地址,并不是返回请求块的数据。
  8. 最终读取来所有的 block 会合并成一个完整的最终文件。

7 HDFS元数据辅助管理

在 Hadoop 的集群中,NameNode的所有元数据信息都保存在了 FsImage 与 Edits 文件中。
这两个文件就记录了所有的数据的元数据信息,元数据信息的保存目录配置在了hdfs-site.xml中。

<property>
	<name>dfs.namenode.name.dir</name>
	<value>file:///export/soft/hadoop-2.7.5/hadoopDatas/namenodeDatas,file:///export/soft/hadoop-2.7.5/hadoopDatas/namenodeDatas2</value>
</property>
<property>
	<name>dfs.namenode.edits.dir</name>
	<value>file:///export/soft/hadoop-2.7.5/hadoopDatas/nn/edits</value>
</property>

7.1 FsImage和Edits说明

  • fsimage
    • NameNode 中关于元数据的镜像,一般称为检查点,fsimage 存放了一份比较完整的元数据信息。
    • 因为 fsimage 是 NameNode 的完整的镜像,如果每次都加载到内存生成树状拓扑结构,这是非常耗内存和CPU, 所以一般开始时对 NameNode 的操作都放在 edits 中。
    • fsimage 内容包含了 NameNode 管理下的所有 DataNode 文件及文件 block 及 block 所在的 DataNode 的元数据信息。
    • 随着 edits 内容增大,就需要在一定时间点和 fsimage 合并。
  • edits
    • edits 存放了客户端最近一段时间的操作日志。
    • 客户端对 HDFS 进行写文件时会首先被记录在 edits 文件中。
    • edits 修改时元数据也会更新。

7.2 fsimage中的文件信息查看

首先要进入目录

cd /export/soft/hadoop2.7.5/hadoopDatas/namenodeDatas

再通过oiv进行查看

hdfs oiv -i fsimage_0000000000000000864 -p XML -o hello.xml

注,如果不知道具体命令,可以查看系统中的帮助:

hdfs oiv

7.3 edits中的文件信息查看

同样,首先要进入目录
再通过oev查看

hdfs oev -i edits_0000000000000000865-0000000000000000866 -p XML -o myedit.xml

7.4 SecondaryNameNode 如何辅助管理 fsimage 与 edits 文件

SecondaryNameNode 定期合并 fsimage 和 edits,把 edits 控制在一个范围内。
配置 SecondaryNameNode
{
SecondaryNameNode运行在另外一台机器上:

  • SecondaryNameNode 在 conf/masters 中指定
  • 在 masters 指定的机器上, 修改hdfs-site.xml
<property>
    <name>dfs.http.address</name>
    <value>host:50070</value>
</property>

}

  • 修改core-site.xml,这一步不做配置保持默认也可以
<!-- 多久记录一次 HDFS 镜像, 默认1小时 -->
<property>
    <name>fs.checkpoint.period</name>
    <value>3600</value>
</property>
<!-- 一次记录多大, 默认64M -->
<property>
    <name>fs.checkpoint.size</name>
    <value>67108864</value>
</property>

过程:

  1. SecondaryNameNode 通知 NameNode 切换 editlog(即切换为edits.new)
  2. SecondaryNameNode 从 NameNode 中获得 fsimage 和 editlog(通过http方式)
  3. SecondaryNameNode 将 fsimage 载入内存,然后开始合并 editlog,合并之后成为新的 fsimage
  4. SecondaryNameNode 将新的 fsimage 发回给 NameNode
  5. NameNode 用新的 fsimage 替换旧的 fsimage
    特点:
  6. 完成合并的是 SecondaryNameNode,会请求 NameNode 停止使用 edits,暂时将新写操作放入一个新的文件中edits.new
  7. SecondaryNameNode 从 NameNode 中通过 Http GET 获得 edits,因为要和 fsimage 合并,所以也是通过 Http Get 的方式把 fsimage 加载到内存,然后逐一执行具体对文件系统的操作,与 fsimage 合并,生成新的 fsimage,然后通过 Http POST 的方式把 fsimage 发送给 NameNode。NameNode 从 SecondaryNameNode 获得了 fsimage 后会把原有的 fsimage 替换为新的 fsimage,把 edits.new 变成 edits。同时会更新 fsimage。
  8. Hadoop 进入安全模式时需要管理员使用 dfsadmin 的 save namespace 来创建新的检查点。
  9. SecondaryNameNode 在合并 edits 和 fsimage 时需要消耗的内存和 NameNode 差不多,所以一般把 NameNode 和 SecondaryNameNode 放在不同的机器上。

8 HDFS的API操作

8.1 配置Windows下的Hadoop环境

以Hadoop2.7.5为例,主要是Hadoop的bin目录,除此之外,还要添加一些文件,winutils等等:
这些文件是在Windows下编译得到的
参考地址:https://github.com/cdarlint/winutils
将hadoop2.7.5文件夹拷贝到一个没有中文没有空格的路径
配置环境变量
把hadoop2.7.5文件夹中bin目录下的hadoop.dll文件放到系统盘:C:\Windows\System32目录

8.2 导入Maven依赖

<dependencies>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>2.7.5</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>2.7.5</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>2.7.5</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-mapreduce-client-core</artifactId>
        <version>2.7.5</version>
    </dependency>
    <!--日志-->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>2.0.0-alpha1</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
                <!--    <verbal>true</verbal>-->
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>2.4.3</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <minimizeJar>true</minimizeJar>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

注:
为了不显示日志的警告,可以在resources中加入log4j.properties文件:

# Configure logging for testing: optionally with log file

#log4j.rootLogger=debug,appender
log4j.rootLogger=info,appender  
#log4j.rootLogger=error,appender

#\u8F93\u51FA\u5230\u63A7\u5236\u53F0
log4j.appender.appender=org.apache.log4j.ConsoleAppender  
#\u6837\u5F0F\u4E3ATTCCLayout
log4j.appender.appender.layout=org.apache.log4j.TTCCLayout

8.3 使用url方式访问数据

@Test
public void urlHdfs() throws IOException {
    //1.注册url
    URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
    //2.获取hdfs文件输入流
    InputStream inputStream = new URL("hdfs://node01:8020/a.txt").openStream();
    //3.获取本地文件输出流
    FileOutputStream fileOutputStream = new FileOutputStream(new File("D:/java2/hello.txt"));
    //4.文件拷贝
    IOUtils.copy(inputStream, fileOutputStream);
    //5.关闭
    IOUtils.closeQuietly(inputStream);
    IOUtils.closeQuietly(fileOutputStream);
}

8.4 使用FileSystem方式访问数据

8.4.1 主要涉及的类

  • Configuration
    • 该类的对象封转了客户端或者服务器的配置,通过set方法设置文件系统类型
  • FileSystem
    • 该类的对象是一个文件系统对象,可以用该对象的一些方法来对文件进行操作, 通过FileSystem的静态方法get获得该对象
      • get 方法从 conf 中的一个参数 fs.defaultFS 的配置值判断具体是什么类型的文件系统
      • 如果我们的代码中没有指定 fs.defaultFS,并且工程 ClassPath 下也没有给定相应的配置,conf 中的默认值就来自于 Hadoop 的 Jar 包中的 core-default.xml
      • 默认值为 file:///,则获取的不是一个 DistributedFileSystem 的实例,而是一个本地文件系统的客户端对象

8.4.2 获取FileSystem的方式

@Test
public void getFileSystem1() throws IOException {
    //1.创建Configuration对象
    Configuration configuration = new Configuration();
    //2.设置文件系统类型
    configuration.set("fs.defaultFS", "hdfs://node01:8020");
    //3.获取指定的文件系统
    FileSystem fileSystem = FileSystem.get(configuration);
    //4.输出
    System.out.println(fileSystem);
}
@Test
public void getFileSystem2() throws IOException, URISyntaxException {
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
    System.out.println(fileSystem);
}
@Test
public void getFileSystem3() throws IOException {
    //1.创建Configuration对象
    Configuration configuration = new Configuration();
    //2.设置文件系统类型
    configuration.set("fs.defaultFS", "hdfs://node01:8020");
    //3.获取指定的文件系统
    FileSystem fileSystem = FileSystem.newInstance(configuration);
    //4.输出
    System.out.println(fileSystem);
}
@Test
public void getFileSystem4() throws IOException, URISyntaxException {
    FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://node01:8020"), new Configuration());
    System.out.println(fileSystem);
}

8.4.3 遍历HDFS所有文件

@Test
public void listFiles() throws URISyntaxException, IOException {
    //1.获取FileSystem实例
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
    //2.调用listFiles获取/目录下所有的文件信息
    RemoteIterator<LocatedFileStatus> iterator = fileSystem.listFiles(new Path("/"), true);
    //3.遍历迭代器
    while (iterator.hasNext()) {
        LocatedFileStatus fileStatus = iterator.next();
        //获取文件的绝对路径 hdfs://node01:8020/xxx
        System.out.println(fileStatus.getPath() + "----" + fileStatus.getPath().getName());
        //获取文件的block信息
        BlockLocation[] blockLocations = fileStatus.getBlockLocations();
        System.out.println("block数:" + blockLocations.length);
    }
}

8.4.4 HDFS上创建文件夹

可以创建文件夹、文件
创建文件时,如果目录不存在,则会创建目录

@Test
public void mkdirsTest() throws IOException, URISyntaxException {
    //1.
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
    //2.创建文件夹
    /*boolean bl = fileSystem.mkdirs(new Path("/aaa/bbb/ccc"));
    System.out.println(bl);*/
    fileSystem.create(new Path("/aaa/bbb/ccc/a.txt"));
    //3.关闭fs
    fileSystem.close();
}

8.4.5 HDFS下载文件

@Test
public void getFileToLocal() throws URISyntaxException, IOException {
    //1.
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
    //2.获取hfds输入流
    FSDataInputStream fsDataInputStream = fileSystem.open(new Path("/a.txt"));
    //3.获取本地路径的输入流
    FileOutputStream fileOutputStream = new FileOutputStream("D:/java2/a.txt");
    //4.文件的拷贝
    IOUtils.copy(fsDataInputStream, fileOutputStream);
    //5.关闭流
    IOUtils.closeQuietly(fsDataInputStream);
    IOUtils.closeQuietly(fileOutputStream);
}
@Test
public void getFileToLocal2() throws IOException, URISyntaxException {
    //1.
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
    //2.调用方法,实现文件下载
    fileSystem.copyToLocalFile(new Path("/a.txt"), new Path("D:/java2/a.txt"));
    //3.关闭fs
    fileSystem.close();
}

8.4.6 HDFS上传文件

@Test
public void putFileFromLocal() throws URISyntaxException, IOException {
    //1.
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
    //2.调用方法,实现文件上传
    fileSystem.copyFromLocalFile(new Path("D:/java2/123.txt"), new Path("/"));
    //3.关闭fs
    fileSystem.close();
}

8.4.7 HDFS访问权限控制

hdfs访问权限控制由hdfs-site.xmlchmod设置的权限决定
hdfs-site.xml:
如果“真”,则在HDFS中启用权限检查。如果“false”,则关闭权限检查,但所有其他行为不变。从一个参数值切换到另一个参数值不会更改模式、所有者或文件或目录组。

<property>
	<name>dfs.permissions.enabled</name>
	<value>true</value>
</property>

伪装用户
getnewInstance的三参数的方法,第三个参数是String user,这个user可以是任意的

@Test
public void getFileToLocal3() throws IOException, URISyntaxException, InterruptedException {
    //1.
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration(), "root");
    //2.调用方法,实现文件下载
    fileSystem.copyToLocalFile(new Path("/a.txt"), new Path("D:/java2/a.txt"));
    //3.关闭fs
    fileSystem.close();
}

8.4.8 小文件合并

# 将很多的 hdfs 文件合并成一个大文件下载到本地
hdfs dfs -getmerge /config/*.xml ./hello.xml

上传时将小文件合并为一个大文件:

@Test
public void mergeFiles() throws URISyntaxException, IOException, InterruptedException {
    //1.(伪造了一个root用户)
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration(), "root");
    //2.获取hdfs大文件的输出流
    FSDataOutputStream fsDataOutputStream = fileSystem.create(new Path("/bigText.txt"));
    //3.获取一个本地文件系统
    LocalFileSystem localFileSystem = FileSystem.getLocal(new Configuration());
    //4.获取本地文件夹下所有文件的详情
    FileStatus[] fileStatuses = localFileSystem.listStatus(new Path("D:/java2/d0"));
    //5.遍历每个文件,获取每个文件的输入流
    for (FileStatus fileStatus : fileStatuses) {
        FSDataInputStream fsDataInputStream = localFileSystem.open(fileStatus.getPath());
        //6.将小文件的数据复制到大文件
        IOUtils.copy(fsDataInputStream, fsDataOutputStream);
        IOUtils.closeQuietly(fsDataInputStream);
    }
    //7.关闭流
    IOUtils.closeQuietly(fsDataOutputStream);
    localFileSystem.close();
    fileSystem.close();
}

9 HDFS高可用机制

9.1 介绍

在典型的HA集群中,两台独立的机器被配置为NameNode。在工作集群中,NameNode机器中的一个处于Active状态,另一个处于Standby状态。Active NameNode负责群集中的所有客户端操作,而Standby充当从服务器。Standby机器保持足够的状态以提供快速故障切换(如果需要)。
Hadoop学习笔记01_第1张图片

9.2 组件

  1. ZKFailoverController
    是基于Zookeeper的故障转移控制器,它负责控制NameNode的主备切换,ZKFailoverController会监测NameNode的健康状态,当发现Active NameNode出现异常时会通过Zookeeper进行一次新的选举,完成Active和Standby状态的切换。
  2. HealthMonitor
    周期性调用NameNode的HAServiceProtocol RPC接口(monitorHealth 和 getServiceStatus),监控NameNode的健康状态并向ZKFailoverController反馈。
  3. ActiveStandbyElector
    接收ZKFC的选举请求,通过Zookeeper自动完成主备选举,选举完成后回调ZKFailoverController的主备切换方法对NameNode进行Active和Standby状态的切换。
  4. NameNode和DataNode
    NameNode包含了HDFS的元数据信息和数据块信息(blockmap),其中数据块信息通过DataNode主动向Active NameNode和Standby NameNode上报。
  5. 共享存储系统
    共享存储系统负责存储HDFS的元数据(EditsLog),Active NameNode(写入)和 Standby NameNode(读取)通过共享存储系统实现元数据同步,在主备切换过程中,新的Active NameNode必须确保元数据同步完成才能对外提供服务。

10 HDFS的联邦机制

  • 多个NN共用一个集群里的存储资源,每个NN都可以单独对外提供服务。
  • 每个NN都会定义一个存储池,有单独的id,每个DN都为所有存储池提供存储。
  • DN会按照存储池id向其对应的NN汇报块信息,同时,DN会向所有NN汇报本地存储可用资源情况。

你可能感兴趣的:(Hadoop)