Hadoop --- 入门之MapReduce示例

单词统计(WordCount)示例:

1、定义一个Mapper类:

import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

/**
 * KEYIN:默认情况下,是mr框架所读到的一行文本的起始偏移量,LongWritable
 * VALUEIN:默认情况下,是mr框架所读到的一行文本的内容,Text
 * 
 * KEYOUT:是用户自定义逻辑处理完成之后输出数据中的key,word count中是单词,Text
 * VALUEOUT:是用户自定义逻辑处理完成之后输出数据中的value,word count中是单词次数,IntWritable
 * 
 * 因为所有数据都需要被序列化,而Java的Serializable序列化会包含很多类的冗余信息,所以不适用Java中的类型,而是使用hadoop封装的可序列化类型
 * 
 * @author Administrator
 *
 */
public class WordCountMapper extends Mapper{

	/**
	 * map阶段的业务逻辑就写在自定义的map()方法中
	 * maptask会对每一行输入数据调用一次我们自定义的map()方法
	 */
	@Override
	protected void map(LongWritable key, Text value, Mapper.Context context)
			throws IOException, InterruptedException {
		
		// 将maptask传给我们的文本内容先转换成String
		String line = value.toString();
		// 根据空格将这一行切分成单词
		String[] words = line.split(" ");
		
		// 将单词输出为
		for (String word:words) {
			// 将单词作为key,将次数1作为value,以便于后续的数据分发,可以根据单词分发,以便于相同单词汇总到相同的reduce task
			context.write(new Text(word),new IntWritable(1));
		}
		
	}
}

 

2、定义一个Reducer类

import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

/**
 * KEYIN:对应mapper输出的KEYOUT
 * VALUEIN:对应mapper输出的VALUEOUT
 * 
 * KEYOUT:是用户自定义逻辑处理完成之后输出数据中的key,word count中是单词,Text
 * VALUEOUT:是用户自定义逻辑处理完成之后输出数据中的value,word count中是单词次数,IntWritable
 * 
 * 因为所有数据都需要被序列化,而Java的Serializable序列化会包含很多类的冗余信息,所以不适用Java中的类型,而是使用hadoop封装的可序列化类型
 * 
 * @author Administrator
 *
 */
public class WordCountReducer extends Reducer{

	/**
	 * 参数1:key是一组相同单词对的key
	 */
	@Override
	protected void reduce(Text key, Iterable values,
			Reducer.Context context) throws IOException, InterruptedException {
		
		int count = 0;
		for(IntWritable value : values){
			count += value.get();
		}
		
		context.write(key, new IntWritable(count));
	}
}

 

3、定义一个Job类

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

/**
 * 相当于一个yarn集群的客户端
 * 需要在此封装我们的mr程序的相关运行参数,指定jar包
 * 最后提交给yarn
 * @author Administrator
 *
 */
public class WordCountDriver {

	public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();
		
		Job job = Job.getInstance(conf);
		
		//指定本程序的jar包所在的路径
		job.setJarByClass(WordCountDriver.class);
		
		//指定本业务job要使用的mapper业务类
		job.setMapperClass(WordCountMapper.class);
		//指定本业务job要使用的reducer业务类
		job.setReducerClass(WordCountReducer.class);
		
		//指定mapper输出数据的KV类型
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(IntWritable.class);
		
		//指定最终输出的数据的KV类型
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);
		
		//指定job的输入原始文件所在的目录
		FileInputFormat.setInputPaths(job, new Path(args[0]));
		
		//指定job的输出结果所在的目录
		FileOutputFormat.setOutputPath(job, new Path(args[1]));
		
		//将job中配置的相关参数,以及job所用的java类所在的jar包,提交给jarn去运行
		/*job.submit(); */
		//将job中配置的相关参数,以及job所用的java类所在的jar包,提交给jarn去运行,并且等待集群运行的返回结果,参数true表示是否打印集群反馈信息
		boolean res = job.waitForCompletion(true);
		
		System.exit(res?0:1);
		
	

 

手机流量统计(FlowCount)示例:

1、定义一个Mapper类:

import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class FlowCountMapper extends Mapper{

	@Override
	protected void map(LongWritable key, Text value, Mapper.Context context)
			throws IOException, InterruptedException {
		
		// 将一行内容转成String
		String line = value.toString();
		
		String[] fields = line.split("\t");
		
		// 取出手机号
		String phoneNbr = fields[1];
		
		//取出上行流量和下行流量
		long upFlow = Long.parseLong(fields[fields.length - 3]);
		long dnFlow = Long.parseLong(fields[fields.length - 2]);
		
		context.write(new Text(phoneNbr), new FlowBean(upFlow, dnFlow));
		
	}
	
}

 

2、定义一个Reducer类

import java.io.IOException;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class FlowCountReducer extends Reducer{

	@Override
	protected void reduce(Text key, Iterable values, Reducer.Context context)
			throws IOException, InterruptedException {

		long sum_upFlow = 0;
		long sum_dnFlow = 0;
		
		// 遍历所有bean,将其中的上行流量,下行流量分别累加
		for (FlowBean flowBean : values) {
			
			sum_upFlow += flowBean.getUpFlow();
			sum_dnFlow += flowBean.getDnFlow();
		}
		
		context.write(key, new FlowBean(sum_upFlow, sum_dnFlow));
		
	}
}

 

3、定义一个Job类:

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import com.newtouch.mr.provinceflow.ProvincePartitioner;

public class FlowCountDriver {

	public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();

		Job job = Job.getInstance(conf);

		// 指定本程序的jar包所在的路径
		job.setJarByClass(FlowCountDriver.class);

		// 指定自定义的数据分区器
		job.setPartitionerClass(ProvincePartitioner.class);
		// 同时指定相应数据分区数量的reducetask:0,1,2,3,4
		job.setNumReduceTasks(5);
		
		// 指定本业务job要使用的mapper业务类
		job.setMapperClass(FlowCountMapper.class);
		// 指定本业务job要使用的reducer业务类
		job.setReducerClass(FlowCountReducer.class);

		// 指定mapper输出数据的KV类型
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(FlowBean.class);

		// 指定最终输出的数据的KV类型
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(FlowBean.class);

		// 指定job的输入原始文件所在的目录
		FileInputFormat.setInputPaths(job, new Path(args[0]));
		
		// 指定job的输出结果所在的目录
		FileOutputFormat.setOutputPath(job, new Path(args[1]));

		// 将job中配置的相关参数,以及job所用的java类所在的jar包,提交给jarn去运行
		/* job.submit(); */
		// 将job中配置的相关参数,以及job所用的java类所在的jar包,提交给jarn去运行,并且等待集群运行的返回结果,参数true表示是否打印集群反馈信息
		boolean res = job.waitForCompletion(true);

		System.exit(res ? 0 : 1);
	}
}

 

4、定义一个Partitioner类:

如果要对统计数据进行分类处理(手机流量按照省份分类),那么需要自定义一个Partitioner,此时需要注意Job中设置的ReduceTask的数量必须大于分类数量

import java.util.HashMap;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;
import com.newtouch.mr.flowsum.FlowBean;

/**
 * 该类的作用是将手机号的流量统计后,按照省进行分区,并将结果归属于各省的文件夹中
 * 
 * K2,V2:对应的是map输出的KV的类型
 * @author Administrator
 *
 */
public class ProvincePartitioner extends Partitioner{

	public static HashMap proviceDict = new HashMap();
	static{
		proviceDict.put("136",  0);
		proviceDict.put("137",  1);
		proviceDict.put("138",  2);
		proviceDict.put("139",  3);
	}
	
	@Override
	public int getPartition(Text key, FlowBean value, int numPartitions) {
		
		String prefix = key.toString().substring(0, 3);
		Integer provinceId = proviceDict.get(prefix);
		
		return provinceId == null?4:provinceId;
	}

}

 

MapReduce程序运行模式

1、本地运行模式

(1)mapreduce程序是被提交给LocalJobRunner在本地以单进程的形式运行

(2)而处理的数据及输出结果可以在本地文件系统,也可以在hdfs上

(3)怎样实现本地运行?写一个程序,不要带集群的配置文件(本质是你的mr程序的conf中是否有

mapreduce.framework.name=local以及yarn.resourcemanager.hostname参数)

(4)本地模式非常便于进行业务逻辑的debug,只要在eclipse中打断点即可,如果在windows下想运行本地模式来测试程序逻辑,需要在windows中配置环境变量:

%HADOOP_HOME%  =  d:/hadoop-2.8.0

%PATH% =  %HADOOP_HOME%\bin

并且要将d:/hadoop-2.8.0的lib和bin目录替换成windows平台编译的版本

 

2、集群运行模式

(1)将mapreduce程序提交给yarn集群resourcemanager,分发到很多的节点上并发执行

(2)处理的数据和输出结果应该位于hdfs文件系统

(3)提交集群的实现步骤:

 A、将程序打成JAR包,然后在集群的任意一个节点上用hadoop命令启动

hadoop jar workcount.jar cn.gigdata.hdfs.mr.WordcountDriver /wordcount/input1 /wordcount/outputWord

B、直接在linux的eclipse中运行main方法(项目中要带参数:mapreduce.framework.name=yarn以及yarn的两个基本配置)

C、如果要在windows的eclipse中提交job给集群,则要修改YarnRunner类

 

MapReduce程序编程规范: 

(1)用户编写的程序分成三个部分:Mapper,Reducer,Driver(提交运行mr程序的客户端)

(2)Mapper的输入数据是KV对的形式(KV的类型可自定义)

(3)Mapper的输出数据是KV对的形式(KV的类型可自定义)

(4)Mapper中的业务逻辑写在map()方法中

(5)map()方法(maptask进程)对每一个调用一次

(6)Reducer的输入数据类型对应Mapper的输出数据类型,也是KV

(7)Reducer的业务逻辑写在reduce()方法中

(8)Reducetask进程对每一组相同k的组调用一次reduce()方法

(9)用户自定义的Mapper和Reducer都要继承各自的父类

(10)整个程序需要一个Drvier来进行提交,提交的是一个描述了各种必要信息的job对象

 

你可能感兴趣的:(------,Hadoop,大数据)