本文发表于本人博客

前面几次讲了关于Hadoop的环境搭建、HDFS操作,今天接着继续。本来Hadoop源码中就有一个例子WordCount,但是今天我们来自己实现一个加深对这个Mapper、Reducer的理解,如有不对欢迎指正。

我们先来梳理一下思路,对于自定义Mapper以及Reducer,我们先要覆盖其map以及reduce函数,然后按照相关步骤比如设置输入文件目录、输入文件格式化类、设置自定义Mapper、分区、排序、分组、规约、设置自定义Reducer等等。这里我们把输入文件的使用空格分割(也可以用制表符来),下面是自定义Mapper类MyMapper:

import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Mapper.Context; public class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable> { @Override
protected void map(LongWritable key, Text value,Context context) throws IOException, InterruptedException {
String[] splied = value.toString().split(" ");
for (int i = 0; i < splied.length; i++) {
String lineWord = splied[i];
context.write(new Text(lineWord), new LongWritable(1));
}
}
}

这里我选择的是新的API,相关库基本是在org.apache.hadoop.mapreduce下,旧API是在org.apache.hadoop.mapred下,包括一些引用库也是这样。自定义MyMapper是泛型继承Mapper,其中参数key\value是Hadoop内部类型,它不支持java的基本类型这里我们需要注意下为什么不选择java的基本类型呢,原因是不需要其它额外是操作,而且本身需要序列化反序列化并提升其性能所以加入了hadoop的类型放弃java的基本类型。关于hadoop key\value跟java基本类型相互转换的问题也很简单,从java基本类型转换至hadoop的key\value的话直接new带参就可以了,从hadoop的key\value类型转换至java的基本类型使用get方法就可以了!如:

LongWritable lw = new LongWritable(1L);
long temp = lw.get();

接下来继续看自定义Reducer类MyReduce:

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.Reducer.Context; public class MyReduce extends Reducer<Text, LongWritable, Text, LongWritable> { @Override
protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
long count = 0L;
for(LongWritable value: values) {
count += value.get();
}
context.write(key, new LongWritable(count));
}
}

这个跟上面类似了,再来看看main方法的如何执行的!

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.InputFormat;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.mapreduce.lib.partition.HashPartitioner; import com.sun.org.apache.xpath.internal.axes.HasPositionalPredChecker; public class Test {
static final String OUTPUT_DIR = "hdfs://hadoop-master:9000/mapreduce/output/";
static final String INPUT_DIR = "hdfs://hadoop-master:9000/mapreduce/input/test.txt"; public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = new Job(conf, Test.class.getSimpleName());
deleteOutputFile(OUTPUT_DIR); //1设置输入目录
FileInputFormat.setInputPaths(job, INPUT_DIR);
//2设置输入格式化类
job.setInputFormatClass(TextInputFormat.class);
//3设置自定义Mapper以及键值类型
job.setMapperClass(MyMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
//4分区
job.setPartitionerClass(HashPartitioner.class);
job.setNumReduceTasks(1);
//5排序分组
//6设置在自定义Reduce以及键值类型
job.setReducerClass(MyReduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
//7设置输出目录
FileOutputFormat.setOutputPath(job, new Path(OUTPUT_DIR));
//8提交job
job.waitForCompletion(true);
} static void deleteOutputFile(String path) throws Exception{
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(new URI(INPUT_DIR),conf);
if(fs.exists(new Path(path))){
fs.delete(new Path(path));
}
}
}

执行的时候先会输出上次执行过的输出目录。然后就按照步骤:

1.设置输入文件目录;
2.输入文件格式化类;
3.设置自定义Mapper以及其键值类型;
4.分区;
5.排序;
6.分组;
7.规约;
8.设置自定义Reducer以及其键值类型;
9.设置输出目录;
10.代码提交至JobTracker。

当然这过程中有些是可以省略的比如输出文件格式化类。从这个例子我们可以得出:既然可以设置自定义Mapper以及自定义Reducer,那么也应该可以设置自定义的输入文件格式化类以及分区、排序、分组、规约等等,这个以后会有相关的笔记现在这里只是写个简单的例子。我们编写一个文件如下并把它上传至hdfs://hadoop-master:9000/mapreduce/input/test.txt:

luoliang me
asura asura.com luoliang
me

然后执行main函数,将会在hdfs://hadoop-master:9000/mapreduce/output/目录下输出一个类似part-*的文件,我们可以使用如下命令查看:

hadoop fs -text /output/part-*

此时会输出:

asura 1
asura.com 1
luoliang 2
me 2

现在文件是输出了也对比下是正确,但是脑子还是一片空白,不知道其怎么做到的,那么这个就是关于mapreduce的原理了,下面我也说说大概其原理:从把代码提交至JobTracker开始,它就会从指定的输入文件路径去获取文件,这里支持多个文件以及二级目录下的多个文件,这里获取就是使用的HDFS api来操作了!把所有文件读取出来之后按照指定的大小进行分割InputSplit,把分割好后的键值FileSplit(比如:<0,"luoliang me">,<13,"asura asura.com luoliang">)再转化为RecordReader(比如<"luoliang",1>,<"luoliang",1>),此时全部转换完毕后会每个都调用map函数,map函数把数据写入到Mapper.Context中,再会对数据进行分区排序分组规约,最后通过shuffle到达reduce端,这其中每个map的输出数量是等于reduce的输入数量。到达reduce端数据已经发生了质变了不在是<"luoliang",1>而是类似变成<"luoliang",{1,1}>这样的键值数据,这是我们需要迭代获取总数量并在写会context中,计算完后输出到指定的目录。在这里由于有重复的单词所以map函数的调用次数跟reduce函数调用次数是不同的。规约这个其实就是自定义reduce,但是这个不是必须有的因为如果是统计关于类似平均数的问题,数据在map端进行规约了,虽然传送时间以及处理时间减少性能提升了但是对于最终结果可能会有影响,所以这个规约要看具体情况才能使用。至于这个shuffle一步还不是怎么了解需要多多再看看。

这次先到这里。坚持记录点点滴滴!

Hadoop 编写WordCount的更多相关文章

  1. 大数据之路week07--day03(Hadoop深入理解,JAVA代码编写WordCount程序,以及扩展升级)

    什么是MapReduce 你想数出一摞牌中有多少张黑桃.直观方式是一张一张检查并且数出有多少张是黑桃. MapReduce方法则是: 1.给在座的所有玩家中分配这摞牌 2.让每个玩家数自己手中的牌有几 ...

  2. hadoop的wordcount例子运行

    可以通过一个简单的例子来说明MapReduce到底是什么: 我们要统计一个大文件中的各个单词出现的次数.由于文件太大.我们把这个文件切分成如果小文件,然后安排多个人去统计.这个过程就是”Map”.然后 ...

  3. indows Eclipse Scala编写WordCount程序

    Windows Eclipse Scala编写WordCount程序: 1)无需启动hadoop,因为我们用的是本地文件.先像原来一样,做一个普通的scala项目和Scala Object. 但这里一 ...

  4. 5行代码怎么实现Hadoop的WordCount?

    初学编程的人,都知道hello world的含义,当你第一次从控制台里打印出了hello world,就意味着,你已经开始步入了编程的大千世界,这和第一个吃螃蟹的人的意义有点类似,虽然这样比喻并不恰当 ...

  5. Hadoop中wordcount程序

    一.测试过程中 输入命令: 首先需要在hadoop集群中添加文件 可以首先进行查看hadoop集群中文件目录 hadoop fs -ls / hadoop fs -ls -R / hadoop fs ...

  6. [Linux][Hadoop] 运行WordCount例子

    紧接上篇,完成Hadoop的安装并跑起来之后,是该运行相关例子的时候了,而最简单最直接的例子就是HelloWorld式的WordCount例子.   参照博客进行运行:http://xiejiangl ...

  7. 伪分布式环境下命令行正确运行hadoop示例wordcount

    首先确保hadoop已经正确安装.配置以及运行. 1.     首先将wordcount源代码从hadoop目录中拷贝出来. [root@cluster2 logs]# cp /usr/local/h ...

  8. 一个可以跑的Hadoop的WordCount程序

    搭个新环境时总要折腾一下,于是干脆记下来. 程序: package com.my; import java.io.IOException; import java.util.Iterator; imp ...

  9. hadoop执行wordcount例子

    1:下载hadoop.http://mirror.esocc.com/apache/hadoop/common/hadoop-1.2.1/hadoop-1.2.1.tar.gz 2:解压. tar - ...

随机推荐

  1. Oracle Apex 有用笔记系列 2 - 文件上传管理

    1. 页面设计 页面A有若干region, 当中一个region用于文件列表管理(包含显示,下载.删除).如图A. 在页面A有一button,点击它会调用页面B,页面B负责文件上传.如图B. 图A 图 ...

  2. stm32入门(从51过渡到32)

    单片机对于我来说,就是一个超级大机器,上面有一排一排数不尽的开关,我需要做的,就是根据我的设计,拿着一张超级大的表(Datasheet),把需要的开关(reg)都开关(config)到对应功能的位置( ...

  3. C语言之选择结构

    该章内容:本章我们学习三大结构之一:选择结构,采用选择结构来解决问题称为判断问题,它的求解规则是在不同的条件下进行不同的操作.选择结构比顺序结构要复杂一些.本章是考试的重点章节. 学习方法:先了解选择 ...

  4. C++11新特性之二——std::bind std::function 高级用法

    /* * File: main.cpp * Author: Vicky.H * Email: eclipser@163.com */ #include <iostream> #includ ...

  5. cocos2d-x游戏引擎核心之十一——并发编程(消息通知中心)

    [续] cocos2d-x游戏引擎核心之八——多线程 这里介绍cocos2d-x的一种消息/数据传递方式,内置的观察者模式,也称消息通知中心,CCNotificationCenter. 虽然引擎没有为 ...

  6. 图片上传根据stream生成image

    对于图片上传代码的整合 因为需要判断上传的图片的宽高是否符合尺寸,所以在最初拿到inputstream的时候,就直接获取image格式的图片 本来是想在下面的checkFile中获取的,不过直接使用S ...

  7. poj_2182 线段树/树状数组

    题目大意 n个数排成一排(不知道大小,只是占了一个位置),从a[1]到a[n]进行遍历,对于每个a[i],给出从a[1]到a[i-1]中小于a[i]数的个数.要求出 a[1]到a[n]中这n个数的相对 ...

  8. Docker源码分析(六):Docker Daemon网络

    1. 前言 Docker作为一个开源的轻量级虚拟化容器引擎技术,已然给云计算领域带来了新的发展模式.Docker借助容器技术彻底释放了轻量级虚拟化技术的威力,让容器的伸缩.应用的运行都变得前所未有的方 ...

  9. LeetCode——Lowest Common Ancestor of a Binary Search Tree

    Description: Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given no ...

  10. Android 系统镜像: boot.img kernel.img ramdisk.img system.img userdata.img cache.img recovery.img

    boot.img(kernel.img+ramdisk.img) ramdisk.img(/) system.img(/system) userdata.img(/data) cache.img(/c ...