序列化概念
序列化:将内存中的对象装换成字节序列,以便于持久化存储到磁盘中以及网络传输。
反序列化:将收到的字节序列(或者其他数据传输协议)或者是磁盘的持久化数据,转换成内存中的对象。
为什么要序列化?
一般对象只存储在本地内存,允许本地进程调用。而在集群环境下,需要在不同的进程调用对象,这就需要将对象通过网络传输到另外的主机上。但对象无法直接通过网络传输,只有通过序列化处理之后才能通过网络传输。
Java里的序列化机制
在java中,创建一个类的时候实现 Serializable 接口,java内部就会自动实现相应的序列化。
但是由于Java的序列化接口实现的时候,会附带很多额外的信息,如各种校检信息,header等,不便于在网络上高效传输,所以hadoop自己开发了一套序列化机制,体积小,性能高。
Hadoop的序列化
- 紧凑:方便网络传输,高效使用存储空间
- 快速:序列化和反序列化的性能高
- 可扩展:可以随着通信协议的变化而变化
- 互操作:支持多语言交互
Hadoop序列化实现
hadoop中实现 Writable 这个接口的类,就可以实现序列化。
Hadoop对基本数据类型的包装
Hadoop参照JDK里面的数据类型实现了自己的数据类型,Hadoop自己实现的原理会使数据更紧凑一些,效率会高一些。序列化之后的字节数组大小会比JDK序列化出来的更小一些。
自定义Writable的步骤
- 实现writable接口
- 重写readFields反序列化和write序列化方法(注意这两个方法中的参数顺序要一致)。
- 必须要有无参构造器。
Hadoop序列化的实例:
修改 Hadoop_WordCount单词统计 项目。
- 创建Shengyu类
package com.blu.entity;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Writable;
/**
* zhangsan 500 450 jan
*
* @author BLU
*
*/
public class Shengyu implements Writable{
private String uname;
private Double income;
private Double expend;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public Double getIncome() {
return income;
}
public void setIncome(Double income) {
this.income = income;
}
public Double getExpend() {
return expend;
}
public void setExpend(Double expend) {
this.expend = expend;
}
public void readFields(DataInput in) throws IOException {
uname = in.readUTF();
income = in.readDouble();
expend = in.readDouble();
}
public void write(DataOutput out) throws IOException {
out.writeUTF(uname);
out.writeDouble(income);
out.writeDouble(expend);
}
}
- CountMoneyMapper类
package com.blu.countMoney;
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import com.blu.entity.Shengyu;
/**数据:
* 姓名 收入 支出 月份
* zhangsan 500 450 jan
* lisi 200 150 jan
* lilei 150 160 jan
* zhangsan 500 500 feb
* lisi 200 150 feb
* lilei 150 160 feb
*
* @author BLU
*
*/
public class CountMoneyMapper extends Mapper<LongWritable, Text, Text, Shengyu>{
/**输出的格式:
* zhangsan Shengyu
* lisi Shengyu
* .......
*/
Text t = new Text();
Shengyu sy = new Shengyu();
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, Shengyu>.Context context)
throws IOException, InterruptedException {
//拿到一行数据:zhangsan 500 450 jan
String line = value.toString();
//按空格切分
String[] vals = line.split(" ");
t.set(vals[0]);//取到用户名作为mapper阶段输出的Key的值
sy.setUname(vals[0]);
sy.setIncome(Double.parseDouble(vals[1]));
sy.setExpend(Double.parseDouble(vals[2]));
context.write(t, sy);
}
}
- CountMoneyReducer类
package com.blu.countMoney;
import java.io.IOException;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import com.blu.entity.Shengyu;
/**最终输出格式:
* zhangsan 50
* lisi 100
* lilei -20
*
* @author BLU
*
*/
public class CountMoneyReducer extends Reducer<Text, Shengyu, Text, DoubleWritable>{
DoubleWritable dw = new DoubleWritable();
@Override
protected void reduce(Text text, Iterable<Shengyu> value, Reducer<Text, Shengyu, Text, DoubleWritable>.Context context)
throws IOException, InterruptedException {
double insum = 0; //收入总计
double exsum = 0; //支出总计
for(Shengyu s : value) {
insum += s.getIncome();
exsum += s.getExpend();
}
dw.set(insum - exsum);
context.write(text, dw);
}
}
- CountMoneyJob
package com.blu.countMoney;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DoubleWritable;
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.blu.entity.Shengyu;
public class CountMoneyJob {
public static void main(String[] args) throws Exception {
Job job = Job.getInstance();
job.setJarByClass(CountMoneyJob.class);
job.setMapperClass(CountMoneyMapper.class);
job.setReducerClass(CountMoneyReducer.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Shengyu.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(DoubleWritable.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
boolean flag = job.waitForCompletion(true);
System.exit(flag?0:1);
}
}
- 在D:\data目录下创建money.txt
zhangsan 500 450 jan
lisi 200 150 jan
lilei 150 160 jan
zhangsan 500 500 feb
lisi 200 150 feb
lilei 150 160 feb
- 带参数:D:\data\money.txt D:\data\output 运行main方法,将在D:\data\output下生成以下文件:
- 以文本文件形式打开part-r-00000文件
lilei -20.0
lisi 100.0
zhangsan 50.0