1.序列化:把结构化对象转化为字节流
2.反序列化 序列化的逆过程 把字节流转化为结构化对象
3.序列化格式特点:紧凑:高效使用存储空间。
快速:读写数据的额外开销小
可扩展:可透明地读取老格式的数据
互操作:支持多语言的交互
hadoop的序列化格式 : Writable
4.序列化在分布式环境的两大作用:进程间通信,永久存储。
即节点一 传输文件至节点二 1上消息序列化为二进制流 中间二进制流传输 2上二进制流反序列化为消息
MR的任意Key和Value必须实现Writable接口.
MR的任意key必须实现WritableComparable接口
6.hadoop中的数据类型
1~5这种类似于数据库中的varchar()有多少占多少弹性的
概述、hadoop最多可以提供两对输入输出的key value对 超出的话该怎么办
所以引入了自定义Writable类 (前面讲过所有的key value都要去实现Writable接口)
package com.myreduce;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Writable;
/**
* 这是自己定义的一个Writable类
* 因为hadoop只能提供两个的输入输出的数据操作 而我们要处理四个的超出了负载 所以需要自己定义一个 MapReduce任意的key
* value都实现了Writable接口(可以查看源码) MapReduce任意的key 都必须实现Writablecomparable
*
* @author zzh
*
*/
public class Traffic implements Writable {
private long t1 = 0;
private long t2 = 0;
private long t3 = 0;
private long t4 = 0;
// 无参构造
public Traffic() {
}
public Traffic(long t1, long t2, long t3, long t4) {
this.t1 = t1;
this.t2 = t2;
this.t3 = t3;
this.t4 = t4;
}
public long getT1() {
return t1;
}
public void setT1(long t1) {
this.t1 = t1;
}
public long getT2() {
return t2;
}
public void setT2(long t2) {
this.t2 = t2;
}
public long getT3() {
return t3;
}
public void setT3(long t3) {
this.t3 = t3;
}
public long getT4() {
return t4;
}
public void setT4(long t4) {
this.t4 = t4;
}
// 实现接口 实现(重写)write和read方法
public void write(DataOutput out) throws IOException {
out.writeLong(t1);
out.writeLong(t2);
out.writeLong(t3);
out.writeLong(t4);
}
public void readFields(DataInput in) throws IOException {
this.t1 = in.readLong();
this.t2 = in.readLong();
this.t3 = in.readLong();
this.t4 = in.readLong();
}
@Override
public String toString() {
return "Traffic [t1=" + t1 + ", t2=" + t2 + ", t3=" + t3 + ", t4=" + t4
+ "]";
}
}
package com.myreduce;
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
/**
* map阶段
* @author zzh
* KEYIN:默认情况下是MapReduce框架所读到的一行文本的偏移量,Long
* 但是在hadoop中有自己更精简的序列化接口,所以不直接用long而是用LongWritable
*
* VALUE:默认情况下是MapReduce框架所读到的一行文本的内容。String同上 用Text
*
* KEYOUT:是用户自定义逻辑处理写成之后输出数据中的key,String同上Text
*
* VALUEOUT:是用户自定义逻辑处理写成之后输出数据中的value 再此处因为输出四个数据,所以用自定义的Traffic
*/
public class TrafficMapper extends Mapper{
/*
* map阶段的业务逻辑就写在自己定义的map()方法中
* 把读入的文件解析成key value对。 对输入的文件每一行解析为key value对
* 每个键值对都会调用一次我们定义的map()方法
*/
@Override
protected void map(LongWritable key, Text value,
Mapper.Context context)
throws IOException, InterruptedException {
//把读入的value转换成字符串 根据TAb键把一行分隔成单个的单词
String[] values = value.toString().split("\t");
Text outkey = new Text(values[1]);
long t1 = Long.parseLong(values[6]);
long t2 = Long.parseLong(values[7]);
long t3 = Long.parseLong(values[8]);
long t4 = Long.parseLong(values[9]);
Traffic tf = new Traffic();
tf.setT1(t1);
tf.setT2(t2);
tf.setT3(t3);
tf.setT4(t4);
context.write(outkey, tf);
}
}
package com.myreduce;
import java.io.IOException;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
public class TrafficReduce extends Reducer{
@Override
protected void reduce(Text key, Iterable value,
Reducer.Context context)
throws IOException, InterruptedException {
//reduce任务是把多个map任务的输出合并排序 把相同key的value放入同一个集合中
//将输入的key value对解析成新的keyvalue对 输出
//此处的key值不需要动 可以原样输出 相同的key的value进行合并
int sum1 = 0;
int sum2 = 0;
int sum3 = 0;
int sum4 = 0;
for(Traffic s : value){
sum1 += s.getT1();
sum2 += s.getT2();
sum3 += s.getT3();
sum4 += s.getT4();
}
context.write(key, new Traffic(sum1,sum2,sum3,sum4));
}
}
package com.myreduce;
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.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class TrafficApp {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "file:///");
Job job = Job.getInstance(conf);
//设置job的属性
job.setJobName("trafficapp");
job.setJarByClass(Traffic.class);
job.setInputFormatClass(TextInputFormat.class);
//设置输入输出路径
FileInputFormat.addInputPath(job, new Path("file:///e:/copy/"));
FileOutputFormat.setOutputPath(job, new Path("file:///e:/copy/c"));
job.setMapperClass(TrafficMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Traffic.class);
job.setReducerClass(TrafficReduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Traffic.class);
job.waitForCompletion(true);
}
}
上述是在本地执行的 我们还要在hadoop集群上去执行 (hdfs 和yarn都需要启动)
把第一个file:注释掉 两个路径改为args[0] args[1]
打成jar包 传入Linux中 还需要把需要处理的文件传到hdfs上 输入输出路径都要在hdfs上
$>hadoop jar MyMapReduce.jar com.shulian.mr.WCApp 输入路径 输出路径
今天主要就是会运用自定义的Writable类 理解每一步的过程 能够在hadoop集群上运行即可
理解一下序列化 hadoop的数据对应的java的数据
查看是否传输成功192.168.163.101:8088 8088端口去看整个的运作过程
1.抛出数组越界的异常 :原因是分隔符的问题“\t”代表的是table键 我把斜杠打反了
2.甩出一个io异常,数据类型有问题 原因是我把App这个类里面定义Map的输出类型时定义错误
3.没有权限,无法创建文件 原因是我的file没写冒号............