【大数据学习】HDFS编程入门实践

Hadoop分布式文件系统(Hadoop Distributed File System,HDFS)是Hadoop核心组件之一,如果已经安装了Hadoop(我已经安装了,安装过程->传送门),其中就已经包含了HDFS组件,不需要另外安装,可以做HDFS的编程实践了。这里主要参考林子雨老师的博客。

这里涉及的知识点比较多:分布式文件系统、HDFS简介、HDFS的相关概念、HDFS体系结构、HDFS的存储原理、HDFS的数据读写过程。

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

在开始学习HDFS编程实践前,需要启动Hadoop(版本是Hadoop3.1.3)。执行如下命令

start-dfs.sh #启动hadoop

一、利用Shell命令与HDFS进行交互

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

在终端输入如下命令,查看fs总共支持了哪些命令:

hadoop fs

1. 目录操作

注意

Hadoop系统安装好以后,第一次使用HDFS时,需要首先在HDFS中创建用户目录。这里采用hadoop用户登录Linux系统,因此,需要在HDFS中为hadoop用户创建一个用户目录,命令如下:

hdfs dfs -mkdir -p /user/hadoop 

参考上一节的伪分布式实例,如果之前已经创建好用户目录,这里就不需要再创建。

“-ls”列出HDFS某个目录下的所有内容

hdfs dfs -ls . # “.”表示HDFS中的当前用户目录,现在就是“/user/hadoop”目录
hdfs dfs -ls /user/hadoop # 现在这两条命令是等效的,选择一个即可

如果要列出HDFS上的所有目录,可以使用如下命令:

hdfs dfs -ls

2. 文件操作

在实际应用中,经常需要从本地文件系统向HDFS中上传文件,或者把HDFS中的文件下载到本地文件系统中。
首先,使用vim编辑器,在Linux文件系统的“/home/hadoop/”目录下创建一个文件myLocalFile.txt,里面可以随意输入一些单词,比如,输入如下三行:

Hadoop
Spark
XMU DBLAB

然后,可以使用如下命令把本地文件系统的“/home/hadoop/myLocalFile.txt”上传到HDFS中的当前用户目录的input目录下,也就是上传到HDFS的“/user/hadoop/input/”目录下:

hdfs dfs -put /home/hadoop/myLocalFile.txt  input

可以使用ls命令查看一下文件是否成功上传到HDFS中

hdfs dfs -ls input

显示类似如下的信息:

Found 10 items
......
-rw-r--r--   1 hadoop supergroup         23 2020-05-25 14:46 input/myLocalFile.txt
......

查看HDFS中的myLocalFile.txt这个文件的内容:

hdfs dfs -cat input/myLocalFile.txt

把HDFS中的myLocalFile.txt文件下载到本地文件系统中的“/home/hadoop/download/”这个目录下,命令如下:

mkdir /home/hadoop/download
hdfs dfs -get input/myLocalFile.txt  /home/hadoop/download

到本地文件系统查看下载下来的文件myLocalFile.txt:

cd /home/hadoop/download
ls
cat myLocalFile.txt

了解

把文件从HDFS中的一个目录拷贝到HDFS中的另外一个目录。

比如,如果要把HDFS的“/user/hadoop/input/myLocalFile.txt”文件,拷贝到HDFS的另外一个目录“/input”中(注意,这个input目录位于HDFS根目录下),可以使用如下命令:

hdfs dfs -cp input/myLocalFile.txt  /input

可以使用rm命令删除一个目录,比如,可以使用如下命令删除刚才在HDFS中创建的“/input”目录(不是“/user/hadoop/input”目录):

hdfs dfs -rm -r /input

二、利用Web界面管理HDFS

可以访问 Web 界面 http://NameNodeIp:9870 即可看到HDFS的web管理界面。比如我这里http://dxystudy.cn:9870 或者 http://47.115.49.250:9870。

web管理界面

三、利用Java API与HDFS进行交互

Hadoop不同的文件系统之间通过调用Java API进行交互,上面介绍的Shell命令,本质上就是Java API的应用。

Hadoop官方的Hadoop API文档:Hadoop API文档

在林子雨老师的教程中是使用虚拟机里的ecplise,显然我们用云服务器的话是不可行的,可以本地编程然后上传运行。

1. IDEA创建项目

可以导入hadoop目录下的依赖包,但是这样依赖管理很不方便,这里使用IDEA的创建的MAVEN项目,关于创建IDEA的Maven工程项目,比较简单,工程名可以自定义,这里取hadoopdemo,具体步骤这里不一步一步演示了。

2. 添加依赖

这里需要用到基础依赖hadoop-corehadoop-common,因为还需要读写HDFS,所以还需要依赖hadoop-hdfshadoop-client

在项目的pom.xml文件中加入以下依赖项:

<dependencies>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-core</artifactId>
        <version>1.2.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>3.1.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>3.1.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>3.1.3</version>
    </dependency>
</dependencies>

3. 编写、运行Java应用程序

(1)编写应用程序
1.1 词频统计

① 编写应用程序

src->main->java下新建一个WordCount类,添加内容

import java.io.IOException;
import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class WordCount {

    public static class TokenizerMapper
            extends Mapper<Object, Text, Text, IntWritable> {

        private final static IntWritable ONE = new IntWritable(1);
        private Text word = new Text();

        @Override
        public void map(Object key, Text value, Context context
        ) throws IOException, InterruptedException {
            StringTokenizer itr = new StringTokenizer(value.toString());
            while (itr.hasMoreTokens()) {
                word.set(itr.nextToken());
                context.write(word, ONE);
            }
        }
    }

    public static class IntSumReducer
            extends Reducer<Text, IntWritable, Text, IntWritable> {
        private IntWritable result = new IntWritable();

        @Override
        public void reduce(Text key, Iterable<IntWritable> values,
                           Context context
        ) throws IOException, InterruptedException {
            int sum = 0;
            for (IntWritable val : values) {
                sum += val.get();
            }
            result.set(sum);
            context.write(key, result);
        }
    }

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "word count");
        job.setJarByClass(WordCount.class);
        job.setMapperClass(TokenizerMapper.class);
        job.setCombinerClass(IntSumReducer.class);
        job.setReducerClass(IntSumReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

这个是官方的WordCount示例程序,不需要安装任何模式的Hadoop

② 配置输入文件

WordCount对输入文件字符进行计数,输出计数的结果。首先需要配置输入路径,这里在WordCount下(src同级目录)新建一个文件夹input,并添加一个或多个文本文件到input中,作为示例。

这里还有一件事情,点击File->Project Structure,在弹出来的对话框中选择Modules项,点击Sources选项卡,将Language level调整为8。可以在这里将input文件夹标记为Excluded

配置输入文件

③ 配置运行参数

这里需要配置此程序运行时的Main class,以及WordCount需要的输入输出路径。

在Intellij菜单栏中选择Run->Edit Configurations,在弹出来的对话框中点击+,新建一个Application配置。配置NameMain classWordCount(可以点击右边的...选择),Program argumentsinput/ output/,即输入路径为刚才创建的input文件夹,输出为output。确认后点击ok没成配置。

配置运行参数

④ 运行和调试

上述配置完成后,点击菜单栏Run->Run 'WordCount'即开始运行此MapReduce程序,Intellij下方会显示Hadoop的运行输出。待程序运行完毕后,Intellij左上方会出现新的文件夹output,其中的part-r-00000就是运行的结果了!

报错信息①:

IDEA Error:java: Compilation failed: internal java compiler error

解决办法很简单:File–>Setting…–>Build,Execution,Deployment–>Compiler–>Java Compiler 设置相应Module的target bytecode version的合适版本(跟你jkd版本一致),这里我改成1.8版本的。

报错信息②:

Failed to set permissions of path

这是因为当前用户没有权限来设置路径权限(Linux无此问题),一个解决方法是给hadoop打补丁,参考Failed to set permissions of path: tmp,因为这里使用的Maven,此方法不太适合。另一个方法是将当前用户设置为超级管理员(“计算机管理”,“本地用户和组”中设置),或以超级管理员登录运行此程序。

最好的解决方法是在Linux或macOS上跑hadoop。

事实上我也因为在windows运行出现太多错误了,所以还是打包到ubuntu上去的hadoop来运行。下面会讲。

1.2 文件合并

① 编写应用程序

src->main->java下新建一个MergeFile类,添加内容

import java.io.IOException;
import java.io.PrintStream;
import java.net.URI;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;

/**
 * 过滤掉文件名满足特定条件的文件 
 */
class MyPathFilter implements PathFilter {
    String reg;
    MyPathFilter(String reg) {
        this.reg = reg;
    }
    public boolean accept(Path path) {
        return !(path.toString().matches(reg));
    }
}
/***
 * 利用FSDataOutputStream和FSDataInputStream合并HDFS中的文件
 */
public class MergeFile {
    /**
     * 待合并的文件所在的目录的路径
     */
    Path inputPath;

    /**
     * 输出文件的路径
     */
    Path outputPath;

    public MergeFile(String input, String output) {
        this.inputPath = new Path(input);
        this.outputPath = new Path(output);
    }

    public void doMerge() throws IOException {
        Configuration conf = new Configuration();
        conf.set("fs.defaultFS","hdfs://localhost:9000");
        conf.set("fs.hdfs.impl","org.apache.hadoop.hdfs.DistributedFileSystem");
        FileSystem fsSource = FileSystem.get(URI.create(inputPath.toString()), conf);
        FileSystem fsDst = FileSystem.get(URI.create(outputPath.toString()), conf);
        //下面过滤掉输入目录中后缀为.abc的文件
        FileStatus[] sourceStatus = fsSource.listStatus(inputPath,
                new MyPathFilter(".*\\.abc"));
        FSDataOutputStream fsdos = fsDst.create(outputPath);
        PrintStream ps = new PrintStream(System.out);
        //下面分别读取过滤之后的每个文件的内容,并输出到同一个文件中
        for (FileStatus sta : sourceStatus) {
            //下面打印后缀不为.abc的文件的路径、文件大小
            System.out.print("路径:" + sta.getPath() + "    文件大小:" + sta.getLen()
                    + "   权限:" + sta.getPermission() + "   内容:");
            FSDataInputStream fsdis = fsSource.open(sta.getPath());
            byte[] data = new byte[1024];
            int read;

            while ((read = fsdis.read(data)) > 0) {
                ps.write(data, 0, read);
                fsdos.write(data, 0, read);
            }
            fsdis.close();
        }
        ps.close();
        fsdos.close();
    }
    public static void main(String[] args) throws IOException {
//        MergeFile merge = new MergeFile(
//                "hdfs://localhost:9000/user/hadoop/",
//                "hdfs://localhost:9000/user/hadoop/merge.txt");
        MergeFile merge = new MergeFile(args[0], args[1]);
        merge.doMerge();
    }
}
(2) 部署运行

在部署运行前确定Hadoop已经运行了。如果没有,则需要启动,命令如下

start-dfs.sh

下面介绍如何把Java应用程序生成JAR包,部署到Hadoop平台上运行。

首先,在Hadoop安装目录下新建一个名称为myapp的目录,用来存放编写的Hadoop应用程序,可以在Linux的终端中执行如下命令:

cd $HADOOP_HOME
mkdir myapp

将Maven工程编译打包好的jar包文件上传到该目录上。查看一下

cd /usr/local/hadoop/myapp
ls

上传应用文件

  • 运行词频统计程序

    如果做过上一节的实例并且没有清除掉input里的文件,可以运行下面代码查看结果:

    hadoop jar hadoopdemo-1.0-SNAPSHOT.jar WordCount input output
    hdfs dfs -ls output
    hdfs dfs -cat output/part-r-00000
    

运行结果

  • 运行文件合并程序

    运行文件合并程序

    在一个目录下,这里用/home/hadoop/MergeFileTest目录

    cd /home/hadoop
    mkdir MergeFileTest
    

    使用vim新建文件file1.txt、file2.txt、file3.txt、file4.abc和file5.abc,每个文件里面有内容。这里,假设文件内容如下:
    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

    将这些文件提交到HDFS上

    hdfs dfs -mkdir input-MegeFile
    hdfs dfs -put /home/hadoop/MergeFileTest/*  input-MegeFile
    

    运行程序

    hadoop jar hadoopdemo-1.0-SNAPSHOT.jar MergeFile input-MegeFile merge.txt
    

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

    hdfs dfs -cat /user/hadoop/merge.txt
    

    可以看到如下结果:

    this is file1.txt
    this is file2.txt
    this is file3.txt
    

    在HDFS的“/user/hadoop”目录下新建

由于Hadoop的设定,下次运行时务必删除output文件夹!

清除之前运行后的结果记录

hdfs dfs -rm -r output

四、其他

  1. 分布式文件系统设计的需求

  2. 分布式文件系统如何实现较高水平扩展的

  3. HDFS中的块和普通文件系统中的块的区别

  4. HDFS中的名称节点和数据节点的具体功能

  5. 在分布式文件系统中,中心节点的设计至关重要,HDFS是如何减轻中心节点的负担的。

  6. HDFS只设置唯一一个名称结点,在简化系统设计的同时也带来了一些明显的局限性,局限性具体表现在哪些方面?

  7. HDFS冗余数据保存策略

  8. 数据复制主要是在数据写入和数据恢复的时候发生,HDFS数据复制是使用流水线复制的策略。该策略的细节?

  9. HDFS是如何探测错误发生以及如何进行恢复的。

  10. HDFS在不发生故障的情况下读、写文件的过程。

参考资料

[1] 林子雨. HDFS编程实践(Hadoop3.1.3)

[2] Hadoop: Intellij结合Maven本地运行和调试MapReduce程序 (无需搭载Hadoop和HDFS环境)

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值