一、学习内容
1.hadoop map的时候split的方法
2.hadoop reduce时候Partition的自定义实现
3.eclipse本地化调试
二、基础回顾
hadoop的mapreduce可以分为两部分,一部分是map,一部分是reduce,map的输出对应的是reduce的输入,以此达到协调工作的目的。Map-Reduce框架的运作完全基于<key,value>对,即数据的输入是一批<key,value>对,生成的结果也是一批<key,value>对,只是有时候它们的类型不一样而已。Key和value的类由于需要支持被序列化(serialize)操作,所以它们必须要实现Writable接口,而且key的类还必须实现WritableComparable接口,使得可以让框架对数据集的执行排序操作。
一个Map-Reduce任务的执行过程以及数据输入输出的类型如下所示:
(input)<k1,v1> -> map -> <k2,v2> -> combine -> <k2,v2> -> reduce -> <k3,v3>(output)
三、内容详解
1.hadoop map对数据进行split(分块)的时候,将严格按照大小进行分块,其hadoop默认的分块大小是64MB,我们可以通过修改hadoop-default.xm配置文件进行大小的分块,至于具体要分多少块需要结合实际的项目情况及结合hadoop官方建议的情况做23:00判断。当然,一说到分块,大家可能就会自觉的想到,同一个文件会被分到不同块中,更极端或者说更常见的情况是一行完整的数据被分到不同的块中,这种情况hadoop是怎么处理的呢?首先,hadoop在分块的时候会记住文件的起始位置和偏移量,然后再根据这个计算,举个普通的例子,如一个文件最后一行有一个单词room,很不幸在分块的时候,ro分在了第一个块,om分在了第二个块,当hadoop读取文件的时候会根据记住地一块的ro后的标志位,读取第二块的第一行,但读取第二块的时候就跳过第一行读取,以确保数据的完整性。
2.hadoop reduce时候Partition的自定义实现,指的是我们可以自己实现一些算法,实现reduce任务的客户化分发。实现的时候需要指定reduce任务的个数,
job.setNumReduceTasks(3);3个表示有0、1、2三个任务可供partition选择,一个具体的例子如下:
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
import org.apache.hadoop.mapreduce.Mapper.Context;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.partition.TotalOrderPartitioner;
import org.apache.hadoop.util.*;
public class Sort3{
public static class TestPar extends Partitioner<Text, IntWritable>{
@Override
public int getPartition(Text arg0, IntWritable arg1, int arg2) {
// TODO Auto-generated method stub
if(Integer.parseInt(arg0.toString().substring(7)) < 500)
return 0;
else
return 1;
}
}
public static class Map extends Mapper<LongWritable, Text, Text, IntWritable> {
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] arr = line.split(",");
word.set(arr[0]);
context.write(word, one);
}
}
public static class Reduce extends Reducer<Text, IntWritable, Text, IntWritable> {
public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int i=0;
for (IntWritable value : values){
i++;
}
context.write(key, new IntWritable(i));
}
}
public static void main(String[] args) throws Exception {
Job job = new Job();
job.setJarByClass(Sort3.class);
job.setJobName("Sort2");
FileInputFormat.addInputPath(job, new Path("hdfs://localhost:9000/input/"));
FileOutputFormat.setOutputPath(job, new Path("hdfs://localhost:9000/output/"));
job.setMapperClass(Map.class);
//job.setCombinerClass(Reduce.class);
job.setReducerClass(Reduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
job.setNumReduceTasks(3);
job.setPartitionerClass(TestPar.class);
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
3.eclipse的本地调试是指,mapreduce任务可以在操作系统本地文件上运行,一般如果在hadoop 文件系统上运行的话,需要把每个Java文件导出成jar,在后台运行,这样必然会导致开发效率的降低,我们可以搭本地的环境,调试好以后再放到hadoop上运行,搭本地的时候最好把hadoop文件系统停止了。一下是具体例子:
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
import org.apache.hadoop.mapreduce.Mapper.Context;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.*;
public class debug{
public static class Map extends Mapper<LongWritable, Text, Text, IntWritable> {
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] arr = line.split(",");
word.set(arr[0]);
context.write(word, one);
}
}
public static class Reduce extends Reducer<Text, IntWritable, Text, IntWritable> {
public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int i=0;
for (IntWritable value : values){
i++;
}
context.write(key, new IntWritable(i));
}
}
public static void main(String[] args) throws Exception {
Job job = new Job();
job.setJarByClass(debug.class);
job.setJobName("CdrStat");
job.getConfiguration().set("mapred.job.tracker", "local");
job.getConfiguration().set("fs.default.name", "local");
FileInputFormat.addInputPath(job, new Path("/home/robby/input/"));
FileOutputFormat.setOutputPath(job, new Path("/home/robby/output/"));
job.setMapperClass(Map.class);
//job.setCombinerClass(Reduce.class);
job.setReducerClass(Reduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
job.setNumReduceTasks(2);
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}