Java笔记---Hadoop 2.7.1下WordCount程序详解

一、前言

在之前我们已经在 CenOS6.5 下搭建好了 Hadoop2.x 的开发环境。既然环境已经搭建好了,那么现在我们就应该来干点正事嘛!比如来一个Hadoop世界的HelloWorld,也就是WordCount程序(一个简单的单词计数程序)

二、WordCount 官方案例的运行

2.1 程序简介

WordCount程序是hadoop自带的案例,我们可以在 hadoop 解压目录下找到包含这个程序的 jar 文件(hadoop-mapreduce-examples-2.7.1.jar),该文件所在路径为 hadoop/share/hadoop/mapreduce。

我们可以使用 hadoop jar 命令查看该jar包详细信息。执行命令:hadoop jar hadoop-mapreduce-examples-2.7.1.jar

可以看到,该 jar 文件中并不止有一个案例,当然我们此时只想看看 WordCount 程序,其他的靠边边。那么我们按照提示,执行命令:hadoop jar hadoop-mapreduce-examples-2.7.1.jar wordcount 看看有什么东西?

根据提示,它说是需要输入文件和输出目录,那么接下来,我们就准备以下输入文件和输出目录吧。

注:其实只需要准备输入文件,不需要准备输入目录。因为 MapReduce 程序的运行,其输入目录不能是已存在的,否则会抛出异常。这是为了避免数据覆盖的问题。请看《Hadoop权威指南》

2.2 准备材料

为了方便使用该官方 jar 文件,我们在当前目录下创建一个 input 目录(你也可以在别的目录下创建目录,目录名也可以自己取,喜欢就好),用来存放输入文件。然后准备2个输入文件。如下所示:

因为我们是使用 HDFS 文件系统的,所以我们要运行 WordCount 这个 MapReduce 程序的话,需要将文件放入 HDFS 上。因此我们使用 HDFS 的文件系统命令,在HDFS文件系统根目录下创建一个input目录,用来保存输入文件。执行命令:hadoop fs -mkdir /input

注:hadoop fs -mkdir 命令是用来在 HDFS 上创建目录的,类似于Linux下的 mkdir 命令

目录创建好后,我们需要把刚刚在本地文件系统上准备的输入文件拷贝到 HDFS 上。执行命令:hadoop fs -put input/f*.txt /input

2.3 运行程序

准备工作就绪了,那么现在就开始运行程序了。执行命令:hadoop jar hadoop-mapreduce-examples-2.7.1.jar wordcount /input /output

注:hadoop jar hadoop-mapreduce-examples-2.7.1.jar wordcount /input /output详解
- 该命令中 /input 表示使用 HDFS 上根目录(/)下的 input 目录下所有文件作为程序输入
- /output 表示使用 HDFS 根目录下的 output 目录存储程序的输出(该 output 文件,是本来不存在的,会由程序自动创建)

从终端可以看到如下命令输出:

程序运行完毕,我们看一下输出都有啥,执行命令:hadoop fs -cat /output/*

注:hadoop fs -cat 命令功能类似于linux下的 cat 命令

从上面的输出,可以看到该程序将我们的输入文件中的单词出现情况,进行了统计。都是 key,value 的形式出现的

三、WordCount 官方程序的源码分析

3.1 查看源码

刚刚已经运行WordCount程序爽了一把,现在我们通过查看源码来看看该程序的真面目。

我们使用 Eclipse 来查看源码。在 hadoop-mapreduce-examples-2.7.1.jar 文件所在目录中,有一个 source 目录,其中就存有该 jar 对应的 hadoop-mapreduce-examples-2.7.1-sources.jar。通过 Eclipse 查看的情况如下:

可以看到该程序很简单,代码量很少。其中内置了2个内部类,分别继承字 Mapper 和 Reducer 类。这其实就是编写 MapReduce 程序时,我们需要进行的工作。

3.2 分析程序结构

程序大概结构如下:(此处只简单说一下,待会儿在自实现的 WordCount 中进行详细的代码注释)

① Mapper 区:继承自 Mapper 类的一个内部类,实现 map() 函数

② Reducer 区:继承自 Reducer 类的一个内部类,实现 reduce() 函数

③ Client 区:程序运行入口

这个结构,也就是 MapReduce 程序编写的基本结构了。编写一个 MapReduce 程序,我们程序猿只需要实现 map() 和 reduce() 程序。

3.3 自实现 WordCount

我们按照 MapReduce 程序的编写情况,实现一个我们自己的 WordCount 程序。

① 创建一个 Java Project

② 导入如下 jar 包

注:其中 hadoop-hdfs-2.7.1.jar 和 hadoop-mapreduce-client-core-2.7.1.jar 是编写程序需要的 jar 包,其他的 jar 包是我们在 linux 下运行程序需要的依赖 jar,也就是环境需要的 jar 包。

③ 编写程序:WordCount.java

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.LongWritable;
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;

/**
 * WordCount:MapReduce初级案例,按八股文的结构遍写
 * @author johnnie
 *
 */
public class WordCount {

    /**
     * Mapper区: WordCount程序 Map 类
     * Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>:
     *        |       |           |             |
     *  输入key类型  输入value类型      输出key类型 输出value类型
     * @author johnnie
     *
     */
    public static class TokenizerMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
        // 输出结果
        private Text word = new Text();                             // KEYOUT
        // 因为若每个单词出现后,就置为 1,并将其作为一个<key,value>对,因此可以声明为常量,值为 1
        private final static IntWritable one = new IntWritable(1);  // VALUEOUT

        /**
         * value 是文本每一行的值
         * context 是上下文对象
         */
        @Override
        public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            // 获取每行数据的值
            String lineValue = value.toString();
            // 分词:将每行的单词进行分割,按照"  \t\n\r\f"(空格、制表符、换行符、回车符、换页)进行分割
            StringTokenizer tokenizer = new StringTokenizer(lineValue);
            // 遍历
            while (tokenizer.hasMoreTokens()) {
                // 获取每个值
                String wordValue = tokenizer.nextToken();
                // 设置 map 输出的 key 值
                word.set(wordValue);
                // 上下文输出 map 处理结果
                context.write(word, one);
            }
        }
    }

    /**
     * Reducer 区域:WordCount 程序 Reduce 类
     * Reducer<KEYIN, VALUEIN, KEYOUT, VALUEOUT>:Map 的输出类型,就是Reduce 的输入类型
     * @author johnnie
     *
     */
    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;                        // 累加器,累加每个单词出现的总次数
            // 遍历values
            for (IntWritable val : values) {
                sum += val.get();               // 累加
            }
            // 设置输出 value
            result.set(sum);
            // 上下文输出 reduce 结果
            context.write(key, result);
        }
    }

    // Driver 区:客户端
    public static void main(String[] args) throws Exception {
        // 获取配置信息
        Configuration conf = new Configuration();
        // 创建一个 Job
        Job job = Job.getInstance(conf, "word count");      // 设置 job name 为 word count
//      job = new Job(conf, "word count");                  // 过时的方式
        // 1. 设置 Job 运行的类
        job.setJarByClass(WordCount.class);

        // 2. 设置Mapper类和Reducer类
        job.setMapperClass(TokenizerMapper.class);
        job.setReducerClass(IntSumReducer.class);   

        // 3. 获取输入参数,设置输入文件目录和输出文件目录
        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        // 4. 设置输出结果 key 和 value 的类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
//      job.setCombinerClass(IntSumReducer.class);

        // 5. 提交 job,等待运行结果,并在客户端显示运行信息,最后结束程序
        boolean isSuccess = job.waitForCompletion(true);

        // 结束程序
        System.exit(isSuccess ? 0 : 1);
    }

}

④ 编写 Main.java:我们配置一个程序执行入口,方便以后添加其他的示例,做成官方 jar 的样子

⑤ 我们将该项目打包成可执行 jar包:此处我将该 jar 取名为 WordCount.jar

⑥ 利用 FileZilla 将该 jar 包上传到 CenOS6.5 上

⑦ 利用 hadoop jar 命令来执行程序。执行命令:hadoop jar wordcount.jar WordCount ./data/f*.txt /out

注:这种传参方式就是 Linux下参数传递的方式。这里也介绍了我们平时在 Java 程序中 main() 中 args 的使用--就是来接受命令行参数的。

具体的步骤,还是和刚刚一样,准备输入文件,以及指定输出目录。输入文件和输出目录的路径都是在 HDFS 文件系统上的。./data/f*.txt 也和刚刚的 f1.txt 和 f2.txt 差不多。输出结果也不多说了,也差不多。

时间: 2024-10-11 21:33:40

Java笔记---Hadoop 2.7.1下WordCount程序详解的相关文章

Hadoop下面WordCount运行详解

单词计数是最简单也是最能体现MapReduce思想的程序之一,可以称为MapReduce版"Hello World",该程序的完整代码可以在Hadoop安装包的"src/examples"目录下找到.单词计数主要完成功能是:统计一系列文本文件中每个单词出现的次数,如下图所示. 现在我们以"hadoop"用户登录"Master.Hadoop"服务器. 1. 创建本地的示例数据文件: 依次进入[Home]-[hadoop]-[ha

第130讲:Hadoop集群管理工具DataBlockScanner 实战详解学习笔记

第130讲:Hadoop集群管理工具DataBlockScanner 实战详解学习笔记 DataBlockScanner在datanode上运行的block扫描器,定期检测当前datanode节点上所有的block,从而在客户端读到有问题的块前及时检测和修复有问题的块. 它有所有维护的块的列表,通过对块的列表依次的扫描,查看是否有校验问题或错误问题,它还有截流机制. 什么叫截流机制?DataBlockScanner扫描时会消耗大量的磁盘带宽,如果占用磁盘带宽太大,会有性能问题.所以它会只占用一小

【linux_笔记】Linux_文件查找(find)详解&&特殊权限

学习资源来自:www.magedu.com 学习记录过程中难免出现错误,如有发现,还望大神们指出. 示例操作部分有的与历史操作有关,如果先前的示例操作没有执行过的话,可能会有部分示例的操作无法执行.示例仅供参考(练习题在附录). 文件查找: locate(不常用):非实时,模糊匹配,根据全系统文件数据库进行查找,速度快:# updatedb, 手动生成文件数据库(非常耗时) find:实时,精确,支持众多查找标准,遍历指定目录中的所有文件完成查找,速度慢: 命令格式:find 查找路径 查找标准

Linux 程序设计学习笔记----终端及串口编程基础之概念详解

转载请注明出处,谢谢! linux下的终端及串口的相关概念有: tty,控制台,虚拟终端,串口,console(控制台终端)详解 部分内容整理于网络. 终端/控制台 终端和控制台都不是个人电脑的概念,而是多人共用的小型中型大型计算机上的概念. 1.终端 一台主机,连很多终端,终端为主机提供了人机接口,每个人都通过终端使用主机的资源. 终端有字符哑终端和图形终端两种. 控制台是另一种人机接口, 不通过终端与主机相连, 而是通过显示卡-显示器和键盘接口分别与主机相连, 这是人控制主机的第一人机接口.

java中静态代码块的用法 static用法详解

(一)java 静态代码块 静态方法区别一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的;需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的. 静态方法在类加载的时候 就已经加载 可以用类名直接调用比如main方法就必须是静态的 这是程序入口两者的区别就是:静态代码块是自动执行的;静态方法是被调用的时候才执行的.静态方法(1)在Java里,可以定义一个不需要创建对象的方法,这种方法就是

笔记-【3】-event事件对象的详解!

event事件对象:是指当前对象发生的事件的一些详细的信息在event这个对象里. event对象从哪里来?从事件函数中传入 obj. //e就会当前的事件对象event } 对象就有属性和方法:那么event对象也有属性和方法 event的属性和方法: { 属性: button :  当前事件的方法中判断鼠标的按键位置 有三个值: 0 (左键) 1(滚轮) 2(右键) ctrlkey:  判断是否按下了ctrl键; altkey:  判断是否按下了alt键; shiftkey:  判断是否按下

iOS回顾笔记(04) -- UIScrollView的基本使用详解

html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption

JAVA环境变量JAVA_HOME、CLASSPATH、PATH设置详解

JAVA环境变量JAVA_HOME.CLASSPATH.PATH设置详解 Windows下JAVA用到的环境变量主要有3个,JAVA_HOME.CLASSPATH.PATH.下面逐个分析. JAVA_HOME 指向的是JDK的安装路径,如C:\jdk1.5.0_06,在这路径下你应该能够找到bin.lib等目录.值得一提的是,JDK的安装路径可以选择任意磁盘目录,不过建议你放的目录层次浅一点,如果你放的目录很深,比如x:\XXXXXX\xxxxx\XXXX\xxxx\XXXX\xxxx\XXXX

java开源框架SpringSide3多数据源配置的方法详解

原创整理不易,转载请注明出处:java开源框架SpringSide3多数据源配置的方法详解 代码下载地址:http://www.zuidaima.com/share/1781579130801152.htm 在SpringSide 3社区中,不断有人提出多数据源配置的问题,但是时至今日却一直没有一个完美的答案.经过一个星期的折腾,我总算搞清楚了在SpringSide 3中配置多数据源的各种困难并加以解决,在这里,特地把我配置SpringSide 3项目中多数据源的过程写出来,与大家分享. 我使用