大多数的第一个map-reduce程序都是WordCount,但对于初学者来说,想要弄懂WordCount的流程不是一件简单的事,这里我就把WordCount拆分一下,只map不reduce。
先不看程序,自己来想想,写map-reduce的程序应该有哪些步骤。
1、输入输出。这就不用说了吧,任何程序都有输入输出,那么map-reduce中的输入输出又是怎么回事呢? 思考一下,在整个程序中,肯定有一个类似于setInputPath的方法来设置输入输出路径。
2、指定mapper方法。稍微了解hadoop就知道,map-reduce中的map和reduce分别是两个函数,用这两个函数来处理文件。
3、指定reduce方法。其实并不是所有的程序都有reduce,根据情况看是否需要reduce。
4、指定输入输出类型。map-reduce的输入一般都是文件,那么输入类型也是文件吗?其实不是的,每一次map-reduce的输入其实是文件的一行,而不是整个文件。那么这么来说,map-reduce的输入就是String(假定文件的一行的内容存放在String里面)吗?
这就是一般map-reduce的处理过程,当然由此可见,reduce兼带了输出,其实想想,reduce的过程就是处理输出结果的过程。在这里,sort、combine都是系统自动做的,我们只需要指定input、map和reduce。
package hadoop;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
public class GetIpFromFile
{
private static String HDFS_PATH = "hdfs://master:9000";
private static String INPUT_FILE_NAME = "input.txt";
private static String INPUT_FILE_PATH = "/home/hadoop/data/20150716/input";
private static String OUTPUT_FILE_PATH = "/home/hadoop/data/20150716/output";
public static void initFile()
{
try
{
FileSystem fs = FileSystem.get(new URI(HDFS_PATH), new Configuration());
Path inputPath = new Path(INPUT_FILE_PATH + INPUT_FILE_NAME );
if ( fs.exists(inputPath))
{
System.out.println("input file already exists, delete now...");
fs.delete(inputPath, true);
}
FSDataOutputStream out = fs.create(inputPath);
out.write("hello world".getBytes());
out.flush();
out.close();
FSDataInputStream in = fs.open(inputPath);
System.out.println("after init, the input file is:");
//IOUtils.copyBytes(in, System.out, 1023, false);
byte[] b = new byte[1023];
in.read(b);
System.out.println(new String(b));
}
catch (URISyntaxException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}
public static class MyMapper extends Mapper<Object, Text, NullWritable, Text>
{
public void map(Object key, Text value, Context context)
{
System.out.print("Line " + value.toString());
}
}
public static void main(String[] args)
{
initFile(); //这里是构造输入文件
try
{
Job job = new Job(new Configuration()); //新建一个job作业
job.setJarByClass(GetIpFromFile.class); //对job设置jar
job.setMapperClass(MyMapper.class); //指定map类
//job.setReducerClass(cls); //指定reduce类,由于这个程序中不需要reduce,所以不需要指定
job.setOutputKeyClass(NullWritable.class); //指定输出class的Key
job.setOutputValueClass(Text.class); //指定输出class的Value
FileSystem fs = FileSystem.get(new URI(HDFS_PATH), new Configuration()); //删除输入文件
if (fs.exists(new Path(OUTPUT_FILE_PATH)))
{
fs.delete(new Path(OUTPUT_FILE_PATH), true);
}
FileInputFormat.addInputPath(job, new Path(HDFS_PATH + INPUT_FILE_PATH)); //指定输入文件路径
FileOutputFormat.setOutputPath(job, new Path(HDFS_PATH + OUTPUT_FILE_PATH)); //指定输出文件路径
job.waitForCompletion(true); //提交作业并等待结束
}
catch (IOException e)
{
e.printStackTrace();
}
catch (URISyntaxException e)
{
e.printStackTrace();
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
这里我们只是为了在map里面看到文件的每一行,如果要写真正的map-reduce程序,那文件的处理就在map里面。