自定义MapReduce业务逻辑

110 篇文章 0 订阅
95 篇文章 0 订阅

1.我们刚一开始的时候,在HDFS上面处理文件时候,我们并没有自己写MapReduce,而是用的是镜像架包下面的/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.6.4.jar,同样的也将运行出来结果(hadoop jar hadoop-mapreduce-examples-2.6.4.jar wordcount 文件的在Linux上的源路径  文件处理后结果存放在HDFS上的路径)

例如:hadoop jar hadoop-mapreduce-examples-2.6.4.jar wordcount /root/wordcount.txt /wordcount/input

这其实是hadoop架包里面自带的一个Mapreduce的demo实例,红色是我们要运行的主方法名,通常我们在实例中需要写绝对路径;黄色是我们将要处理的文件在Linux上的绝对路径;绿色是我们处理后文件,得到的结果保存的位置(指的是保存在HDFS上的绝对路径,并且这个路径在HDFS上不能存在,当运行命令的时候回自动创建,如果存在程序会包错误)

2.往往在实际开发中我们需要自己写MapReduce的业务逻辑,hadoop架包里的已经不能满足我们的多样化需求了,下面就介绍如何去customMapReduce

我们在创建项目时候可以选择maven也可以选择javaProject,maven比较简单直接从aliyun仓库在线下载架包即可,Javaproject则需要我们机子手动搭建所需架包,

  1. 首先将你的cenos-6.5-hadoop-2.6.4架包解压,
  2. 进入\cenos-6.5-hadoop-2.6.4\hadoop-2.6.4\share\hadoop;将common下面lib下的+sources下的+hadoop-common-2.6.4+hdfs下面的lib下面的+sources下面的+hadoop-hdfs-2.6.4导入buildpath下面
  3. \cenos-6.5-hadoop-2.6.4\hadoop-2.6.4\share\hadoop\mapreduce\下面的全部架包导入buildpath下面
  4. 最后apply一下就可以

3.实例:统计每一个用户的使用总流量

此案例我们以对象的形式在网络间传输,所以要想在网络间传输,我们就要对对象进行序列化,正好hadoop内部封装有序列化接口,我们只需要实现这个接口即可WritableComparable

public class TelBean implements WritableComparable<TelBean> {

	private String tel;用户电话号码
	private Long upPayLoad;上行流量
	private Long downPayLoad;下行流量
	private Long totalPayLoad;总流量=上行+下行

	public String getTel() {
		return tel;
	}

	public void setTel(String tel) {
		this.tel = tel;
	}

	public Long getUpPayLoad() {
		return upPayLoad;
	}

	public void setUpPayLoad(Long upPayLoad) {
		this.upPayLoad = upPayLoad;
	}

	public Long getDownPayLoad() {
		return downPayLoad;
	}

	public void setDownPayLoad(Long downPayLoad) {
		this.downPayLoad = downPayLoad;
	}

	public Long getTotalPayLoad() {
		return totalPayLoad;
	}

	public void setTotalPayLoad(Long totalPayLoad) {
		this.totalPayLoad = totalPayLoad;
	}


//序列化
	@Override
	public void readFields(DataInput in) throws IOException {
		// TODO Auto-generated method stub
		this.tel = in.readUTF();
		this.upPayLoad = in.readLong();
		this.downPayLoad = in.readLong();
		this.totalPayLoad = in.readLong();
	}
//反序列化
	@Override
	public void write(DataOutput out) throws IOException {
		// TODO Auto-generated method stub
		out.writeUTF(tel);
		out.writeLong(upPayLoad);
		out.writeLong(downPayLoad);
		out.writeLong(totalPayLoad);
	}
//对象之间的比较,排序
	@Override
	public int compareTo(TelBean bean) {
		// TODO Auto-generated method stub
		if (this.getTotalPayLoad() > bean.getTotalPayLoad()) {
			return -1;
		} else if (this.getTotalPayLoad() < bean.getTotalPayLoad()) {
			return 1;
		} else {
			if (this.getDownPayLoad() > bean.getDownPayLoad()) {
				return -1;
			} else {
				return 1;
			}
		}
	}

	@Override
	public String toString() {
		return tel + "\t" + upPayLoad + "\t" + downPayLoad + "\t" + totalPayLoad;
	}

	public TelBean(String tel, Long upPayLoad, Long downPayLoad, Long totalPayLoad) {
		super();
		this.tel = tel;
		this.upPayLoad = upPayLoad;
		this.downPayLoad = downPayLoad;
		this.totalPayLoad = totalPayLoad;
	}

	public TelBean() {
		super();
		// TODO Auto-generated constructor stub
	}

	public TelBean(Long upPayLoad, Long downPayLoad, Long totalPayLoad) {
		super();
		this.upPayLoad = upPayLoad;
		this.downPayLoad = downPayLoad;
		this.totalPayLoad = totalPayLoad;
	}

}

4.重写Mapper,同样只需要继承hadoop内部封装好的Mapper类即可,要注意的要在网络间传输,一切都要序列化,string对应的序列化后是text,int序列化后是IntWritable,long序列化后LongWritable等等

LongWritable指的是获取一行内容的起始偏移量
Text指的是一行文本内容
Text指的是我们将要输出的tel
TelBean指的是以对象的形式进行输出
public class TCMapper extends Mapper<LongWritable, Text, Text, TelBean>{

	@Override
	protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, TelBean>.Context context)
			throws IOException, InterruptedException {
		// TODO Auto-generated method stub
获取一行信息
		String line = value.toString();
		将单词以\t进行分割
		String[] fields = line.split("\t");
		实例化对象
		TelBean bean = new TelBean(fields[1],Long.valueOf(fields[8]),Long.valueOf(fields[9]),new Long(0));
		设置将要传送给reduce的数据形式
		context.write(new Text(fields[1]), bean);
	}
}

5.对于Reduce同样hadoop内部封装有Reducer

注意的是map的输出就是reduce的输入
Text指的是map传送过来的keyout,在这里是keyin

我们最后想要的结果肯定是手机号对应的上行,下行,总流量
所以text数字的是tel
其他的封装在telBean里面

public class TCReducer extends Reducer<Text, TelBean, Text, TelBean>{

	@Override
	protected void reduce(Text key, Iterable<TelBean> value, Context context)
			throws IOException, InterruptedException {
		// TODO Auto-generated method stub
//		做统计
		long sumUp = 0;
		long sumDown = 0;
		
		for (TelBean bean : value) {
			sumUp += bean.getUpPayLoad();
			sumDown += bean.getDownPayLoad();
		}
		
//		telBean的属性应该与log一一对应
//		当前的bean应该是一个新的bean
		TelBean bean = 
				new TelBean(sumUp, sumDown, sumUp+sumDown);
		
		context.write(new Text(key), bean);
	}
}

6.如果我们想将所有手机号前三位为135或者136的手机号令存放一个文件,代表的是同一个归属地的手机号,那么我们要用到partitioner分割器

map -- suffer -- reduce
map的输出是suffer的输入

public class TCPartitioner extends Partitioner<Text, TelBean>{

	@Override
	public int getPartition(Text key, TelBean bean, int arg2) {
		// TODO Auto-generated method stub

//生成的文件part-r-00000 part的编号的结尾就是这个int类型的返回值
//根据不同的电话号码 ,划分到不同的区里面

		String tel = bean.getTel();
		
		String subTel = tel.substring(0, 3);
		
		if ("135".equals(subTel)||"136".equals(subTel)) {
			return 1;//part-r-00001里面
		}
		return 0;
	}

}

7.创建job

public class TCAPP {

	public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//		获取job
		Configuration conf = new Configuration();
		Job job = Job.getInstance(conf);
		
//		指定job使用的类
		job.setJarByClass(TCAPP.class);
		
//		将partitioner添加到job里面
		job.setPartitionerClass(TCPartitioner.class);
//		设置reduceTasks的数量 有几个分区设置几个任务
		job.setNumReduceTasks(2);
		
//		设置mapper的类以及属性
		job.setMapperClass(TCMapper.class);
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(TelBean.class);
		
//		设置reduce的类以及属性
		job.setReducerClass(TCReducer.class);
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(TelBean.class);
		
//		设置输入文件 在调用的时候动态的传递参数
		FileInputFormat.setInputPaths(job, new Path(args[0]));
		
//		设置输出目录
		FileOutputFormat.setOutputPath(job, new Path(args[1]));
		
//		提交任务
		job.waitForCompletion(true);
	}
}

8.将你当前的项目打包

9.启动hadoop集群

10.上传要处理的文件和打好的包到Linux下

11.将要处理的文件上传到HDFS根目录下

hadoop fs -put /root/要处理的文件名+后缀 /   (/代表的是hdfs根目录)

12.将hadoop的前端页面打开,观察是否上传成功

13.运行架包

hadoop jar 架包名   我们住方法绝对路径    我们上传在HDFS里面要处理的文件绝对路径       处理结果存放在HDFS里面的绝对路径

实际开发中要确保处理文件的时间大于HDFS启动的时间,要记住hadoop不适合处理小文件

14.前端页面查看是否成功

_SUCCESS只是一个标识,代表运行成功

part-r-000001则是我们要求的手机号码为135或者136开头的存放在一个文件中

注意:hadoop不适合处理小文件,实际开发中要避免多个小文件的产生,在源头进行处理,将小文件合并,或者是在map阶段将小的分区内容进行适当合并,减少reduce阶段的处理压力。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值