hadoop学习案例——使用Java API操作HDFS

案例————使用Java API操作HDFS

  • Hadoop的核心是HDFS和MapReduce。其中,HDFS是解决海量大数据文件存储的问题,是目前应用最广泛的分布式文件系统。

  • HDFS(Hadoop Distributed Filesystem)是一个易于扩展的分布式文件系统,运行在成百上千台低成本的机器上。它与现有的分布式文件系统有许多相似之处,都是用来存储数据的系统工具,而区别于HDFS具有高度容错能力,旨在部署在低成本机器上。HDFS主要用于对海量文件信息进行存储和管理,也就是解决大数据文件(如TB乃至PB级)的存储问题。


一、HDFS的基本概念

1、NameNode(名称节点):

NameNode是HDFS集群的主服务器,通常称为名称节点或者主节点。一旦NameNode关闭,就无法访问Hadoop集群。NameNode主要以元数据的形式进行管理和存储,用于维护文件系统名称并管理客户端对文件的访问;NameNode记录对文件系统名称空间或其属性的任何更改操作;HDFS负责整个数据集群的管理,并且在配置文件中可以设置备份数量,这些信息都由NameNode存储。

2、DataNode(数据节点)

DataNode是HDFS集群中的从服务器,通常称为数据节点。文件系统存储文件的方式是将文件切分成多个数据块,这些数据块实际上是存储在DataNode节点中的,因此DataNode机器需要配置大量磁盘空间。它与NameNode保持不断的通信,DataNode在客户端或者NameNode的调度下,存储并检索数据块,对数据块进行创建、删除等操作,并且定期向NameNode发送所存储的数据块列表。

3、Block(数据块)

每个磁盘都有默认的数据块大小,这是磁盘进行数据读/写的最小单位,HDFS同样也有块(block)的概念,它是抽象的块,而非整个文件作为存储单元,在Hadoop2.x版本下,默认大小是128M,且备份3份,每个块尽可能地存储于不同的DataNode中。按块存储的好处主要是屏蔽了文件的大小,提供数据的容错性和可用性。

4、Rack(机架)

Rack是用来存放部署Hadoop集群服务器的机架,不同机架之间的节点通过交换机通信,HDFS通过机架感知策略,使NameNode能够确定每个DataNode所属的机架ID,使用副本存放策略,来改进数据的可靠性、可用性和网络带宽的利用率。

5、 Metadata(元数据)

元数据从类型上分可分三种信息形式,一是维护HDFS文件系统中文件和目录的信息,例如文件名、目录名、父目录信息、文件大小、创建时间、修改时间等;二是记录文件内容存储相关信息,例如文件分块情况、副本个数、每个副本所在的DataNode信息等;三是用来记录HDFS中所有DataNode的信息,用于DataNode管理。

6、HDFS的特点:

优点 高容错 流式数据访问 支持超大文件 高数据吞吐量
缺点 高延迟 不适合小文件存取 不适合并发写入 ……

二、HDFS的Shell介绍

文件系统(FS)Shell包含了各种的类Shell的命令,可以直接与Hadoop分布式文件系统以及其他文件系统进行交互。

命令参数 功能描述 命令参数 功能描述
-ls 查看指定路径的目录结构 -cat 查看文件内容
-du 统计目录下所有文件大小 -text 源文件输出为文本格式
-mv 移动文件 -mkdir 创建空白文件夹
-cp 复制文件 -put 上传文件
-rm 删除文件/空白文件夹 -help 删除文件/空白文件夹

三、案例——使用Java API操作HDFS

1. 搭建项目环境

  1. 创建一个项目名为“HadoopDemo”,包名为“com.itcast”的Maven项目

    • groupId设为com.itcast
    • artifactId设为HadoopDemo
    • 版本默认即可

    image-20240419141014815

  2. 在项目的pom.xml文件中引入hadoop-common、hadoop-hdfs、hadoop-client以及单元测试junit的依赖

    • 示例代码如下:

      <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.itcast</groupId>
            <artifactId>HadoopDemo</artifactId>
            <version>1.0-SNAPSHOT</version>
            <packaging>jar</packaging>
      
            <name>HadoopDemo</name>
            <url>http://maven.apache.org</url>
      
            <properties>
              <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            </properties>
      
            <dependencies>
            <dependency>
              <groupId>org.apache.hadoop</groupId>
              <artifactId>hadoop-common</artifactId>
              <version>2.7.3</version>
            </dependency>
            <dependency>
              <groupId>org.apache.hadoop</groupId>
              <artifactId>hadoop-hdfs</artifactId>
              <version>2.7.3</version>
            </dependency>
            <dependency>
              <groupId>org.apache.hadoop</groupId>
              <artifactId>hadoop-client</artifactId>
              <version>2.7.3</version>
            </dependency>
              <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>RELEASE</version>
      <!--          <scope>test</scope>-->
              </dependency>
            </dependencies>
      </project>
      

2. 初始化客户端对象

  • 在项目src文件夹下创建com.itcast.hdfsdemo包

  • 在该包下创建HDFS_CRUD.java文件

    image-20240419133836329

  • 示例代码:

        @Before    //初始化
        public void init() throws Exception {
            // 构造一个配置参数对象,设置一个参数:我们要访问的hdfs的URI
            Configuration conf = new Configuration();
            // 这里指定使用的是HDFS文件系统
            conf.set("fs.defaultFS", "hdfs://hadoopShaowenhua1:9000");
            // 通过如下的方式进行客户端身份的设置
            System.setProperty("HADOOP_USER_NAME", "root");
            // 通过FileSystem的静态方法获取文件系统客户端对象
            fs = FileSystem.get(conf);
        }
    

3. 上传文件到HDFS

  • 由于采用Java测试类来实现JavaApi对HDFS的操作,因此可以在HDFS_CRUD.java文件中添加一个testAddFileToHdfs()方法来演示本地文件上传到HDFS的示例。

  • 示例代码

        @Test
        public void testAddFileToHdfs() throws IOException {
            // 要上传的文件所在本地路径
            Path src = new Path("D:/test.txt");
            // 要上传到hdfs的目标路径
            Path dst = new Path("/testFile/test");
            // 上传文件方法
            fs.copyFromLocalFile(src, dst);
            // 关闭资源
            fs.close();
        }
    

4. 从HDFS下载文件到本地

  • 在HDFS_CRUD.java文件中添加一个testDownloadFileToLocal()方法,来实现从HDFS中下载文件到本地系统的功能。

  • 示例代码

        // 从hdfs中复制文件到本地文件系统
        @Test
        public void testDownloadFileToLocal() throws IllegalArgumentException, IOException {
            // 下载文件
            fs.copyToLocalFile(new Path("/testFile/test/test.txt"), new Path("D:/"));
        }
    

5. 目录操作

  • 在HDFS_CRUD.java文件添加一个testMkdirAndDeleteAndRename()方法,实现目录的创建、删除、重命名的功能。

  • 示例代码

    //     创建,删除,重命名文件
        @Test
        public void testMkdirAndDeleteAndRename() throws Exception {
             //创建目录
            fs.mkdirs(new Path("/a/b/c"));
            fs.mkdirs(new Path("/a2/b2/c2"));
            // 重命名文件或文件夹
            fs.rename(new Path("/a"), new Path("/a3"));
            // 删除文件夹,如果是非空文件夹,参数2必须给值true
            fs.delete(new Path("/a3"), true);
        }
    

6. 查看目录中的文件信息

  • 在HDFS_CRUD.java文件中添加一个testListFiles()方法,实现查看目录中所有文件的详细信息的功能。

  • 示例代码

    //     查看目录信息,只显示文件
        @Test
        public void testListFiles() throws FileNotFoundException, IllegalArgumentException, IOException {
            // 获取迭代器对象
            RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);
            while (listFiles.hasNext()) {
                LocatedFileStatus fileStatus = listFiles.next();
                // 打印当前文件名
                System.out.println(fileStatus.getPath().getName());
                // 打印当前文件块大小
                System.out.println(fileStatus.getBlockSize());
                // 打印当前文件权限
                System.out.println(fileStatus.getPermission());
                // 打印当前文件内容长度
                System.out.println(fileStatus.getLen());
                // 获取该文件块信息(包含长度,数据块,datanode的信息)
                BlockLocation[] blockLocations = fileStatus.getBlockLocations();
                for (BlockLocation bl : blockLocations) {
                    System.out.println("block-length:" + bl.getLength() + "--" + "block-offset:" + bl.getOffset());
                    String[] hosts = bl.getHosts();
                    for (String host : hosts) {
                        System.out.println(host);
                    }
                }
                System.out.println("----------------------------");
            }
        }
    

7.查看文件及文件夹信息

  • 示例代码

    // 查看文件及文件夹信息
    @Test
    public void testListAll() throws FileNotFoundException, IllegalArgumentException, IOException {
        // 获取HDFS系统中文件和目录的元数据等信息
        FileStatus[] listStatus = fs.listStatus(new Path("/"));
        String flag = "d--             ";
        for (FileStatus fstatus : listStatus) {
            // 判断是文件还是文件夹
            if (fstatus.isFile())
                flag = "f--         ";
            System.out.println(flag + fstatus.getPath().getName());
        }
    }
    

8.最终代码展示

package com.itcast.hdfsdemo;

import java.io.FileNotFoundException;
import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.junit.Before;
import org.junit.Test;

public class HDFS_CRUD {
    FileSystem fs = null;

    @Before    //初始化
    public void init() throws Exception {
        // 构造一个配置参数对象,设置一个参数:我们要访问的hdfs的URI
        Configuration conf = new Configuration();
        // 这里指定使用的是HDFS文件系统
        conf.set("fs.defaultFS", "hdfs://hadoopShaowenhua1:9000");
        // 通过如下的方式进行客户端身份的设置
        System.setProperty("HADOOP_USER_NAME", "root");
        // 通过FileSystem的静态方法获取文件系统客户端对象
        fs = FileSystem.get(conf);
    }

//    @Test
//    public void testAddFileToHdfs() throws IOException {
//        // 要上传的文件所在本地路径
//        Path src = new Path("D:/test.txt");
//        // 要上传到hdfs的目标路径
//        Path dst = new Path("/testFile/test");
//        // 上传文件方法
//        fs.copyFromLocalFile(src, dst);
//        // 关闭资源
//        fs.close();
//    }

    // 从hdfs中复制文件到本地文件系统
    @Test
    public void testDownloadFileToLocal() throws IllegalArgumentException, IOException {
        // 下载文件
        fs.copyToLocalFile(new Path("/testFile/test/test.txt"), new Path("D:/"));
    }

//     创建,删除,重命名文件
//    @Test
//    public void testMkdirAndDeleteAndRename() throws Exception {
//         //创建目录
//        fs.mkdirs(new Path("/a/b/c"));
//        fs.mkdirs(new Path("/a2/b2/c2"));
//        // 重命名文件或文件夹
//        fs.rename(new Path("/a"), new Path("/a3"));
//        // 删除文件夹,如果是非空文件夹,参数2必须给值true
//        fs.delete(new Path("/a3"), true);
//    }

//     查看目录信息,只显示文件
    @Test
    public void testListFiles() throws FileNotFoundException, IllegalArgumentException, IOException {
        // 获取迭代器对象
        RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);
        while (listFiles.hasNext()) {
            LocatedFileStatus fileStatus = listFiles.next();
            // 打印当前文件名
            System.out.println(fileStatus.getPath().getName());
            // 打印当前文件块大小
            System.out.println(fileStatus.getBlockSize());
            // 打印当前文件权限
            System.out.println(fileStatus.getPermission());
            // 打印当前文件内容长度
            System.out.println(fileStatus.getLen());
            // 获取该文件块信息(包含长度,数据块,datanode的信息)
            BlockLocation[] blockLocations = fileStatus.getBlockLocations();
            for (BlockLocation bl : blockLocations) {
                System.out.println("block-length:" + bl.getLength() + "--" + "block-offset:" + bl.getOffset());
                String[] hosts = bl.getHosts();
                for (String host : hosts) {
                    System.out.println(host);
                }
            }
            System.out.println("----------------------------");
        }
    }

    // 查看文件及文件夹信息
    @Test
    public void testListAll() throws FileNotFoundException, IllegalArgumentException, IOException {
        // 获取HDFS系统中文件和目录的元数据等信息
        FileStatus[] listStatus = fs.listStatus(new Path("/"));
        String flag = "d--             ";
        for (FileStatus fstatus : listStatus) {
            // 判断是文件还是文件夹
            if (fstatus.isFile())
                flag = "f--         ";
            System.out.println(flag + fstatus.getPath().getName());
        }
    }

}

9.部分执行结果

img

posted @ 2024-04-19 14:05  朝暮青丝  阅读(406)  评论(0编辑  收藏  举报