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. 搭建项目环境
-
创建一个项目名为“HadoopDemo”,包名为“com.itcast”的Maven项目
- groupId设为com.itcast
- artifactId设为HadoopDemo
- 版本默认即可
-
在项目的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文件
-
示例代码:
@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());
}
}
}