hadoop 序列化小案例

上次我们做了简单的一个wordcount案例,下边我们在做一个复杂一点的小案例。
例如传输一个对象到wordcount中,输入数据格式如下:

3	12939234290		120.156.21.100		www.baidu.com  	 116	           125		 	200
id       手机号码                       网络ip               网址         下载流量           上传流量                 响应状态码	

期望输出的数据格式:

12939234290         116            125        241
手机号码             下行流量        上行流量    总流量

hadoop支持大量数据进行分布式存储,便于计算向数据移动。但是上边的数据是多个,但在hadoop中并没有支持对象这种复杂型数据类型,那该如何进行传输呢?
那我们对应上边的数据进行封装,那封装的对象该如何传递呢?
这里是不是会想到RPC框架里的数据传递,使用序列化。对的,hadoop也使用了自己的序列化方案。
那它和我们普通的序列化比较有何不同呢?
1.紧凑:存储空间更少
2.快速:传输速度更快
3.互操作:支持多语言操作

分析:
我们需要将上边一条数据,利用Tab分隔符进行分割,得到每一个数据,
然后对其进行封装到我们自定义的一个对象中去。
那自定义的对象要如何,序列化呢?hadoop有没有现成的序列化和反序列化呢?
我们可以查看之前小案例中的IntWriteable或者LongWriteable类,
他们可以在hadoop的序列化和反序列化中传递,都实现了一个相同的接口WritableComparable接口。
在进入查看WritableComparable接口,可以看到是继承了Writable和Comparable接口。这2个接口都有什么方法呢?
在Writable接口中,我们看到了序列化和反序列化的方法注释和解释
在Comparable接口中,我们看到了排序的方法注释和解释,针对我们的key进行排序
那我们就仿照这这个来定义一下我们要传递的对象

FlowBean类

import org.apache.hadoop.io.WritableComparable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

/**
 * @author tenic
 * @version 1.0
 * @Date 2021-04-13
 * @Desc
 */
public class FlowBean  implements WritableComparable<FlowBean> {

    private Long downFlow;
    private Long upFlow;
    private Long totalFlow;

    public Long getUpFlow() {
        return upFlow;
    }

    public void setUpFlow(Long upFlow) {
        this.upFlow = upFlow;
    }

    public Long getDownFlow() {
        return downFlow;
    }

    public void setDownFlow(Long downFlow) {
        this.downFlow = downFlow;
    }

    public Long getTotalFlow() {
        return totalFlow;
    }

    public void setTotalFlow(Long totalFlow) {
        this.totalFlow = totalFlow;
    }

    public FlowBean() {
        super();
    }

    public void setSumFlow(){
        this.totalFlow=this.upFlow + this.downFlow;
    }

    @Override
    public void write(DataOutput out) throws IOException {
        out.writeLong(this.downFlow);
        out.writeLong(this.upFlow);
        out.writeLong(this.totalFlow);
    }

    @Override
    public void readFields(DataInput in) throws IOException {
        this.downFlow = in.readLong();
        this.upFlow = in.readLong();
        this.totalFlow = in.readLong();
    }

    @Override
    public int compareTo(FlowBean o) {
        return (this.totalFlow <o.totalFlow ? -1 : (this.totalFlow ==o.totalFlow ? 0 : 1));
    }

    @Override
    public String toString() {
        return  downFlow + "\t" + upFlow + "\t" + totalFlow ;
    }
}

FlowMapper类

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

/**
 * @author tenic
 * @version 1.0
 * @Date 2021-04-13
 * @Desc
 */
public class FlowMapper extends Mapper<LongWritable,Text, Text,FlowBean> {

    private FlowBean flowBean = new FlowBean();
    private Text outK = new Text();


    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String line = value.toString();
        String[] args = line.split("\t");
        //1	12309866412		192.168.56.1	www.baidu.com		10	30	200
        //一行数据形式如上边所示,经过制表符分割后形成的数组形式里,
        // 下标为1的是手机号码,下标是4的是下载流量,下标是5的是上传流量
        outK.set(args[1]);
        flowBean.setDownFlow(Long.parseLong(args[args.length-3]));
        flowBean.setUpFlow(Long.parseLong(args[args.length-2]));
        flowBean.setSumFlow();

        context.write(outK,flowBean);
    }
}

FlowReducer类

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

/**
 * @author tenic
 * @version 1.0
 * @Date 2021-04-13
 * @Desc
 */
public class FlowReducer extends Reducer<Text,FlowBean, Text,FlowBean> {

    @Override
    protected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException {
        // 18732919322   [{"upFlow":203,"downFlow":123,"totalFlow":326},{……}]
        //上边的是mapper输出的结果信息,我们可以看到前边是手机号,后边是一个flowbean的list对象
        Long upFlow = 0L;
        Long downFlow = 0L;
        for (FlowBean value : values) {
            upFlow += value.getUpFlow();
            downFlow += value.getDownFlow();
        }
        FlowBean flowBean = new FlowBean();
        flowBean.setDownFlow(downFlow);
        flowBean.setUpFlow(upFlow);
        flowBean.setSumFlow();

        context.write(key,flowBean);

    }
}

FlowBeanDriver类

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.output.FileOutputFormat;


/**
 * @author tenic
 * @version 1.0
 * @Date 2021-04-13
 * @Desc
 */
public class FlowBeanDriver {

    public static void main(String[] args) throws Exception {
        Configuration config = new Configuration();
        Job job = Job.getInstance(config);
        //设置启动类
        job.setJarByClass(FlowBeanDriver.class);
        //设置mapper和reducer类
        job.setMapperClass(FlowMapper.class);
        job.setReducerClass(FlowReducer.class);
        //设置mapper输出的key和value类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(FlowBean.class);
        //设置最后reducer输出的key和value的类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);
        //输入输出路径信息
        FileInputFormat.setInputPaths(job,new Path("/Users/waterair/Documents/shelltest/mr/demo3/input/demo1.txt"));
        FileOutputFormat.setOutputPath(job,new Path("/Users/waterair/Documents/shelltest/mr/demo3/output1/"));

        System.exit(job.waitForCompletion(true)?0:1);
    }
}

运行我们的driver类,我们可以到我们的输出路径上看到我们的结果,是按手机号码排序的一个结果
如下图:
输入文件:

输出文件:

总结

具体实现对象序列化步骤如下:

  1. 必须实现WritableComparable接口
  2. 反序列化时,需要反射空构造函数,必须有空构造函数
  3. 重写序列化write方法
  4. 重写反序列化readFields方法
  5. 注意反序列化的顺序和序列化顺序完全一致
  6. 要想把结果显示在文件中,需要重写toString(),可用'\t'分开,方便后续使用。
  7. 如果需要将自定义的bean放在key中传输,则还需要实现Comparable接口的方法,
    因为MapReduce框架中有排序的过程,必须要按某一个key进行升序排列
posted @ 2021-04-13 23:53  Tenic  阅读(84)  评论(0编辑  收藏  举报