1. 开发环境
=========
2. 数据准备
=========
3. 配置eclipse
=========
用hadoop搞map-reduce自然要用java,用java自然要用eclipse
mapper在我们求平均分的例子中是将每一行数据拆分成<键:学号,值:分数>的对以交给reducer使用
reducer从mapper收到整理好的键值对,其中键是每一个学生的学号,值是一个列表,里面有这个学生的所有成绩
=========
hadoop:
Hadoop 1.1.2
Subversion https://svn.apache.org/repos/asf/hadoop/common/branches/branch-1.1 -r 1440782
Compiled by hortonfo on Thu Jan 31 02:08:44 UTC 2013
From source with checksum c720ddcf4b926991de7467d253a79b8b
java:
java version "1.6.0_27"
OpenJDK Runtime Environment (IcedTea6 1.12.5) (6b27-1.12.5-0ubuntu0.12.04.1)
OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)
os:
Distributor ID: Ubuntu
Description: Ubuntu 12.04.2 LTS
Release: 12.04
Codename: precise
eclipse:
Eclipse Platform
Version: 3.7.2
Build id: I20110613-17362. 数据准备
=========
要做map-reduce,得先准备点数据。
在这个例子中,我产生了1亿条,1000个学生的考试成绩数据,学生的学号是从1 - 1000,考试成绩的范围是0 - 100
用php搞个小脚本来搞定
<?php
$i = 0;
$nRows = 10000000;
$fileData = fopen("Score.data", "a+");
if(!$fileData)
die("Can't open file!\n");
for(; $i < $nRows; $i++)
{
$nNumber = rand(1, 1000);
$nScore = rand(0, 100);
$strLine = $nNumber."\t".$nScore."\n";
fputs($fileData, $strLine);
}
fclose($fileData);
?>3. 配置eclipse
=========
用hadoop搞map-reduce自然要用java,用java自然要用eclipse
eclipse是有hadoop map-reduce插件的,但是那个插件兼容的版本已经太老了,因此如果你用了新版的eclipse,还得自己配一下hadoop的包。
hadoop的包在ubuntu上安装在
/usr/share/hadoop该路径下有一系列jar包(包括lib的子目录里等),导入到工程中,其中一般有用的是hadoop-core-1.1.2.jar这个包,基本上都要导入
4. 写个mapper
=========mapper在我们求平均分的例子中是将每一行数据拆分成<键:学号,值:分数>的对以交给reducer使用
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reporter;
public class CAvgScoreMapper implements Mapper<LongWritable, Text, Text, IntWritable>
{
//Mapper<LongWritable, Text, Text, IntWritable>是映射器接口,模板参数:
//LongWritable是输入给映射器的Key(一般是文件的偏移量)
//Text是输入给映射器的键值对中的值,一般是每一行文件的内容,
//Text是输出给归约器的键, 在此学号我们认为是字符串数据
//IntWritable是输出给归约器的值,在此是成绩,因此是整数
@Override
public void configure(JobConf objJobConfig)
{
//这个函数将作业启动时的job配置对象传入
}
@Override
public void close() throws IOException
{return;
//目前没啥用处,不用管
}
@Override
public void map(LongWritable lKey, Text txtLine, OutputCollector<Text, IntWritable> ReducerInput, Reporter objReporter)
throws IOException
{
//这是映射器的映射函数,在此将数据的每一行拆成学号:成绩的对
try
{
String strLine = txtLine.toString(); //这个函数中txtLine的输入就恰是每一行数据.
if(strLine.length() < 3) return; //正常数据至少有3个字符:学号一个字符,制表符一个字符,分数一个字符
StringTokenizer objToken = new StringTokenizer(strLine, "\t");
String strPair[] = new String[2];
int i = 0;
for(; (i < 2) && objToken.hasMoreTokens(); i++) //分解每一行的数据,构成键值对
{
strPair[i] = objToken.nextToken();
}
if(i < 2) return; //行数据分解的有问题
int nScore = Integer.parseInt(strPair[1]);
ReducerInput.collect(new Text(strPair[0]), new IntWritable(nScore));
}
catch(Exception expError)
{
return;
}
}
}
5. 写个reducer
=========reducer从mapper收到整理好的键值对,其中键是每一个学生的学号,值是一个列表,里面有这个学生的所有成绩
import java.io.IOException;
import java.util.Iterator;
import org.apache.hadoop.io.FloatWritable;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
public class CAvgScoreReducer implements Reducer<Text, IntWritable, Text, FloatWritable>
{
//Reducer<Text, IntWritable, Text, FloatWritable>是归约器的接口,模板参数:
//Text 从映射器收来的参数,这里是字符串(学号)
//IntWritable 从映射收来的参数,这里是整数(分数)
//Text 向结果输出的键,这里是字符串(学号)
//FloatWritable 向结果输出的值,这里是浮点(平均分)
@Override
public void configure(JobConf objJobConf)
{
//这个函数将作业启动时的job配置对象传入
}
@Override
public void close() throws IOException
{
//目前没啥用处,不用管
}
@Override
public void reduce(Text nNumber, Iterator<IntWritable> arrScores, OutputCollector<Text, FloatWritable> objOutput, Reporter objReporter)
throws IOException
{
try
{
//从分数中读取所有数据进行加合
int nScoreCount = 0;
long nTotal = 0;
for(; arrScores.hasNext(); nScoreCount++)
{
nTotal += arrScores.next().get();
}
if(nScoreCount <= 0) //分数统计的有问题
return;
float fAvgScore = nTotal / nScoreCount;
objOutput.collect(nNumber, new FloatWritable(fAvgScore)); //将平均分写入输出
}
catch(Exception expError)
{
return;
}
}
}
6. 写个作业启动器
=========写好mapper和reducer后,还需要写一个能够启动作业的玩意
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
public class CAvgScoreApp
{
public static void main(String arrArgvs[])
{
try
{
if(arrArgvs.length < 2)
{
System.out.println("Usage: AgvScore <input file> <output dir>");
System.exit(-1);
}
JobConf objJobConfig = new JobConf(); //创建一个作业配置对象
objJobConfig.setJobName("Get-Average-Score");
FileInputFormat.addInputPath(objJobConfig, new Path(arrArgvs[0])); //作业的输入是一个文件
FileOutputFormat.setOutputPath(objJobConfig, new Path(arrArgvs[1])); //设定作业是向文件输出,此处指定输出目录
objJobConfig.setMapperClass(CAvgScoreMapper.class); //设定映射器类
objJobConfig.setReducerClass(CAvgScoreReducer.class); //设定本作业归约器的名
objJobConfig.setOutputKeyClass(Text.class); //设定映射器向归约器输出时,键的类型
objJobConfig.setOutputValueClass(IntWritable.class); //设定映射器向归约器输出时,值的类型
JobClient.runJob(objJobConfig); //运行任务
}
catch(Exception expError)
{
System.out.println(expError.getMessage());
System.exit(-1);
}
}
}
7. 启动作业
=========
将程序封装成一个可执行的Jar包,然后在terminal
hadoop jar jar包名字 输入文件的名字 输出目录
即可,这里一定注意,是jar,不是-jar,原因参见我另一篇博文
下面是一个输出结果的例子
1 49.0
10 49.0
100 49.0
1000 50.0
101 50.0
102 49.0
103 49.0
104 49.0
105 49.0
106 50.0
107 49.0
108 49.0
本文详细介绍了使用 Hadoop 进行 MapReduce 数据处理的全过程,包括开发环境搭建、数据准备、Eclipse 配置、编写 Mapper 和 Reducer 代码、作业启动及输出结果分析。
1991

被折叠的 条评论
为什么被折叠?



