一、实验目的

  1. 理解HDFS在Hadoop体系结构中的角色
  2. 熟练使用HDFS操作常用的shell命令
  3. 熟悉HDFS操作常用的Java API

二、实验平台

  • 操作系统:CentOS 8
  • Hadoop版本:3.3.1
  • jdk版本:1.8
  • Java IDE:Eclipse

三、实验内容

1. 使用Hadoop命令操作分布式文件系统。

  1. 新建目录
    在本地和hadoop中分别创建文件夹:

    在本地创建目录:
    在这里插入图片描述
    Hadoop创建目录:
    在这里插入图片描述

  2. 上传文件至dfs
    切换到本地input目录下,创建文件并添加数据:hello hadoop。
    在这里插入图片描述
    在这里插入图片描述
    将该文件上传至hadoop:使用hadoop fs -put <要上传的文件> <hdfs目录>命令。
    在这里插入图片描述
    查看上传到HDFS的文件:
    在这里插入图片描述

  3. 移动与删除
    列出HDFS中的目录和文件:
    在这里插入图片描述
    在这里插入图片描述
    将helloworld.txt移动到根目录:
    在这里插入图片描述
    删除helloworld.txt;
    在这里插入图片描述

下表列出了Hadoop常用的shell命令,在之后使用的时候可以作为参考。

名称选项使用格式含义
-lsr-lsr <路径>递归查看指定路径的目录结构
-du-du <路径>统计目录下个文件大小
-dus-dus <路径>汇总统计目录下文件(夹)大小
-count-count [-q] <路径>统计文件(夹)数量
-mv-mv <源路径> <目的路径>移动
-cp-cp <源路径> <目的路径>复制
-rm-rm [-skipTrash] <路径>删除文件/空白文件夹
-rmr-rmr [-skipTrash] <路径>递归删除
-put-put <多个 linux 上的文件> <hdfs 路径>上传文件
-copyFromLocal-copyFromLocal <多个 linux 上的文件><hdfs 路径>从本地复制
-moveFromLocal-moveFromLocal <多个 linux 上的文件><hdfs 路径>从本地移动
-getmerge-getmerge <源路径> <linux 路径>合并到本地
-cat-cat <hdfs 路径>查看文件内容
-text-text <hdfs 路径>查看文件内容
-copyToLocal-copyToLocal [-ignoreCrc] [-crc] [hdfs 源路径] [linux 目的路径]从HDFS复制到本地
-moveToLocal-moveToLocal [-crc] <hdfs 源路径> <linux目的路径>从HDFS移动到本地
-mkdir-mkdir <hdfs 路径>创建空白文件夹
-setrep-setrep [-R] [-w] <副本数> <路径>修改副本数量
-touchz-touchz <文件路径>创建空白文件

2. HDFS-JAVA接口之读取文件

  1. 在eclipse中创建Hadoop项目
    (1) 创建项目,新建lib目录
    在这里插入图片描述
    (2)将Hadoop项目所需要的jar包copy到lib目录下。
    将之前下载的hadoop压缩包解压到本地,打开share/hadoop的common目录和hdfs目录并将其中的jar包全部拷贝至项目的lib目录下。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    选中所有jar包添加到项目依赖
    在这里插入图片描述
    然后就可以开始进行编写hadoop项目了

  2. FileSystem对象
    要从Hadoop文件系统中读取文件,最简单的办法是使用java.net.URL对象打开数据流,从中获取数据。不过这种方法一般要使用FsUrlStreamHandlerFactory实例调用setURLStreamHandlerFactory()方法。不过每个Java虚拟机只能调用一次这个方法,所以如果其他第三方程序声明了这个对象,那我们将无法使用了。
    因为有时候我们不能在程序中设置URLStreamHandlerFactory实例,这个时候咱们就可以使用FileSystem API来打开一个输入流,进而对HDFS进行操作。

  3. FileSystem API示例
    首先我们在本地创建一个文件,然后上传到HDFS以供测试。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    接下来,在eclipse中编写代码,使用FileSystem,查看刚刚上传的文件。
    在这里插入图片描述
    运行出现错误:
    在这里插入图片描述
    因为我们没有log4j.properties文件,我们在src目录下创建一个log4j.properties文件,写入以下内容:

    #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
    #--------console-----------
    log4j.rootLogger=info,myconsole,myfile
    log4j.appender.myconsole=org.apache.log4j.ConsoleAppender
    log4j.appender.myconsole.layout=org.apache.log4j.SimpleLayout
    #log4j.appender.myconsole.layout.ConversionPattern =%d [%t] %-5p [%c] - %m%n
    
    #log4j.rootLogger=error,myfile
    log4j.appender.myfile=org.apache.log4j.DailyRollingFileAppender
    log4j.appender.myfile.File=/tmp/flume.log
    log4j.appender.myfile.layout=org.apache.log4j.PatternLayout
    log4j.appender.myfile.layout.ConversionPattern =%d [%t] %-5p [%c] - %m%n
    

    然后再次运行
    在这里插入图片描述
    还是不行,如果我们写的直接是虚拟机的ip地址,那默认访问的应该是8020端口,我在Hadoop配置core-site.xml文件的时候设置的是8088端口,所以需要修改端口号。
    在这里插入图片描述

    还是不行,
    在这里插入图片描述
    检查了一下我的hadoop启动情况,resourceManager没有启动,而ResourceManager的作用是:负责集群中所有资源的统一管理和分配,它接收来自各个NodeManager的资源汇报信息,并把这些信息按照一定的策略分配给各个ApplicationMaster。
    在这里插入图片描述
    原来是我yarn-site.xml中resourcemanager的ip写错了,详细过程请看这篇文章resourcemanager启动失败
    最后启动成功:
    在这里插入图片描述
    回到eclipse,重新运行代码,还是出现重连的情况,然后突然想起来是不是我的防火墙没有关,使用systemctl status firewalld.service查看防火墙
    在这里插入图片描述
    果然没有关防火墙,使用systemctl stop firewalld.service关闭防火墙,再输入systemctl disable firewalld.service永久关闭防火墙
    在这里插入图片描述
    然后再回到eclipse运行代码,现在是出现乱码错误:
    在这里插入图片描述
    点击"Windows–>preferences"
    在这里插入图片描述
    选择"general–>workspace–>text file encoding",选择other,然后选择utf-8
    在这里插入图片描述
    最后"apply and close",最后再重新运行,这下终于可以了:
    在这里插入图片描述
    FileSystem是一个通用的文件系统API,FileSystem实例有下列几个静态工厂方法用来构造对象。

    public static FileSystem get(Configuration conf)throws IOException  
    public static FileSystem get(URI uri,Configuration conf)throws IOException  
    public static FileSystem get(URI uri,Configuration conf,String user)throws IOException  
    

    Configuration对象封装了客户端或服务器的配置,通过设置配置文件读取类路径来实现(如:/etc/hadoop/core-site.xml)。
    (1) 第一个方法返回的默认文件系统是在core-site.xml中指定的,如果没有指定,就使用默认的文件系统。
    (2) 第二个方法使用给定的URI方案和权限来确定要使用的文件系统,如果给定URI中没有指定方案,则返回默认文件系统,
    (3) 第三个方法作为给定用户来返回文件系统,这个在安全方面来说非常重要。

  4. FSDataInputStream对象
    实际上,FileSystem对象中的open()方法返回的就是FSDataInputStream对象,而不是标准的java.io类对象。这个类是继承了java.io.DataInputStream的一个特殊类,并支持随机访问,由此可以从流的任意位置读取数据。

    在有了FileSystem实例之后,我们调用open()函数来获取文件的输入流。

    public FSDataInputStream open(Path p)throws IOException  
    public abstract FSDataInputStream open(Path f,int bufferSize)throws IOException  
    

    使用FSDataInputStream获取HDFS的/user/tmp/目录下的task.txt的文件内容,并输出
    首先我们在本地创建task.txt文件,写入"怕什么真理无穷,进一寸有一寸的欢喜。",然后上传到hdfs
    在这里插入图片描述
    在eclipse中编写代码:
    在这里插入图片描述

    成功输出:
    在这里插入图片描述

3. HDFS-JAVA接口之上传文件

FSDataOutputStream对象
FileSystem类有一系列新建文件的方法,最简单的方法是给准备新建的文件制定一个path对象,然后返回一个用于写入数据的输出流:

public FSDataOutputStream create(Path p)throws IOException  

该方法有很多重载方法,允许我们指定是否需要强制覆盖现有文件,文件备份数量,写入文件时所用缓冲区大小,文件块大小以及文件权限。

注意:create()方法能够为需要写入且当前不存在的目录创建父目录,即就算传入的路径是不存在的,该方法也会为你创建一个目录,而不会报错。如果有时候我们并不希望它这么做,可以先用exists()方法先判断目录是否存在。

我们在写入数据的时候经常想要知道当前的进度,API也提供了一个Progressable用于传递回调接口,这样我们就可以很方便的将写入datanode的进度通知给应用了。

package org.apache.hadoop.util;  
public interface Progressable{  
    public void progress();  
}  

在本地目录下创建test2.txt文件,并输入如下数据,注意这里是在Windows的本地目录下创建文件:

迢迢牵牛星,皎皎河汉女。
纤纤擢素手,札札弄机杼。
终日不成章,泣涕零如雨。
河汉清且浅,相去复几许?
盈盈一水间,脉脉不得语。
《迢迢牵牛星》

在这里插入图片描述在这里插入图片描述

使用FSDataOutputStream对象将文件上传至HDFS的/user/tmp/目录下,并打印进度。
编写代码如下:

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;


public class FileSystemUpload {
	
	public static void main(String[] args) throws IOException {
		//路径最好写绝对路径,要不然容易报找不到文件的错误
		File localPath = new File("E:\\Users\\cl\\eclipse-workspace\\hadoop\\src\\unit2\\test2.txt");
		//这里是hdfs的路径
		String hdfsPath = "hdfs://192.168.*.*:8088/user/tmp/test2.txt";
		//获取输入对象
		InputStream in = new BufferedInputStream(new FileInputStream(localPath));
		
		Configuration config=new Configuration();
		FileSystem fs = null;
		try {
			//最后一个参数是指以什么身份上传文件,如果不写默认以你创建的用户的身份上传
			fs = FileSystem.get(URI.create(hdfsPath), config, "root");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		//待上传文件大小
		long fileSize = localPath.length() > 65536 ? localPath.length() / 65536 : 1;
		FSDataOutputStream out = fs.create(new Path(hdfsPath), new Progressable() {
			//方法在每次上传了64KB字节大小的文件之后会自动调用一次
			long fileCount = 0;
			public void progress() {
				System.out.println("总进度"+(fileCount / fileSize)*100+"%");
				fileCount++;
			}
		});
		//最后一个参数的意思是使用完之后是否关闭流
		IOUtils.copyBytes(in, out, 2048, true);
	}
}

运行结果:
在这里插入图片描述

4. HDFS-JAVA接口之删除文件

  1. 列出文件
    在HDFS的API中就提供了listStatus()方法来实现该功能。

    public FileStatus[] listStatus(Path f)throws IOException  
    public FileStatus[] listStatus(Path f,PathFilter filter)throws IOException  
    public FileStatus listStatus(Path[] files)throws IOException  
    public FileStatus() listStatus(Path[] files,PathFilter filter)throws IOException  
    

    当传入参数是一个文件时,他会简单的转变成以数组方式返回长度为1的FileStatus对象,当传入参数是一个目录时,则返回0或多个FileStatus对象,表示此目录中包含的文件和目录。

    现在我们使用listStatus()方法来列出hdfs根目录下的文件夹与user目录下的文件夹。
    在这里插入图片描述

    运行结果:
    在这里插入图片描述

  2. 删除文件
    使用FileSystem的delete()方法可以永久性删除文件或目录。

    public boolean delete(Path f,boolean recursive)throws IOException  
    

    如果f是一个文件或者空目录,那么recursive的值可以忽略,当recursize的值为true,并且p是一个非空目录时,非空目录及其内容才会被删除(否则将会抛出IOException异常)。
    我们来创建两个目录,一个是空目录,一个是非空目录
    在这里插入图片描述
    在这里插入图片描述

    然后编写代码将这两个目录删除
    在这里插入图片描述

    运行结果:
    在这里插入图片描述

    在这里插入图片描述

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐