Hadoop的序列化
序列化:数据从内存中的到输出流,比如磁盘,网络,也就是说数据出内存的过程就是序列化的过程。
反序列化:数据从输入流到内存缓冲区,比如从磁盘、网络,也就是说数据进入内存的过程就是发序列化的过程,和序列化互为逆过程。
Hadoop自定义序列化类的案例
需求:
是电信一段日志记录的表结构,现需要统计每一个手机号码的upPackNum、downPackNum、upPayLoad、dwonPayLoad的分别的总和。
分析:
1、要按照手机号码来统计上述四个值,那么我们的k2就应该是手机号码,类型可以是Text或者IntWritable
2、接下来要确定v2的类型
我们发现要分别统计四个值的综合,可以有两种方式
1°第一种方式:
将每一条记录拼成一个字符串,用相应的分隔符分割这四个值,eg:
v2Str = upPackNum + ” ” + downPackNum + ” ” + upPayLoad + ” ” + dwonPayLoad;
这样的话,在reducer中只需要对每一个v2Str进行解析即可获取每一个字段的值,最后进行累加(String–>IntWritable)。
这里必须要注意的是:解析的顺序不能乱。这种方式留给大家自己来做。
2°、第二种方式:
我们可以采用面向对象的方式,因为在java的世界中,绝大部分数据都封装在对象里面,我们就可以采用自定义Writable
序列化对象的方式来做,那么怎么来做呢?
public class MyWritable implements Writable {
write(DataoutPut out) {
…//–>将获取的每一行数据中的这四个值写到磁盘
}
readFields(DataInPut in) {
…//–>从磁盘中获取write写出去的四个值,然后加载到内存
}
}
注意:我们自定义的MyWritable并没有事项WritableComparable接口,因为我们在这里确定的是v2的类型,不需要再shuffle中
进行分区比较排序,所以就不用实现WritableComparable接口!
3、输出结果格式
手机号 upPackNum downPackNum upPayLoad dwonPayLoad
public class WritableMR {
static class WritableMapper extends Mapper<LongWritable, Text, IntWritable, MyWritable> {
map() {
}
}
static class WritableReducer extends Reducer<IntWritable, MyWritable, IntWritable, MyWritable> {
reduce() {
}
}
}
SequenceFile2HDFS
要将普通的文本文件经过压缩之后上传到HDFS里面,这是我们采用的Gzip,压缩为二进制的序列化sequence文件,
要使用SequenceFile提供的一个类Writer来进行写文件的操作。
/* option数组里面的内容
* Path name 这是压缩输出到hdfs上的文件路径
* Class keyClass 压缩后的数据key的类型
* Class valClass 压缩后的数据value的类型
* SequenceFile.CompressionType compressionType
*/
Option outputPathOption = SequenceFile.Writer.Option.file(outpath);
Option keyClazzOption = SequenceFile.Writer.Option.keyClass(Text.class);
Option valClazzOption = SequenceFile.Writer.Option.ValueClass(NullWritable.class);
Option compressionOption = SequenceFile.Writer.Option.compression(SequenceFile.CompressionType.BLOCK, new GzipCodec());
Option[] opts = new Option[]{outputPathOption, keyClazzOption,valClazzOption, compressionOption};
SequenceFile.Writer writer = SequenceFile.Writer.createWriter(conf, opts);
BufferedReader br = new BufferedReader(new FileReader(inputpath));//inputpath是linux上面的路径
String line = null;
Text key = new Text();
while((line = br.readLine()) != null) {
key.set(line);
writer.append(key, NullWritable.get());
}
writer.close();
br.close();
以上过程的逆过程
/**
* 将HDFS中压缩后的额数据,读取出来,保存成正常的没有经过压缩的文件到本地磁盘
*/
public class HDFS2SequenceFile {
public static void main(String[] args) throws Exception {
if(args == null || args.length < 2) {
System.err.println(“parameter errors! Useage: ”);
System.exit(-1);
}
Path inputPath = new Path(args[0]);
String outputPath = args[1];
Configuration conf = new Configuration();
//获取hdfs中对压缩数据读取的输入流SequenceFile.Reader
SequenceFile.Reader reader = new SequenceFile.Reader(conf,//制定要读取文件的hdfs路径
SequenceFile.Reader.file(inputPath));
Text key = new Text();
FileWriter fw = new FileWriter(new File(outputPath));//创建了一个输出流执行本次磁盘的一个路径
while(reader.next(key, NullWritable.get())) {//循环读取压缩文件到指定的key和value中
String content = key.toString();//对读到的key和value中的数据进行提取或其他操作
fw.write(content);//将提取到的内容写到输出流fw中
}
fw.close();
reader.close();
}
}