Hadoop(十)MapReduce 序列化

 当输出是多个字段是,需要使用对象来表示,此时这个对象要序列化 

序列化概念

    Java 的序列化是一个重量级序列化框架(Serializable),一个对象被序列化后,会附带很多额外的信息(各种校验信息,Header,继承体系等),不便于在网络中高效传输。所以,Hadoop 自己开发了一套序列化机制(Writable)
   在编写MapReduce程序时,我们会发现,对于MapReduce的输入输出数据(key-value),我们只能使用Hadoop提供的数据类型,而不能使用Java本身的基本数据类型,比如,如果数据类型为long,那么在编写MR程序时,对应Hadoop的数据类型则为LongWritable。关于原因,简单说明如下:hadoop在节点间的内部通讯使用的是RPC,RPC协议把消息翻译成二进制字节流发送到远程节点,
远程节点再通过反序列化把二进制流转成原始的信息。也就是说,传递的消息内容是需要经过hadoop特定的序列化与反序列化操作的,因此,才需要使用hadoop提供的数据类型,当然,如果想要自定义MR程序中key-value的数据类型,则需要实现相应的接口,如Writable、WritableComparable接口
也就是说,如果需要自定义key-value的数据类型,必须要遵循如下的原则:

/**
 * MapReduce的任意的key和value都必须要实现Writable接口
 * MapReduce的任意key必须实现WritableComparable接口,WritableComparable是Writable的增强版
 * key还需要实现Comparable的原因在于,对key排序是MapReduce模型中的基本功能
 */


常用数据类型对应的 Hadoop 数据序列化类型

自定义序列化数据类型的步骤

(1)必须实现Writable接口
(2)反序列化时,需要反射调用空参构造函数,所以必须有空参构造
(3)重写序列化方法
(4)重写反序列化方法
(5)注意反序列化的顺序和序列化的顺序完全一致
(6)要想把结果显示在文件中,需要重写 toString(),可用 \t 分开,方便后续用
(7)如果需要将自定义的 bean 放在 key 中传输,则还需要实现Comparable 接口,因为 MapReduce 框中的 Shuffle 过程要求对 key 必须能排序

在MapReduce中,要求被序列化的对象对应的类中必须提供无参构造在MapReduce中,要求被序列化的对象的属性值不能为null

案例统计每个手机号的上下行流量

手机号,上行流量,下行流量 peopleinfo.txt

1880349000110,4321,7000

1880349000111,4121,6000

1880349000112,4021,3000

1880349000110,2321,9000

1880349000112,3000,4000

上传到服务器上,这里在idea里安装插件 hdfs big data tools工具完成上传

编写代码

Mapper

package flow;

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

import java.io.IOException;
// 用于完成Map阶段
// 再MapReduce中,要求被处理的数据能够被序列化
// MApReduce提供了一套单独的序列化机制
// KEYIN-输入的键的类型。如果不指定,那么默认情况下,表示行的字节偏移量
// VALUEIN-输入值得类型。如果不指定,那么默认情况下,表示的读取到的一行数据
// KEYOUT-输出的键的类型。当前案例中,输出的键表示的是手机号
// VALUEOUT-输出的值的类型。当前案例,输出的值表示的是上下行流量
public class FlowMapper extends Mapper<LongWritable, Text, Text,PeopleInfo> {

    // 覆盖map方法,将处理逻辑写到这个方法中
    // key:键。表示的是行的字节偏移量
    // value:值。表示读取到的一行数据
    // context:配置参数

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String[] array = value.toString().split(",");
        PeopleInfo  p = new PeopleInfo();
        p.setUpFlow(Integer.parseInt(array[1]));
        p.setDownFlow(Integer.parseInt(array[2]));
        context.write(new Text(array[0]),p);
    }
}

Reducer

package flow;

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

import java.io.IOException;

// KEYIN、VALUEIN输入的键的类型。
// Reducer的数据从Mapper来的
// 所以Mapper的输出就是Reducer的输入
// KEYOUT、VALUEOUT-输出的值的类型。当前案例中,要输出每一个手机号对应的上下行流量
public class FlowReducer  extends Reducer<Text, PeopleInfo,Text,Text> {

    protected void reduce(Text key, Iterable<PeopleInfo> values, Context context) throws IOException, InterruptedException {
int sumup = 0;
int sumdown = 0;
        for (PeopleInfo p : values) {
            sumup+= p.getUpFlow();
            sumdown+= p.getDownFlow();
        }
        //3、写入上下文
        context.write(key,new Text("上行流量:"+sumup+"下行流量:"+sumdown));
    }
}

Driver

package flow;



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.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;



import java.io.IOException;

public class JobMain {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

        //一、初始化Job
        Configuration configuration = new Configuration();

        //获取运行命令的参数,参数一:输入文件路径,参数二:输出文件路径
        //如果输入路径是一个文件,那么只处理这个文件,如果指定的路径是目录,则处理这个目录下的所有文件
        //输出路径只能是不存在的目录名
        String [] otherArgs = new GenericOptionsParser(configuration,args).getRemainingArgs();
        if(otherArgs.length < 2){
            System.err.println("必须提供输入文件路径和输出文件路径");
            System.exit(2);
        }
        Job job = Job.getInstance(configuration, "mr");
        job.setJarByClass(JobMain.class);

        //二、设置Job的相关信息 8个小步骤
        //1、设置输入路径
        job.setInputFormatClass(TextInputFormat.class);
        //本地运行
        //TextInputFormat.addInputPath(job,new Path("/tmp/input/mr1.txt"));
        TextInputFormat.addInputPath(job,new Path(args[0]));

        //2、设置Mapper类型,并设置输出键和输出值
        job.setMapperClass(FlowMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(PeopleInfo.class);

        //shuffle阶段,使用默认的
        //3、设置Reducer类型,并设置输出键和输出值
        job.setReducerClass(FlowReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);

        //4、设置输出路径
        job.setOutputFormatClass(TextOutputFormat.class);
        //本地运行
        //TextOutputFormat.setOutputPath(job,new Path("/tmp/output/mr"));
        TextOutputFormat.setOutputPath(job,new Path(args[1]));
        //三、等待完成
        boolean b = job.waitForCompletion(true);
        System.out.println(b==true?"MapReduce 任务执行成功!":"MapReduce 任务执行失败!");
        System.exit(b ? 0 : 1);
    }
}

对象实体

package flow;

import org.apache.hadoop.io.Writable;

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

public class PeopleInfo implements Writable {
    private int upFlow;
    private int downFlow;
    public int getDownFlow() {
        return downFlow;
    }
    public void setDownFlow(int downFlow) {
        this.downFlow = downFlow;
    }
    public int getUpFlow() {
        return upFlow;
    }
    public void setUpFlow(int upFlow) {
        this.upFlow = upFlow;
    }
    // 需要将有必要的属性依次序列化写出即可
    @Override
    public void write(DataOutput out) throws IOException {
        out.writeInt(getUpFlow());
        out.writeInt(getDownFlow());
    }

    @Override
    public void readFields(DataInput in) throws IOException {
        setUpFlow(in.readInt());
        setDownFlow(in.readInt());
    }

}

打成jar包上传

执行

 hadoop jar /hadoopmapreduce-1.0-SNAPSHOT.jar flow.JobMain /peopleinfo.txt /mypeopleinfo

浏览输出文件内容

 hadoop fs -cat /mypeopleinfo/part-r-00000

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Allen019

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值