【Hadoop】:MapReduce实现序列化

一.序列化简介

什么是序列化呢?

 

 

序列化:对象———》字节序列

反序列化:字节序列——》对象

备注:对象在内存(RAM)当中

字节序列:可以在磁盘(ROM)当中,也可以在网络当中进行传输

序列化的根本缘故:将对象从RAM里的数据 转化成ROM里的数据

二.序列化案例

我们这里将要编写的序列化的程序的流程如下图所示,是一个统计手机耗费总流量的case:

 

 对于这个案例而言。为什么需要进行序列化呢?

因为在第三阶段,我们将手机的上行流量和下行流量都分别封装进了一个对象当中,一个手机号对应两个流量。因此一个bean对象(就是一个普通的对象,拥有方法,属性等)当中具有多个数据,因此需要进行序列化。

三.编写Bean类

现在我们开始封装这个手机的数据,代码如下所示。代码主要是为了封装value,也就是手机的上行流量以及下行流量,这里不处理手机号,不将手机号进行封装。因为手机号在我们的mapper阶段,我们将其视为Key同时注意要想把结果显示在文件中,需要重写toString(),且用"\t"分开,方便后续用。

import org.apache.hadoop.io.Writable;

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

//建立每一个手机号所对应的对象
public class FlowBean implements Writable {


    private long upFlow;//上行流量
    private long downFlow;//下行流量
    private long sumFlow;//总流量


    //空参构造,为了后续能够反射
    public FlowBean()
    {
        super();
    }

    public FlowBean(long upFlow,long downFlow)
    {
        super();
        this.upFlow=upFlow;
        this.downFlow=downFlow;
        sumFlow=upFlow+upFlow;
    }


    //序列化方法,这样这个对象就可以很方便地进行序列化和反序列化了!
    //序列化的方法必须和反序列化相同
    @Override
    public void write(DataOutput dataOutput) throws IOException {
        dataOutput.writeLong(upFlow);
        dataOutput.writeLong(downFlow);
        dataOutput.writeLong(sumFlow);
    }
    //反序列方法
    @Override
    public void readFields(DataInput dataInput) throws IOException {
        //必须要求和序列化要求顺序一致,顺序一致就可以进行接收
        upFlow=dataInput.readLong();
        downFlow= dataInput.readLong();
        sumFlow=dataInput.readLong();
    }

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

    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 getSumFlow() {
        return sumFlow;
    }

    public void setSumFlow(long sumFlow) {
        this.sumFlow = sumFlow;
    }

    public void set(long upFlow,long downFlow)
    {
        upFlow=upFlow;
        downFlow=downFlow;
        sumFlow=upFlow+downFlow;
    }

}

在编写这个bean类当中,我们拥有了一个bean类所有的特征,比如get/set方法,以及需要拥有的序列化以及反序列化方法,可以在后续调用这个bean对象的时候,更加方便地进行序列化和反序列化。

四.编写Mapper类

将获得的手机号码设定为key,手机号的上行流量和下行流量分别设定为value,value使用bean类FlowBean来表示。(因为有两个value,而在mapper里面又只能够有一个输出,因此只能使用一个类来代表mapper的输出(valueout)了)

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

public class FlowCountMapper extends Mapper<LongWritable, Text,Text,FlowBean> {
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        Text k=new Text();
        FlowBean v=new FlowBean();

        //1.获取一行,tostring方法已经被改写,因此
         String line=value.toString();
        //2.切割
        String[] fields=line.split("\t");
        //3.封装对象
        k.set(fields[1]);//封装手机号
        //封装
        long upFlow= Long.parseLong(fields[fields.length-3]);
        long downFlow=Long.parseLong(fields[fields.length-2]);
        v.setUpFlow(upFlow);
        v.setUpFlow(downFlow);
        //v.set();
        //4.写出
        context.write(k,v);
    }
}

五.编写Reducer类

import org.apache.hadoop.mapreduce.Reducer;
import javax.xml.soap.Text;
import java.io.IOException;

public class FlowCountReducer extends Reducer<Text,FlowBean,Text,FlowBean> {
   FlowBean v=new FlowBean();
    @Override
    protected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException {
        long sum_upFlow=0;
        long sum_downflow=0;
        //1.累加求和
        for (FlowBean flowBean:values)
        {
            sum_upFlow+=flowBean.getUpFlow();
            sum_downflow+=flowBean.getDownFlow();
        }

       v.set(sum_upFlow,sum_downflow);

        //2.写出
        context.write(key,v);
    }
}

六.编写Driver类

编写driver类是一个固定的步骤,可以直接根据注释当中的步骤进行编写即可,代码如下所示:

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import javax.xml.soap.Text;
import java.io.IOException;

public class FlowsumDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        Configuration configuration=new Configuration();
        //1.获取job对象
        Job job=Job.getInstance(configuration);
        //2.设置jar路径
        job.setJarByClass(FlowsumDriver.class);
        //3.关联mapper和reducer
        job.setMapperClass(FlowCountMapper.class);
        job.setReducerClass(FlowCountReducer.class);
        //4.设置mapper输出的key和value类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputKeyClass(FlowBean.class);
        //5.设置最终输出的key和value类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);
        //6.设置输出路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job,new Path(args[1]));
        //7.提交job

        boolean result=job.waitForCompletion(true);
        System.out.println(result);
    }
}

这样我们就可以完成这个任务了!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章