目的:统计各个手机号在某段时间类产生的总流量
准备文件 (已经上传到hdfs上 文件名data.txt)
上图中对应的字段如下图
文件及代码分析
所给的文件是每一个用户每一次上网产生的流量,先如今需要将相同用户进行聚合。
最后输出的结果字段:手机号 上行总流量 下行总流量 总流量
map的输入输出都是以key value 形式存在。输入的键值对为K1为整数 value为字符串 , 输出的键值对K2为字符串(手机号),输出相当于上行总流量 ,下行总流量 ,总流量的list。所以我们用一个对象(DataBean)来保存它们。
reduce的输入就是map的输出(经过shuffle处理,这里不做详细说明),reduce输出的形式为key为手机号(字符串),value为对象(DataBean)的结果就是我们最后想要的结果。
map进行的业务处理就是取出目标文件中的四个字段,然后进行拆分
reduce进行的业务处理,主要是对map的输出中的DataBean里面的流量进行求和,最后输出,下面直接上代码。
DataBean
package cn.master1.hadoop.mr.dc;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Writable;
public class DataBean implements Writable{
private String telNo;
private long upPayLoad;
private long downPayLoad;
private long totalPayLoad;
public DataBean() {}
public DataBean(String telNo, long upPayLoad, long downPayLoad) {
this.telNo = telNo;
this.upPayLoad = upPayLoad;
this.downPayLoad = downPayLoad;
this.totalPayLoad = upPayLoad + downPayLoad;
}
@Override
public String toString() {
return this.upPayLoad + "/t" + this.downPayLoad + "/t" + this.totalPayLoad;
}
public void write(DataOutput out) throws IOException {
out.writeUTF(telNo);
out.writeLong(upPayLoad);
out.writeLong(downPayLoad);
out.writeLong(totalPayLoad);
}
public void readFields(DataInput in) throws IOException {
this.telNo = in.readUTF();
this.upPayLoad = in.readLong();
this.downPayLoad = in.readLong();
this.totalPayLoad = in.readLong();
}
public String getTelNo() {
return telNo;
}
public void setTelNo(String telNo) {
this.telNo = telNo;
}
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;
}
}
package cn.master1.hadoop.mr.dc;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class DataCount {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
job.setJarByClass(DataCount.class);
job.setMapperClass(DCMapper.class);
/*当k2 v2 和 k3 v3 类型一一对应时,此行和下面一行可以省略。*/
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(DataBean.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));
job.setReducerClass(DCReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(DataBean.class);
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);
}
public static class DCMapper extends Mapper<LongWritable, Text, Text, DataBean>{
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, DataBean>.Context context)
throws IOException, InterruptedException {
//接收数据
String line = value.toString();
String[] fileds = line.split("/t");
String telNo = fileds[1];
long up = Long.parseLong(fileds[8]);
long down = Long.parseLong(fileds[9]);
DataBean bean = new DataBean(telNo, up, down);
context.write(new Text(telNo), bean);
}
}
public static class DCReducer extends Reducer<Text, DataBean, Text, DataBean>{
@Override
protected void reduce(Text key, Iterable<DataBean> v2s, Context context)
throws IOException, InterruptedException {
long up_sum = 0;
long down_sum = 0;
for(DataBean bean : v2s){
up_sum += bean.getUpPayLoad();
down_sum += bean.getDownPayLoad();
}
DataBean bean = new DataBean("", up_sum, down_sum);
context.write(key, bean);
}
}
}
jar包
打成jar包(不指定入口程序)命名为examples.jar,放到虚拟机跟目录下,然后执行
hadoop jar /root/examples.jar cn.master1.hadoop.mr.dc.DataCount /data.txt /dataout
cn.master1.hadoop.mr.dc.DataCount指定运行的入口程序 /data.txt 目标文件(存在hdfs上) /dataout输出文件(存放到hdfs上)
最后输出结果如下
下面简单说一说MR的执行流程和hadoop的序列化
MR执行流程
(1).客户端提交一个mr的jar包给RM(resourceManage)(提交方式:hadoop jar ...)
(2).JobClient通过RPC和RM进行通信,返回一个存放jar包的地址(HDFS)和jobId
(3).client将jar包写入到HDFS当中(path = hdfs上的地址 + jobId)
(4).开始提交任务(任务的描述信息,不是jar, 包括jobid,jar存放的位置,配置信息等等)
(5).RM进行初始化任务
(6).读取HDFS上的要处理的文件,开始计算输入分片,每一个分片对应一个NM(nodeManage)
(7).NM通过心跳机制领取任务(任务的描述信息)
(8).下载所需的jar,配置文件等。
(9).NM启动一个java child子进程,用来执行具体的任务(MapperTask或ReducerTask)
(10).将结果写入到HDFS当中。
hadoop序列化
- 序列化的概念
序列化(Serialization)是指把结构化对象转化为字节流。
反序列化(Deserialization)是序列化的逆过程。即把字节流转回结构化对象。
Java序列化(java.io.Serializable)
hadoop序列化并不是用的java自带的序列化机制,java的序列化机制运用的比较广泛,所以序列化和反序列化时保存的东西过多,效率较低,而hadoop在序列化时,只需要保存数据即可,因为只需要传输数据。hadoop具有特定的序列化机制。
- 序列化格式特点:
紧凑:高效使用存储空间。
快速:读写数据的额外开销小
可扩展:可透明地读取老格式的数据
互操作:支持多语言的交互
hadoop的序列化格式Writable
更多详细介绍