前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入浅出学大数据(三)分布式文件系统HDFS及HDFS的编程实践

深入浅出学大数据(三)分布式文件系统HDFS及HDFS的编程实践

作者头像
不温卜火
发布2021-09-29 15:09:36
9170
发布2021-09-29 15:09:36
举报
文章被收录于专栏:不温卜火

  大家好,我是不温卜火,昵称来源于成语—不温不火,本意是希望自己性情温和。作为一名互联网行业的小白,博主写博客一方面是为了记录自己的学习过程,另一方面是总结自己所犯的错误希望能够帮助到很多和自己一样处于起步阶段的萌新。但由于水平有限,博客中难免会有一些错误出现,有纰漏之处恳请各位大佬不吝赐教!博客主页:https://buwenbuhuo.blog.csdn.net/

目录


前言

此系列主要为我的学弟学妹们所创作,在某些方面可能偏基础。如果读者感觉较为简单,还望见谅!如果文中出现错误,欢迎指正~

本文主要介绍了HDFS简介及HDFS的编程实践,包括HDFS简介、HDFS相关概念、HDFS体系结构、HDFS存储原理、HDFS数据读写过程及HDFS编程实践。

一、分布式文件系统HDFS

1.1 什么是分布式文件系统

在说什么是分布式文件系统,我们需要先知道什么是文件系统,根据Google查询概念如下:

🔆文件系统是操作系统用于明确存储设备(常见的是磁盘,也有基于NAND Flash的固态硬盘)或分区上的文件的方法和数据结构;即在存储设备上组织文件的方法。 操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。 文件系统由三部分组成:文件系统的接口,对对象操纵和管理的软件集合,对象及属性。

👉这里仅举例子说明什么是文件系统有助于大家理解,我们知道一台计算机至少有一个操作系统,一个系统就需要对应一个文件系统,来管理组织存储设备的空间。文件系统就是一个软件机构,这个机构用来管理和存取文件,比如用户可以对系统上的文件进行增删查改等操作,如图所示:

🔆分布式文件系统(Distributed File System,DFS)是指文件系统管理的物理存储资源不一定直接连接在本地节点上,而是通过计算机网络与节点(可简单的理解为一台计算机)相连;或是若干不同的逻辑磁盘分区或卷标组合在一起而形成的完整的有层次的文件系统。

🔍1.计算机集群结构

分布式文件系统把文件分布存储到多个计算机节点上,成千上万的计算机节点构成计算机集群。

与之前使用多个处理器和专用高级硬件的并行化处理装置不同的是,目前的分布式文件系统所采用的计算机集群,都是由普通硬件构成的,这就大大降低了硬件上的开销。计算机集群的基本架构如下图所示:

🔍2. 分布式文件系统的结构

分布式文件系统在物理结构上是由计算机集群中的多个节点构成的,这些节点分为两类,一类叫“主节点”(Master Node)或者也被称为“名称结点”(NameNode),另一类叫“从节点”(Slave Node)或者也被称为“数据节点”(DataNode)

🔍3. 分布式文件系统与一般文件系统的对比

Column 1

分布式文件系统

一般文件系统

存储数据的方式

数据分散的存储在多台服务器上

集中存放所有数据,在一台服务器上器上

特点

分布式网络存储系统采用可扩展的系统结构,利用多台存储服务器分担存储负荷,利用位置服务器定位存储信息,它不但提高了系统的可靠性、可用性和存取效率,还易于扩展。

传统的网络存储系统采用集中的存储服务器存放所有数据,存储服务器成为系统性能的瓶颈,也是可靠性和安全性的焦点,不能满足大规模存储应用的需要。

1.2 HDFS产生背景及定义

🔍1. HDFS产生背景

随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理和维护,迫切需要一种系统来管理多台机器上的文件,这就是分布式文件管理系统。HDFS只是分布式文件管理系统中的一种

🔍2. HDFS定义

HDFS(Hadoop Distributed File System),它是一个文件系统,用于存储文件,通过目录树来定位文件;其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。

HDFS的使用场景:适合一次写入,多次读出的场景。一个文件经过创建、写入和关闭之后就不需要改变。

1.3 HDFS的设计

👉在《Hadoop权威指南》第三章3.1节,有HDFS设计的详细介绍。下面我们来看其描述。

以下为描述部分:

HDFS以流式数据访问模式来存储超大文件,运行于商用硬件集群上。让我们仔细看看下面的描述。

  • 超大文件 “超大文件”在这里指具有几百MB、几百GB甚至几百TB大小的文件。目前已经有存储PB级数据的Hadoop集群。
  • 流式数据访问 HDFS的构建思路是这样的:一次写入、多次读取是最高效的访问模式。数据集通常由数据源生成或从数据源复制而来,接着长时间在此数据集上进行各种分析。每次分析都将涉及该数据集的大部分数据甚至全部,因此读取整个数据集的时间延迟比读取第一条记录的时间延迟更重要。
  • 商用硬件 Hadoop并不需要运行在昂贵且高可靠的硬件上。它是设计运行在商用硬件(在各种零售店都能买到的普通硬件)的集群上的,因此至少对于庞大的集群来说,节点故障的几率还是非常高的。HDFS遇到上述故障时,被设计成能够继续运行且不让用户察觉到明显的中断。 同样,那些不适合在HDFS上运行的应用也值得研究。目前HDFS对某些应用领域并不适合,不过以后可能会有所改进。
  • 低时间延迟的数据访问 要求低时间延迟数据访问的应用,例如几十毫秒范围,不适合在HDFS上运行。记住,HDFS是为高数据吞吐量应用优化的,这可能会以提高时间延迟为代价。目前,对于低延迟的访问需求,HBase是更好的选择。
  • 大量的小文件 由于namenode将文件系统的元数据存储在内存中,因此该文件系统所能存储的文件总数受限于namenode的内存容量。根据经验,每个文件、目录和数据块的存储信息大约占150字节。因此,举例来说,如果有一百万个文件,且每个文件占一个数据块,那至少需要300MB的内存。尽管存储上百万个文件是可行的,但是存储数十亿个文件就超出了当前硬件的能力。
  • 多用户写入,任意修改文件 HDFS中的文件写入只支持单个写入者,而且写操作总是以“只添加”方式在文件末尾写数据。它不支持多个写入者的操作,也不支持在文件的任意位置进行修改。可能以后会支持这些操作,但他们相对比较低效。

1.4 HDFS的优缺点

根据上述描述中,了解HDFS特殊的设计,我们可以很容易的看出其优点和存在的缺点。

1.4.1 HDFS优点

🔍1.可构建在廉价机器上,通过多副本机制,提高可靠性。

🔍2.适合处理大数据

数据规模:能够处理数据规模达到GB、TB、甚至PB级别的数据;

文件规模:能够处理百万规模以上的文件数量,数量相当之大。

🔍3.高容错性 数据自动保存多个副本。它通过增加副本的形式,提高容错性。

某一个副本丢失以后,它可以自动恢复。

🔍4.可实现流数据读写

🔍5.简单的文件模型

🔍6.强大的跨平台兼容性

1.4.2 HDFS缺点

🔍1.不适合低延时数据访问,比如毫秒级的存储数据,是做不到的。

🔍2.无法高效的对大量小文件进行存储

存储大量小文件的话,它会占用NameNode大量的内存来存储文件目录和块信息。这样是不可取的,因为NameNode的内存总是有限的;

小文件存储的寻址时间会超过读取时间,它违反了HDFS的设计目标。

🔍3.不支持并发写入、文件随机修改。

一个文件只能有一个写,不允许多个线程同时写;

仅支持数据append(追加),不支持文件的随机修改。

1.5 HDFS的概念

1.5.1 数据块

每个磁盘都有默认的数据块大小,这是磁盘进行数据读/写的最小单位。构建于单个磁盘之上的文件系统通过磁盘块来管理该文件系统中的块,该文件系统块的大小可以是磁盘块的整数倍。文件系统块一般为几千字节,而磁盘块一般为512字节。这些信息(文件系统块大小)对于需要读/写文件的文件系统用户来说是透明的。尽管如此,系统仍然提供了一些工具(如f和fsck)来维护文件系统,由它们对文件系统中的块进行操作。

HDFS同样也有块(block)的概念,但是大得多,默认为128MB。与单一磁盘上的文件系统相似,HDFS上的文件也被划分为块大小的多个分块(chunk),作为独立的存储单元。但与面向单一磁盘的文件系统不同的是,HDFS中小于一个块大小的文件不会占据整个块的空间(例如,当一个1MB的文件存储在一个128MB的块中时,文件只使用1MB的磁盘空间,而不是128MB)。如果没有特殊指出,本篇文章中提到的“块”特指HDFS中的块。

🔆至于HDFS中的块为什么这么大,我们可以看《Hadoop权威指南》给出的解释。

✨对分布式文件系统中的块进行抽象会带来很多好处。其最明显的好处如下:

  • 支持大规模文件存储:文件以块为单位进行存储,一个大规模文件可以被分拆成若干个文件块,不同的文件块可以被分发到不同的节点上,因此,一个文件的大小不会受到单个节点的存储容量的限制,可以远远大于网络中任意节点的存储容量
  • 简化系统设计:首先,大大简化了存储管理,因为文件块大小是固定的,这样就可以很容易计算出一个节点可以存储多少文件块;其次,方便了元数据的管理,元数据不需要和文件块一起存储,可以由其他系统负责管理元数据
  • 适合数据备份:每个文件块都可以冗余存储到多个节点上,大大提高了系统的容错性和可用性。当一台机器数据丢失时,可以从其他候选地点复制到另一台可以正常运行的机器上,以保证复本的数量回到正常水平。

与磁盘文件系统相似,HDFS中fsck指令可以显示块信息。下面先来简单介绍下Hadoop中的fsck命令。

在HDFS中,提供了fsck命令,用于检查HDFS上文件和目录的健康状态、获取文件的block块信息和位置信息等。具体命令如下表所示:

项目

Value

-move

移动损坏的文件到/lost+found目录下

-delete

删除损坏的文件

-openforwrite

输出检测中的正在被写的文件

-list-corruptfileblocks

输出损坏的块及其所属的文件

-files

输出正在被检测的文件

-blocks

输出block的详细报告 (需要和-files参数一起使用)

-locations

输出block的位置信息 (需要和-files参数一起使用)

-racks

输出文件块位置所在的机架信息(需要和-files参数一起使用)

例如,执行以下命令将列出文件系统中input文件夹中core-site.xml这个文件由哪些块构成。

代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hdfs fsck /user/hadoop/input/core-site.xml  -files -block

1.5.2 名称节点和数据节点

HDFS的主要组件包括NaneNodeDataNode,其主要组件的功能如下图。

1. 名称节点(NameNode)

🔍1. 名称节点(NameNode)的数据结构

HDFS中,名称节点(NameNode)负责管理分布式文件系统的命名空间(Namespace),保存了两个核心的数据结构,即FsImageEditLog

  • FsImage用于维护文件系统树以及文件树中所有的文件和文件夹的元数据
  • 操作日志文件EditLog中记录了所有针对文件的创建、删除、重命名等操作

名称节点(NameNode)记录了每个文件中各个块所在的数据节点的位置信息。其结构图如下:

🔍2. FsImage

  • FsImage文件包含文件系统中所有目录和文件inode的序列化形式。每个inode是一个文件或目录的元数据的内部表示,并包含此类信息:文件的复制等级、修改和访问时间、访问权限、块大小以及组成文件的块。对于目录,则存储修改时间、权限和配额元数据
  • FsImage文件没有记录每个块存储在哪个数据节点。而是由名称节点把这些映射信息保留在内存中,当数据节点加入HDFS集群时,数据节点会把自己所包含的块列表告知给名称节点,此后会定期执行这种告知操作,以确保名称节点的块映射是最新的。

🔍3. FsImage和Edit解析

Fsimage和Edits概念

NameNode被格式化之后,将在/opt/module/hadoop/tmp/dfs/name/current目录中产生如下文件

代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop/tmp/dfs/name/current$ ll
总用量 3128
drwxrwxr-x 2 hadoop hadoop    4096 9月  26 20:20 ./
drwxrwxr-x 3 hadoop hadoop    4096 9月  26 20:33 ../
-rw-rw-r-- 1 hadoop hadoop     684 9月  23 23:38 edits_0000000000000000001-0000000000000000009
-rw-rw-r-- 1 hadoop hadoop 1048576 9月  24 00:02 edits_0000000000000000010-0000000000000000244
-rw-rw-r-- 1 hadoop hadoop      42 9月  25 16:19 edits_0000000000000000245-0000000000000000246
-rw-rw-r-- 1 hadoop hadoop 1048576 9月  25 16:19 edits_0000000000000000247-0000000000000000247
-rw-rw-r-- 1 hadoop hadoop      42 9月  26 17:20 edits_0000000000000000248-0000000000000000249
-rw-rw-r-- 1 hadoop hadoop      42 9月  26 18:20 edits_0000000000000000250-0000000000000000251
-rw-rw-r-- 1 hadoop hadoop      42 9月  26 19:20 edits_0000000000000000252-0000000000000000253
-rw-rw-r-- 1 hadoop hadoop      42 9月  26 20:20 edits_0000000000000000254-0000000000000000255
-rw-rw-r-- 1 hadoop hadoop 1048576 9月  26 20:20 edits_inprogress_0000000000000000256
-rw-rw-r-- 1 hadoop hadoop    2994 9月  26 19:20 fsimage_0000000000000000253
-rw-rw-r-- 1 hadoop hadoop      62 9月  26 19:20 fsimage_0000000000000000253.md5
-rw-rw-r-- 1 hadoop hadoop    2994 9月  26 20:20 fsimage_0000000000000000255
-rw-rw-r-- 1 hadoop hadoop      62 9月  26 20:20 fsimage_0000000000000000255.md5
-rw-rw-r-- 1 hadoop hadoop       4 9月  26 20:20 seen_txid
-rw-rw-r-- 1 hadoop hadoop     216 9月  26 16:20 VERSION
  • (1)Fsimage文件:HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目录和文件inode的序列化信息。
  • (2)Edits文件:存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到Edits文件中。
  • (3)seen txid文件保存的是一个数字,就是最后一个edits的数字
  • (4)每次NameNode启动的时候都会将Fsimage文件读入内存,加载Edits里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成NameNode启动的时候就将Fsimage和Edits文件进行了合开。
  • (5)VERSION文件是一个Java属性文件,其中包含正在运行的HDFS的版本信息

①属性layoutVersion是一个负整数,描述HDFS持久性数据结构(也称布局)的版本,但是该版本号与Hadoop发布包的版本号无关。只要布局变更,版本号便会递减(例如,版本号-64之后是-65),此时,HDFS也需要升级。否则,磁盘仍然使用旧版本的布局,新版本的namenode(或datanode)就无法正常工作。 ②属性namespaceID是文件系统命名空间的唯一标识符,是在namenode首次格式化时创建的。clusterID是将HDFS集群作为一个整体赋予的唯一标识符,对于联邦HDFS非常重要,这里一个集群由多个命名空间组成,且每个命名空间由一个namenode管理。blockpoolID是数据块池的唯一标识符,数据块池中包含了由一个namenode管理的命名空间中的所有文件。 ③cTime属性标记了namenode存储系统的创建时间。对于刚刚格式化的存储系统,这个属性值为0;但是在文件系统升级之后,该值会更新到新的时间戳。 ④storageType属性说明该存储目录包含的是namenode的数据结构。

🔆1. oiv查看Fsimage文件

(1)查看oivoev命令

代码语言:javascript
复制
hadoop@Master:current$ hdfs

......
oev                  apply the offline edits viewer to an edits file
oiv                  apply the offline fsimage viewer to an fsimage
oiv_legacy           apply the offline fsimage viewer to a legacy fsimage
......

(2)基本语法

代码语言:javascript
复制
hdfs oiv -p 文件类型 -i镜像文件 -o 转换后文件输出路径

(3)实操

代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop/tmp/dfs/name/current$ hdfs oiv -p XML -i fsimage_0000000000000000255 -o /opt/moudle/hadoop/fsimage.xml
2021-09-27 11:45:12,357 INFO offlineImageViewer.FSImageHandler: Loading 3 strings

将xml文件内容拷贝到Idea中创建的xml文件中,并格式化。部分显示结果如下。

代码语言:javascript
复制
        <inode>
            <id>16393</id>
            <type>DIRECTORY</type>
            <name>hadoop</name>
            <mtime>1632412810728</mtime>
            <permission>hadoop:supergroup:0755</permission>
            <nsquota>-1</nsquota>
            <dsquota>-1</dsquota>
        </inode>
        <inode>
            <id>16394</id>
            <type>DIRECTORY</type>
            <name>input</name>
            <mtime>1632412547963</mtime>
            <permission>hadoop:supergroup:0755</permission>
            <nsquota>-1</nsquota>
            <dsquota>-1</dsquota>
        </inode>
        <inode>
            <id>16395</id>
            <type>FILE</type>
            <name>capacity-scheduler.xml</name>
            <replication>3</replication>
            <mtime>1632412547687</mtime>
            <atime>1632412546882</atime>
            <preferredBlockSize>134217728</preferredBlockSize>
            <permission>hadoop:supergroup:0644</permission>
            <blocks>
                <block>
                    <id>1073741825</id>
                    <genstamp>1001</genstamp>
                    <numBytes>8260</numBytes>
                </block>
            </blocks>
            <storagePolicyId>0</storagePolicyId>
        </inode>

可以看出,Fsimage中没有记录块所对应DataNode

Fsimage中没有记录块所对应DataNode,为什么?

答:在fsimage中,并没有记录每一个block对应到哪几个datanodes的对应表信息,而只是存储了所有的关于namespace的相关信息。而真正每个block对应到datanodes列表的信息在hadoop中并没有进行持久化存储,而是在所有datanode启动时,每个datanode对本地磁盘进行扫描,将本datanode上保存的block信息汇报给namenode,namenode在接收到每个datanode的块信息汇报后,将接收到的块信息,以及其所在的datanode信息等保存在内存中。(HDFS就是通过这种块信息汇报的方式来完成 block -> datanodes list的对应表构建。Datanode向namenode汇报块信息的过程叫做blockReport,而namenode将block -> datanodes list的对应表信息保存在一个叫BlocksMap的数据结构中。)

🔆2. oev查看Edits文件

(1)基本语法

代码语言:javascript
复制
hdfs oev -p 文件类型 -i编辑日志 -o 转换后文件输出路径

(2)实操

代码语言:javascript
复制
hadoop@Master:current$ hdfs oev -p XML -i edits_0000000000000000254-0000000000000000255 -o /opt/moudle/hadoop/edits.xml

将xml文件内容拷贝到Idea中创建的xml文件中,并格式化。显示结果如下。

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

    <RECORD>
        <OPCODE>OP_ADD</OPCODE>
        <DATA>
            <TXID>50</TXID>
            <LENGTH>0</LENGTH>
            <INODEID>16401</INODEID>
            <PATH>/user/hadoop/input/kms-site.xml._COPYING_</PATH>
            <REPLICATION>3</REPLICATION>
            <MTIME>1632412547879</MTIME>
            <ATIME>1632412547879</ATIME>
            <BLOCKSIZE>134217728</BLOCKSIZE>
            <CLIENT_NAME>DFSClient_NONMAPREDUCE_-1428981819_1</CLIENT_NAME>
            <CLIENT_MACHINE>192.168.2.6</CLIENT_MACHINE>
            <OVERWRITE>true</OVERWRITE>
            <PERMISSION_STATUS>
                <USERNAME>hadoop</USERNAME>
                <GROUPNAME>supergroup</GROUPNAME>
                <MODE>420</MODE>
            </PERMISSION_STATUS>
            <ERASURE_CODING_POLICY_ID>0</ERASURE_CODING_POLICY_ID>
            <RPC_CLIENTID>18cbe0d2-6989-4815-b17d-56ccd10cdf63</RPC_CLIENTID>
            <RPC_CALLID>47</RPC_CALLID>
        </DATA>
    </RECORD>
    <RECORD>
        <OPCODE>OP_ALLOCATE_BLOCK_ID</OPCODE>
        <DATA>
            <TXID>51</TXID>
            <BLOCK_ID>1073741831</BLOCK_ID>
        </DATA>
    </RECORD>
    <RECORD>
        <OPCODE>OP_SET_GENSTAMP_V2</OPCODE>
        <DATA>
            <TXID>52</TXID>
            <GENSTAMPV2>1007</GENSTAMPV2>
        </DATA>
    </RECORD>
    <RECORD>
        <OPCODE>OP_ADD_BLOCK</OPCODE>
        <DATA>
            <TXID>53</TXID>
            <PATH>/user/hadoop/input/kms-site.xml._COPYING_</PATH>
            <BLOCK>
                <BLOCK_ID>1073741831</BLOCK_ID>
                <NUM_BYTES>0</NUM_BYTES>
                <GENSTAMP>1007</GENSTAMP>
            </BLOCK>
            <RPC_CLIENTID/>
            <RPC_CALLID>-2</RPC_CALLID>
        </DATA>
    </RECORD>
    
    <RECORD>
        <OPCODE>OP_CLOSE</OPCODE>
        <DATA>
            <TXID>54</TXID>
            <LENGTH>0</LENGTH>
            <INODEID>0</INODEID>
            <PATH>/user/hadoop/input/kms-site.xml._COPYING_</PATH>
            <REPLICATION>3</REPLICATION>
            <MTIME>1632412547901</MTIME>
            <ATIME>1632412547879</ATIME>
            <BLOCKSIZE>134217728</BLOCKSIZE>
            <CLIENT_NAME/>
            <CLIENT_MACHINE/>
            <OVERWRITE>false</OVERWRITE>
            <BLOCK>
                <BLOCK_ID>1073741831</BLOCK_ID>
                <NUM_BYTES>682</NUM_BYTES>
                <GENSTAMP>1007</GENSTAMP>
            </BLOCK>
            <PERMISSION_STATUS>
                <USERNAME>hadoop</USERNAME>
                <GROUPNAME>supergroup</GROUPNAME>
                <MODE>420</MODE>
            </PERMISSION_STATUS>
        </DATA>
    </RECORD>

思考:NameNode如何确定下次开机启动的时候合并哪些Edits?

答: NameNode启动的时候合并的是上次停机前正在写入的Edits,即edits_inprogress_xxx根据seen_txid里面记录最新的Fsimage(镜像文件)的值去合并Edits(编辑日志)。

🔍4. 名称节点的启动

  • 在名称节点启动的时候,它会将FsImage文件中的内容加载到内存中,之后再执行EditLog文件中的各项操作,使得内存中的元数据和实际的同步,存在内存中的元数据支持客户端的读操作。
  • 一旦在内存中成功建立文件系统元数据的映射,则创建一个新的FsImage文件和一个空的EditLog文件
  • 名称节点起来之后,HDFS中的更新操作会重新写到EditLog文件中,因为FsImage文件一般都很大(GB级别的很常见),如果所有的更新操作都往FsImage文件中添加,这样会导致系统运行的十分缓慢,但是,如果往EditLog文件里面写就不会这样,因为EditLog要小很多。每次执行写操作之后,且在向客户端发送成功代码之前,edits文件都需要同步更新

🔍5. 什么是第二名称节点,什么时候使用第二名称节点

第二名称节点是HDFS架构中的一个组成部分,它是用来保存名称节点中对HDFS 元数据信息的备份,并减少名称节点重启的时间。SecondaryNameNode一般是单独运行在一台机器上。

NameNode运行期间会出现EditLog不断变大的问题,这个时候就需要使用SecondaryNameNode

  • 在名称节点运行期间,HDFS的所有更新操作都是直接写到EditLog中,久而久之, EditLog文件将会变得很大
  • 虽然这对名称节点运行时候是没有什么明显影响的,但是,当名称节点重启的时候,名称节点需要先将FsImage里面的所有内容映像到内存中,然后再一条一条地执行EditLog中的记录,当EditLog文件非常大的时候,会导致名称节点启动操作非常慢,而在这段时间内HDFS系统处于安全模式,一直无法对外提供写操作,影响了用户的使用。

🔍6. NameNode工作机制

🔆第一阶段:NameNode启动

  • (1)第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
  • (2)客户端对元数据进行增删改的请求。
  • (3)NameNode记录操作日志,更新滚动日志。
  • (4)NameNode在内存中对元数据进行增删改。

🔆第二阶段:Secondary NameNode工作

  • (1)Secondary NameNode询问NameNode是否需要CheckPoint。直接带回NameNode是否检查结果。
  • (2)Secondary NameNode请求执行CheckPoint。
  • (3)NameNode滚动正在写的Edits日志。
  • (4)将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode。
  • (5)Secondary NameNode加载编辑日志和镜像文件到内存,并合并。
  • (6)生成新的镜像文件fsimage.chkpoint。
  • (7)拷贝fsimage.chkpoint到NameNode。
  • (8)NameNode将fsimage.chkpoint重新命名成fsimage。

🔍7.CheckPoint时间设置

🔆通常情况下,SecondaryNameNode每隔一小时执行一次。

代码语言:javascript
复制
# hdfs-default.xml
<property>
  <name>dfs.namenode.checkpoint.period</name>
  <value>3600s</value>
</property>

🔆一分钟检查一次操作次数,当操作次数达到1百万时,SecondaryNameNode执行一次

代码语言:javascript
复制
<property>
  <name>dfs.namenode.checkpoint.txns</name>
  <value>1000000</value>
<description>操作动作次数</description>
</property>

<property>
  <name>dfs.namenode.checkpoint.check.period</name>
  <value>60s</value>
<description> 1分钟检查一次操作次数</description>
</property>
2. 数据节点(DataNode)
  • 数据节点是分布式文件系统HDFS的工作节点,负责数据的存储和读取,会根据客户端或者是名称节点的调度来进行数据的存储和检索,并且向名称节点定期发送自己所存储的块的列表
  • 每个数据节点中的数据会被保存在各自节点的本地Linux文件系统中

🔍1. DataNode的工作机制

🔆(1)一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。 🔆(2)DataNode启动后向NameNode注册,通过后,周期性(6小时)的向NameNode上报所有的块信息。

DN向NN汇报当前解读信息的时间间隔,默认6小时

代码语言:javascript
复制
<property>
	<name>dfs.blockreport.intervalMsec</name>
	<value>21600000</value>
	<description>Determines block reporting interval in milliseconds.</description>
</property>

DN扫描自己节点块信息列表的时间,默认6小时

代码语言:javascript
复制
<property>
	<name>dfs.datanode.directoryscan.interval</name>
	<value>21600s</value>
	<description>Interval in seconds for Datanode to scan data directories and reconcile the difference between blocks in memory and on the disk.
	Support multiple time unit suffix(case insensitive), as described
	in dfs.heartbeat.interval.
	</description>
</property>

🔆(3)心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用。 🔆(4)集群运行中可以安全加入和退出一些机器。

🔍2. 数据完整性

如果DataNode节点上的数据损坏了,却没有发现,是否也很危险,该如何解决?如下是DataNode节点保证数据完整性的方法

  • (1)当DataNode读取Block的时候,它会计算CheckSum。
  • (2)如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏。
  • (3)Client读取其他DataNode上的Block。
  • (4)常见的校验算法crc(32),md5(128),sha1(160)
  • (5)DataNode在其文件创建后周期验证CheckSum。

🔍3. 掉线时限参数设置

需要注意的是hdfs-site.xml 配置文件中的heartbeat.recheck.interval的单位为毫秒,dfs.heartbeat.interval的单位为秒。

代码语言:javascript
复制
<property>
    <name>dfs.namenode.heartbeat.recheck-interval</name>
    <value>300000</value>
</property>

<property>
    <name>dfs.heartbeat.interval</name>
    <value>3</value>
</property>

1.6 HDFS体系结构

🔍1. HDFS体系结构概述

HDFS采用了主从(Master/Slave)结构模型,一个HDFS集群包括一个名称节点(NameNode)和若干个数据节点(DataNode)。名称节点作为中心服务器,负责管理文件系统的命名空间及客户端对文件的访问。集群中的数据节点一般是一个节点运行一个数据节点进程,负责处理文件系统客户端的读/写请求,在名称节点的统一调度下进行数据块的创建、删除和复制等操作。每个数据节点的数据实际上是保存在本地Linux文件系统中的,如下图所示:

🔆1.NameNode(nn):就是Master,它是一个主管、管理者。

  • (1)管理HDFS的名称空间;
  • (2)配置副本策略;
  • (3)管理数据块(Block)映射信息;
  • (4)处理客户端读写请求。

🔆2.DataNode:就是Slave。NameNode下达命令,DataNode执行实际的操作。

  • (1)存储实际的数据块;
  • (2)执行数据块的读/写操作。

🔆3.Client:就是客户端。

  • (1)文件切分。文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行上传;
  • (2)与NameNode交互,获取文件的位置信息;
  • (3)与DatalNode交互,读取或者写入数据;
  • (4)Client提供一些命令来管理HDFS,比如NameNode格式化;
  • (5)Client可以通过一些命令来访问HDFS,比如对HDFS增删查改操作;

🔆4.Secondary NameNode:并非NameNode的热备。当NameNode挂掉的时候,它并不能马上替换NameNode并提供服务。

  • (1)辅助NameNode,分担其工作量,比如定期合并Fsimage和Edits,并推送给NameNode;
  • (2)在紧急情兄下,可辅助恢复NameNode。

🔍2. HDFS命名空间管理

  • HDFS的命名空间包含目录、文件和块
  • 在HDFS1.0体系结构中,在整个HDFS集群中只有一个命名空间,并且只有唯一一个名称节点,该节点负责对这个命名空间进行管理
  • HDFS使用的是传统的分级文件体系,因此,用户可以像使用普通文件系统一样,创建、删除目录和文件,在目录间转移文件,重命名文件等

🔍3. HDFS的通信协议

  • HDFS是一个部署在集群上的分布式文件系统,因此,很多数据需要通过网络进行传输
  • 所有的HDFS通信协议都是构建在TCP/IP协议基础之上的
  • 客户端通过一个可配置的端口向名称节点主动发起TCP连接,并使用客户端协议与名称节点进行交互
  • 名称节点和数据节点之间则使用数据节点协议进行交互
  • 客户端与数据节点的交互是通过RPC(Remote Procedure Call)来实现的。在设计上,名称节点不会主动发起RPC,而是响应来自客户端和数据节点的RPC请求

🔍4. HDFS客户端(此部分后续会进行讲解)

  • 客户端是用户操作HDFS最常用的方式,HDFS在部署时都提供了客户端
  • HDFS客户端是一个库,暴露了HDFS文件系统接口,这些接口隐藏了HDFS实现中的大部分复杂性
  • 严格来说,客户端并不算是HDFS的一部分
  • 客户端可以支持打开、读取、写入等常见的操作,并且提供了类似Shell的命令行方式来访问HDFS中的数据
  • 此外,HDFS也提供了Java API,作为应用程序访问文件系统的客户端编程接口

🔍5. HDFS体系结构的局限性

HDFS只设置唯一一个名称节点,这样做虽然大大简化了系统设计,但也带来了一些明显的局限性,具体如下:

  • (1)命名空间的限制:名称节点是保存在内存中的,因此,名称节点能够容纳的对象(文件、块)的个数会受到内存空间大小的限制。
  • (2)性能的瓶颈:整个分布式文件系统的吞吐量,受限于单个名称节点的吞吐量。
  • (3)隔离问题:由于集群中只有一个名称节点,只有一个命名空间,因此,无法对不同应用程序进行隔离。
  • (4)集群的可用性:一旦这个唯一的名称节点发生故障,会导致整个集群变得不可用。

1.7 HDFS存储原理

🔍1. 冗余数据保存

作为一个分布式文件系统,为了保证系统的容错性和可用性,HDFS采用了多副本方式对数据进行冗余存储,通常一个数据块的多个副本会被分布到不同的数据节点上,如下图所示,数据块1被分别存放到数据节点A和C上,数据块2被存放在数据节点A和B上。这种多副本方式具有以下几个优点:

  • (1)加快数据传输速度
  • (2)容易检查数据错误
  • (3)保证数据可靠性

🔍2. 数据存取策略

🔆1.数据存放

  • 第一个副本:放置在上传文件的数据节点;如果是集群外提交,则随机挑选一台磁盘不太满、CPU不太忙的节点
  • 第二个副本:放置在与第一个副本不同的机架的节点上
  • 第三个副本:与第一个副本相同机架的其他节点上
  • 更多副本:随机节点

🔆2.数据读取

  • HDFS提供了一个API可以确定一个数据节点所属的机架ID,客户端也可以调用API获取自己所属的机架ID
  • 当客户端读取数据时,从名称节点获得数据块不同副本的存放位置列表,列表中包含了副本所在的数据节点,可以调用API来确定客户端和这些数据节点所属的机架ID,当发现某个数据块副本对应的机架ID和客户端对应的机架ID相同时,就优先选择该副本读取数据,如果没有发现,就随机选择一个副本读取数据

🔍3. 数据错误与恢复

HDFS具有较高的容错性,可以兼容廉价的硬件,它把硬件出错看作一种常态,而不是异常,并设计了相应的机制检测数据错误和进行自动恢复,主要包括以下几种情形:名称节点出错、数据节点出错和数据出错。

🔆1.名称节点出错

名称节点保存了所有的元数据信息,其中,最核心的两大数据结构是FsImage和Editlog,如果这两个文件发生损坏,那么整个HDFS实例将失效。因此,HDFS设置了备份机制,把这些核心文件同步复制到备份服务器SecondaryNameNode上。当名称节点出错时,就可以根据备份服务器SecondaryNameNode中的FsImage和Editlog数据进行恢复。

🔆2.数据节点出错

  • 每个数据节点会定期向名称节点发送“心跳”信息,向名称节点报告自己的状态
  • 当数据节点发生故障,或者网络发生断网时,名称节点就无法收到来自一些数据节点的心跳信息,这时,这些数据节点就会被标记为“宕机”,节点上面的所有数据都会被标记为“不可读”,名称节点不会再给它们发送任何I/O请求
  • 这时,有可能出现一种情形,即由于一些数据节点的不可用,会导致一些数据块的副本数量小于冗余因子
  • 名称节点会定期检查这种情况,一旦发现某个数据块的副本数量小于冗余因子,就会启动数据冗余复制,为它生成新的副本
  • HDFS和其它分布式文件系统的最大区别就是可以调整冗余数据的位置

🔆3.数据出错

  • 网络传输和磁盘错误等因素,都会造成数据错误
  • 客户端在读取到数据后,会采用md5和sha1对数据块进行校验,以确定读取到正确的数据
  • 在文件被创建时,客户端就会对每一个文件块进行信息摘录,并把这些信息写入到同一个路径的隐藏文件里面
  • 当客户端读取文件的时候,会先读取该信息文件,然后,利用该信息文件对每个读取的数据块进行校验,如果校验出错,客户端就会请求到另外一个数据节点读取该文件块,并且向名称节点报告这个文件块有错误,名称节点会定期检查并且重新复制这个块

1.8 HDFS数据读写流程

🔍1. 读写demo

下图为读取文件demo。

下图为写入文件demo。

✨简单介绍案例demo

  • FileSystem是一个通用文件系统的抽象基类,可以被分布式文件系统继承,所有可能使用Hadoop文件系统的代码,都要使用这个类
  • Hadoop为FileSystem这个抽象类提供了多种具体实现
  • DistributedFileSystem就是FileSystem在HDFS文件系统中的具体实现
  • FileSystem的open()方法返回的是一个输入流FSDataInputStream对象,在HDFS文件系统中,具体的输入流就是DFSInputStream;FileSystem中的create()方法返回的是一个输出流FSDataOutputStream对象,在HDFS文件系统中,具体的输出流就是DFSOutputStream。

🔍2. 读数据的过程

🔍3. 写数据的过程

二、HDFS的编程实践

接下来介绍Linux操作系统中关于HDFS文件操作的常用Shell命令,利用Web界面查看和管理Hadoop文件系统,以及利用Hadoop提供的Java API进行基本的文件操作。

2.1 HDFS的Shell操作

Hadoop支持很多Shell命令,其中fs是HDFS最常用的命令,利用fs可以查看HDFS文件系统的目录结构、上传和下载数据、创建文件等。虽然本人习惯使用hdfs dfs

2.1.1 命令大全

我们可以在终端输入bin/hdfs dfs,查看dfs总共支持了哪些命令

代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hdfs dfs

下面为显示的内容:

代码语言:javascript
复制
Usage: hadoop fs [generic options]
	[-appendToFile <localsrc> ... <dst>]
	[-cat [-ignoreCrc] <src> ...]
	[-checksum <src> ...]
	[-chgrp [-R] GROUP PATH...]
	[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
	[-chown [-R] [OWNER][:[GROUP]] PATH...]
	[-copyFromLocal [-f] [-p] [-l] [-d] [-t <thread count>] <localsrc> ... <dst>]
	[-copyToLocal [-f] [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
	[-count [-q] [-h] [-v] [-t [<storage type>]] [-u] [-x] [-e] <path> ...]
	[-cp [-f] [-p | -p[topax]] [-d] <src> ... <dst>]
	[-createSnapshot <snapshotDir> [<snapshotName>]]
	[-deleteSnapshot <snapshotDir> <snapshotName>]
	[-df [-h] [<path> ...]]
	[-du [-s] [-h] [-v] [-x] <path> ...]
	[-expunge]
	[-find <path> ... <expression> ...]
	[-get [-f] [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
	[-getfacl [-R] <path>]
	[-getfattr [-R] {-n name | -d} [-e en] <path>]
	[-getmerge [-nl] [-skip-empty-file] <src> <localdst>]
	[-head <file>]
	[-help [cmd ...]]
	[-ls [-C] [-d] [-h] [-q] [-R] [-t] [-S] [-r] [-u] [-e] [<path> ...]]
	[-mkdir [-p] <path> ...]
	[-moveFromLocal <localsrc> ... <dst>]
	[-moveToLocal <src> <localdst>]
	[-mv <src> ... <dst>]
	[-put [-f] [-p] [-l] [-d] <localsrc> ... <dst>]
	[-renameSnapshot <snapshotDir> <oldName> <newName>]
	[-rm [-f] [-r|-R] [-skipTrash] [-safely] <src> ...]
	[-rmdir [--ignore-fail-on-non-empty] <dir> ...]
	[-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
	[-setfattr {-n name [-v value] | -x name} <path>]
	[-setrep [-R] [-w] <rep> <path> ...]
	[-stat [format] <path> ...]
	[-tail [-f] [-s <sleep interval>] <file>]
	[-test -[defsz] <path>]
	[-text [-ignoreCrc] <src> ...]
	[-touch [-a] [-m] [-t TIMESTAMP ] [-c] <path> ...]
	[-touchz <path> ...]
	[-truncate [-w] <length> <path> ...]
	[-usage [cmd ...]]
......

2.1.2 常用命令及其作用对比表

序号

命令

作用

1

-help

输出这个命令参数

2

-ls

显示目录信息

3

-mkdir

在HDFS上创建目录

4

-moveFromLocal

从本地剪切粘贴到HDFS

5

-appendToFile

追加一个文件到已经存在的文件末尾

6

-cat

显示文件内容

7

-chgrp 、-chmod、-chown

Linux文件系统中的用法一样,修改文件所属权限

8

-copyFromLocal

从本地文件系统中拷贝文件到HDFS路径去

9

-copyToLocal

从HDFS拷贝到本地

10

-cp

从HDFS的一个路径拷贝到HDFS的另一个路径

11

-mv

在HDFS目录中移动文件

12

-get

等同于copyToLocal,就是从HDFS下载文件到本地

13

-getmerge

合并下载多个文件

14

-put

等同于copyFromLocal

15

-tail

显示一个文件的末尾

16

-rm

删除文件或文件夹

17

-rmdir

删除空目录

18

-du

统计文件夹的大小信息

19

-setrep

设置HDFS中文件的副本数量

20

- expunge

清空HDFS垃圾桶

2.13 常用命令的实际操作

  • 1. -help:输出这个命令参数
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -help rm
  • 2.-ls: 显示目录信息
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -ls /
  • 3. -mkdir:在HDFS上创建目录
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -mkdir -p /sanguo/shuguo
  • 4.-moveFromLocal:从本地剪切粘贴到HDFS
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ touch kongming.txt

hadoop@Master:/opt/moudle/hadoop$ hadoop fs  -moveFromLocal  ./kongming.txt  /sanguo/shuguo
  • 5.-appendToFile:追加一个文件到已经存在的文件末尾
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ vim liubei.txt
输入
bu wen bu huo
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -appendToFile liubei.txt /sanguo/shuguo/kongming.txt
  • 6.-cat:显示文件内容
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -cat /sanguo/shuguo/kongming.txt
  • 7. -chgrp 、-chmod、-chown:Linux文件系统中的用法一样,修改文件所属权限
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs  -chmod  666  /sanguo/shuguo/kongming.txt

hadoop@Master:/opt/moudle/hadoop$ hadoop fs  -chown  bigdata:bigdata   /sanguo/shuguo/kongming.txt
  • 8. -copyFromLocal:从本地文件系统中拷贝文件到HDFS路径去
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -copyFromLocal README.txt /
  • 9.-copyToLocal:从HDFS拷贝到本地
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -copyToLocal /sanguo/shuguo/kongming.txt ./
  • 10. -cp :从HDFS的一个路径拷贝到HDFS的另一个路径
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -cp /sanguo/shuguo/kongming.txt /zhuge.txt
  • 11.-mv:在HDFS目录中移动文件
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -mv /zhuge.txt /sanguo/shuguo/
  • 12.-get:等同于copyToLocal,就是从HDFS下载文件到本地
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -get /sanguo/shuguo/kongming.txt ./
  • 13.-getmerge:合并下载多个文件,比如HDFS的目录 /user/bigdata/test下有多个文件:log.1, log.2,log.3,.
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -getmerge /user/bigdata/test/*  ./zaiyiqi.txt
  • 14. -put:等同于copyFromLocal
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -put ./zaiyiqi.txt /user/bigdata/test/
  • 15. -tail:显示一个文件的末尾
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -tail /sanguo/shuguo/kongming.txt
  • 16.-rm:删除文件或文件夹
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -rm /user/bigdata/test/jinlian2.txt
  • 17. -rmdir:删除空目录
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -mkdir /test

hadoop@Master:/opt/moudle/hadoop$ hadoop fs -rmdir /test
  • 18. -du 统计文件夹的大小信息
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -du -s -h /user/bigdata/test

hadoop@Master:/opt/moudle/hadoop$ hadoop fs -du  -h /user/bigdata/test
  • 19. -setrep:设置HDFS中文件的副本数量
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hadoop fs -setrep 10 /sanguo/shuguo/kongming.txt

这里设置的副本数只是记录在NameNode的元数据中,是否真的会有这么多副本,还得看DataNode的数量。因为目前只有3台设备,最多也就3个副本,只有节点数的增加到10台时,副本数才能达到10。

  • 20. - expunge :清空hdfs垃圾桶
代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hdfs dfs  -expunge

2.2 利用Web界面管理HDFS

🔆打开浏览器,点击此链接HDFS的Web界面,即可看到HDFS的web管理界面。WEB界面的访问地址是http://localhost:9870。

2.3 利用Java API与HDFS进行交互

Hadoop不同的文件系统之间通过调用Java API进行交互,上面介绍的Shell命令,本质上就是Java API的应用。下面提供了Hadoop官方的Hadoop API文档,想要深入学习Hadoop,可以访问如下网站,查看各个API的功能。 Hadoop API文档

利用Java API进行交互,需要利用软件Eclipse或者IDEA编写Java程序,下面对其分别讲解。

2.3.1 Eclipse操作Java API

1 在Ubuntu中安装Eclipse

Eclipse是常用的程序开发工具,我们可以在Linux系统中安装Eclipse。可以到Eclipse官网https://www.eclipse.org/downloads/下载安装包。 或者使用链接:https://pan.baidu.com/s/1rNUb8xOS6pngEke1MwCcIg ,提取码:rmzn 。

🔍1.解压eclipse

代码语言:javascript
复制
hadoop@Master:~$ sudo tar -zxvf /opt/software/eclipse-4.7.0-linux.gtk.x86_64.tar.gz -C /opt/moudle/

🔍2.执行命令启动eclipse

代码语言:javascript
复制
hadoop@Master:~$ cd /opt/moudle/eclipse/
hadoop@Master:/opt/moudle/eclipse$ ll

我们可以看到eclipse是个可执行文件,这个时候只需要输入./eclipse即可。

代码语言:javascript
复制
hadoop@Master:/opt/moudle/eclipse$ ./eclipse
1. 使用Eclipse开发调试HDFS Java程序

Hadoop采用Java语言开发的,提供了Java API与HDFS进行交互。上面介绍的Shell命令,在执行时实际上会被系统转换成Java API调用。Hadoop官方网站提供了完整的Hadoop API文档(http://hadoop.apache.org/docs/stable/api/),想要深入学习Hadoop编程,可以访问Hadoop官网查看各个API的功能和用法。本篇文章只介绍基础的HDFS编程。

现在要执行的任务是:假设在目录“hdfs://master:9000/user/hadoop”下面有几个文件,分别是file1.txt、file2.txt、file3.txt、file4.abc和file5.abc,这里需要从该目录中过滤出所有后缀名不为“.abc”的文件,对过滤之后的文件进行读取,并将这些文件的内容合并到文件“hdfs://master:9000/user/hadoop/merge.txt”中。

🔍1. 在Eclipse中创建项目

启动Eclipse。当Eclipse启动以后,会弹出如下图所示界面,提示设置工作空间(workspace)。

可以直接采用默认的设置“/home/hadoop/workspace”,点击“OK”按钮。可以看出,由于当前是采用hadoop用户登录了Linux系统,因此,默认的工作空间目录位于hadoop用户目录“/home/hadoop”下。

Eclipse启动以后,会呈现如下图所示的界面。

选择“File–>New–>Java Project”菜单,开始创建一个Java工程,会弹出如下图所示界面。

在“Project name”后面输入工程名称“HDFSExample”,选中“Use default location”,让这个Java工程的所有文件都保存到“/home/hadoop/workspace/HDFSExample”目录下。在“JRE”这个选项卡中,可以选择当前的Linux系统中已经安装好的JDK,比如jdk1.8。然后,点击界面底部的“Next>”按钮,进入下一步的设置。

🔍1. 为项目添加需要用到的JAR包

进入下一步的设置以后,会弹出如下图所示界面。

需要在这个界面中加载该Java工程所需要用到的JAR包,这些JAR包中包含了可以访问HDFS的Java API。这些JAR包都位于Linux系统的Hadoop安装目录下,对于本教程而言,就是在“/opt/moudle/hadoop/share/hadoop”目录下。点击界面中的“Libraries”选项卡,然后,点击界面右侧的“Add External JARs…”按钮,会弹出如下图所示界面。

在该界面中,上面的一排目录按钮(即“opt”、“moudle”、“hadoop”、“share”、“hadoop”和“common”),当点击某个目录按钮时,就会在下面列出该目录的内容。

为了编写一个能够与HDFS交互的Java应用程序,一般需要向Java工程中添加以下JAR包:

  • (1)“/opt/moudle/hadoop/share/hadoop/common”目录下的所有JAR包,包括hadoop-common-3.1.3.jar、hadoop-common-3.1.3-tests.jar、haoop-nfs-3.1.3.jar和haoop-kms-3.1.3.jar,注意,不包括目录jdiff、lib、sources和webapps;
  • (2)“/opt/moudle/hadoop/share/hadoop/common/lib”目录下的所有JAR包;
  • (3)“/opt/moudle/hadoop/share/hadoop/hdfs”目录下的所有JAR包,注意,不包括目录jdiff、lib、sources和webapps;
  • (4)“/opt/moudle/hadoop/share/hadoop/hdfs/lib”目录下的所有JAR包。

比如,如果要把“/opt/moudle/hadoop/share/hadoop/common”目录下的hadoop-common-3.1.3.jar、hadoop-common-3.1.3-tests.jar、haoop-nfs-3.1.3.jar和haoop-kms-3.1.3.jar添加到当前的Java工程中,可以在界面中点击目录按钮,进入到common目录,然后,界面会显示出common目录下的所有内容(如下图所示)。

请在界面中用鼠标点击选中hadoop-common-3.1.3.jar、hadoop-common-3.1.3-tests.jar、haoop-nfs-3.1.3.jar和haoop-kms-3.1.3.jar(不要选中目录jdiff、lib、sources和webapps),然后点击界面右下角的“确定”按钮,就可以把这两个JAR包增加到当前Java工程中,出现的界面如下图所示。

从这个界面中可以看出,hadoop-common-3.1.3.jar、hadoop-common-3.1.3-tests.jar、haoop-nfs-3.1.3.jar和haoop-kms-3.1.3.jar已经被添加到当前Java工程中。然后,按照类似的操作方法,可以再次点击“Add External JARs…”按钮,把剩余的其他JAR包都添加进来。需要注意的是,当需要选中某个目录下的所有JAR包时,可以使用“Ctrl+A”组合键进行全选操作。全部添加完毕以后,就可以点击界面右下角的“Finish”按钮,完成Java工程HDFSExample的创建。

🔍3. 编写Java应用程序

下面编写一个Java应用程序。

请在Eclipse工作界面左侧的“Package Explorer”面板中(如下图所示),找到刚才创建好的工程名称“HDFSExample”,然后在该工程名称上点击鼠标右键,在弹出的菜单中选择“New–>Class”菜单。

在该界面中,只需要在“Name”后面输入新建的Java类文件的名称,这里采用名称“MergeFile1”,其他都可以采用默认设置,然后,点击界面右下角“Finish”按钮,出现如下图所示界面。

可以看出,Eclipse自动创建了一个名为“MergeFile1.java”的源代码文件,请在该文件中输入以下代码:

🔍4. 编译运行程序

在开始编译运行程序之前,请一定确保Hadoop已经启动运行,如果还没有启动,需要打开一个Linux终端,输入以下命令启动Hadoop:

然后,要确保HDFS的“/user/hadoop”目录下已经存在file1.txt、file2.txt、file3.txt、file4.abc和file5.abc,每个文件里面有内容。这里,假设文件内容如下:

代码语言:javascript
复制
file1.txt的内容是: this is file1.txt
file2.txt的内容是: this is file2.txt
file3.txt的内容是: this is file3.txt
file4.abc的内容是: this is file4.abc
file5.abc的内容是: this is file5.abc

现在就可以编译运行上面编写的代码。可以直接点击Eclipse工作界面上部的运行程序的快捷按钮,当把鼠标移动到该按钮上时,在弹出的菜单中选择“Run As”,继续在弹出来的菜单中选择“Java Application”,如下图所示。

然后,会弹出如下图所示界面。

在该界面中,点击界面右下角的“OK”按钮,开始运行程序。程序运行结束后,会在底部的“Console”面板中显示运行结果信息(如下图所示)。同时,“Console”面板中可能还会显示一些类似“log4j:WARN…”的警告信息,可以不用理会。

如果程序运行成功,这时,可以到HDFS中查看生成的merge.txt文件,比如,可以在Linux终端中执行如下命令:

代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hdfs dfs -ls /user/hadoop
Found 5 items
-rw-r--r--   3 hadoop supergroup       5889 2021-09-27 13:04 /user/hadoop/MapReduceDemo.jar
drwxr-xr-x   - hadoop supergroup          0 2021-09-27 14:54 /user/hadoop/input
drwxr-xr-x   - hadoop supergroup          0 2021-09-27 13:07 /user/hadoop/input1
-rw-r--r--   3 hadoop supergroup         54 2021-09-27 14:58 /user/hadoop/merge.txt
drwxr-xr-x   - hadoop supergroup          0 2021-09-27 13:10 /user/hadoop/output

hadoop@Master:/opt/moudle/hadoop$ hdfs dfs -cat /user/hadoop/merge.txt
2021-09-27 15:01:04,105 INFO sasl.SaslDataTransferClient: SASL encryption trust check: localHostTrusted = false, remoteHostTrusted = false
this is file1.txt
this is file2.txt
this is file3.txt

🔍5. 应用程序的部署 下面介绍如何把Java应用程序生成JAR包,部署到Hadoop平台上运行。首先,在Hadoop安装目录下新建一个名称为myapp的目录,用来存放我们自己编写的Hadoop应用程序,可以在Linux的终端中执行如下命令:

代码语言:javascript
复制
hadoop@Master:~$ cd /opt/moudle/hadoop/
hadoop@Master:/opt/moudle/hadoop$ mkdir myapp

然后,请在Eclipse工作界面左侧的“Package Explorer”面板中,在工程名称“HDFSExample”上点击鼠标右键,在弹出的菜单中选择“Export”,如下图所示。

然后,会弹出如下图所示界面。

在该界面中,选择“Runnable JAR file”,然后,点击“Next>”按钮,弹出如下图所示界面。

在该界面中,“Launch configuration”用于设置生成的JAR包被部署启动时运行的主类,需要在下拉列表中选择刚才配置的类“MergeFile-HDFSExample”。在“Export destination”中需要设置JAR包要输出保存到哪个目录,比如,这里设置为“/opt/moudle/hadoop/myapp/HDFSExample.jar”。在“Library handling”下面选择“Extract required libraries into generated JAR”。然后,点击“Finish”按钮,会出现如下图所示界面。

可以忽略该界面的信息,直接点击界面右下角的“OK”按钮,启动打包过程。打包过程结束后,会出现一个警告信息界面,如下图所示。

可以忽略该界面的信息,直接点击界面右下角的“OK”按钮。至此,已经顺利把HDFSExample工程打包生成了HDFSExample.jar。可以到Linux系统中查看一下生成的HDFSExample.jar文件,可以在Linux的终端中执行如下命令:

可以看到,“/opt/moudle/hadoop/myapp”目录下已经存在一个HDFSExample.jar文件。 由于之前已经运行过一次程序,已经生成了merge.txt,因此,需要首先执行如下命令删除该文件:

代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop/myapp$ hdfs dfs -rm -r /user/hadoop/merge.txt

现在,就可以在Linux系统中,使用hadoop jar命令运行程序,命令如下:

代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop/myapp$ hadoop jar HDFSExample.jar 

上面程序执行结束以后,可以到HDFS中查看生成的merge.txt文件,比如,可以在Linux终端中执行如下命令:

代码语言:javascript
复制
hadoop@Master:/opt/moudle/hadoop$ hdfs dfs -cat merge.txt

🔍6. 几个测试demo

🔆1.写入文件

🔆2.判断文件是否存在

🔆3.读取文件

2.3.2 IDEA操作Java API

1. 测试前的准备

🔍1. 在IDEA中创建一个Maven工程HdfsClientDemo,并导入相应的依赖坐标

Maven工程中关于Hadoop的配置项有hadoop-common、hadoop-client、hadoop-hdfs三项,具体配置可以在https://www.mvnrepository.com/中进行查询。

🔆 下面在pom中添加依赖

代码语言:javascript
复制
// 需要添加的依赖
    <dependencies>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>3.1.3</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
        </dependency>
    </dependencies>

因为是第一次使用,所以编辑好之后需要下载依赖,如下:

选择IDEA右下角Import Changes即可下载依赖,或者点击同步Maven依赖,下载可能需要较长的时间,需要等待下载并解决相关依赖;

同时,为了后面统计和验证代码方便,还需要引如junit测试和log4j控制日志打印输出级别。

🔍2.日志添加

在项目的src/main/resources目录下,新建一个文件,命名为“log4j.properties”,在文件中填入

代码语言:javascript
复制
log4j.rootLogger=INFO, stdout  
log4j.appender.stdout=org.apache.log4j.ConsoleAppender  
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout  
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n  
log4j.appender.logfile=org.apache.log4j.FileAppender  
log4j.appender.logfile.File=target/spring.log  
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout  
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n

可以根据日志的输出情况修改log4j.rootLogger的值。

🔍3.创建包名:com.buwenbuhuo.hdfs

🔍4.创建HdfsClient类

🔍5.执行程序

提示:客户端去操作HDFS时,是有一个用户身份的。默认情况下,HDFS客户端API会从采用Windows默认用户访问HDFS,会报权限异常错误。所以在访问HDFS时,一定要配置用户。

2. HDFS的API案例实操

🔍1. HDFS文件上传

🔆源码如下:

🔆运行结果如下:

🔍2.HDFS文件下载

🔆源码如下:

🔆运行结果如下:

注意:如果执行上面代码,下载不了文件,有可能是你电脑的微软支持的运行库少,需要安装一下微软运行库。

🔍3.HDFS文件更名和移动

🔆源码如下:

🔆运行结果如下:

🔍4.HDFS删除文件和目录

🔆源码如下:

🔆运行结果如下:

🔍5.HDFS文件详情查看

🔆源码如下:

🔆运行结果如下:

🔍6.HDFS文件和文件夹判断

🔆源码如下:

🔆运行结果如下:

本篇文章到这里就结束了,如有不足请指出~


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/09/27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 前言
  • 一、分布式文件系统HDFS
    • 1.1 什么是分布式文件系统
      • 1.2 HDFS产生背景及定义
        • 1.3 HDFS的设计
          • 1.4 HDFS的优缺点
            • 1.4.1 HDFS优点
            • 1.4.2 HDFS缺点
          • 1.5 HDFS的概念
            • 1.5.1 数据块
            • 1.5.2 名称节点和数据节点
          • 1.6 HDFS体系结构
            • 1.7 HDFS存储原理
              • 1.8 HDFS数据读写流程
              • 二、HDFS的编程实践
                • 2.1 HDFS的Shell操作
                  • 2.1.1 命令大全
                  • 2.1.2 常用命令及其作用对比表
                  • 2.13 常用命令的实际操作
                • 2.2 利用Web界面管理HDFS
                  • 2.3 利用Java API与HDFS进行交互
                    • 2.3.1 Eclipse操作Java API
                    • 2.3.2 IDEA操作Java API
                相关产品与服务
                文件存储
                文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档