序列化与反序列化

博客详细介绍了在Hadoop环境下,如何利用自定义序列化类FlowBean,配合MapReduce计算框架统计每个手机号的上行、下行及总流量。Map阶段处理原始数据,Reducer阶段进行流量汇总。整个过程涉及到了数据的序列化与反序列化,以提高传输效率。
摘要由CSDN通过智能技术生成


前言

1.说明:

什么是序列化、反序列化?
在操作系统中,当两个进程在进行远程通信时,彼此可以发送各种类型的数据,包括文本、图片、视频、音频等。两者之间传输的数据其形式就是二进制序列。那么如果我们需要将Java对象进行传输的时候,是不是也应该先将对象进行序列化?答案是肯定的,我们需要先将Java对象进行序列化,然后通过网络,IO进行传输,当到达目的地之后,再进行反序列化获取到我们想要的对象,最后完成通信。(就比如我们到街上去买菜;为了方便,我们会将菜放入塑料袋,而后到家则从塑料袋中将菜拿出来,这样能更好的运输。这就是一个简单的序列化例子)
序列化:将对象转换为二进制序列在网络中传输或保存到磁盘 反序列化:从网络或磁盘中将二进制序列转换为对象

2. 为什么要序列化

一般来说,“活的”对象只生存在内存里,关机断电就没有了。而且“活的”对象只能由本地的进程使用,不能被发送到网络上的另外一台计算机。 然而 序列化可以存储“活的”对象,可以将“活的”对象发送到远程计算机。

3. 为什么不用 Java的序列化

Java的序列化是一个重量级序列化框架( Serializable),一个对象被序列化后,会附带很多额外的信息(各种校验信息, Header,继承体系等),不便于在网络中高效传输。所以Hadoop自己开发了一套序列化机制( Writable)。

4. Hadoop序列化特点:

紧凑 :高效使用存储空间
快速 :读写数据的额外开销小
互操作 :支持多语言的交互

序列化实例:

统计每一个手机号耗费的总上行流量、总下行流量、总流量。
(1)创建一个文本文档输入数据:

格式:id   手机号           网络ip        域名            上行流量  下行流量      网络状态码
        1	13736230513	192.196.100.1   www.google.com	   2481     24681	    200
        2	13846544121	192.196.100.2	  www.xiaosh.com	264 	  0	        200
        3 	13956435636	192.196.100.3			            132	     1512	    200
        4 	13966251146	192.168.100.1			            240	      0	        404
        5 	18271575951	192.168.100.2	  www.google.com	1527     2106	    200

基本思路(基于mapreduce计算框架):
Map阶段: (1)读取一行数据,切分字段 (2)抽取手机号、上行流量、下行流量 (3)以手机号为key,bean对象为value输出,即context.write(手机号,bean);
Reduce阶段:
(1)累加上行流量和下行流量得到总流量。 (2)实现自定义的bean来封装流量信息,并将bean作为map输出的key来传输 (3)MR程序在处理数据的过程中会对数据排序(map输出的kv对传输到reduce之前,会排序),排序的依据是map输出的key 所以,我们如果要实现自己需要的排序规则,则可以考虑将排序因素放到key中,让key实现接口:WritableComparable

分析:
在这里插入图片描述
分析图:
在这里插入图片描述

代码

1.FlowBean类代码:

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Writable;
    public class FlowBean implements Writable {
        /* 在企业开发中往往常用的基本序列化类型不能满足所有需求 比如在 
        hadoop框架内部传递一个 bean对象,那么该对象就需要实现序列化接口。
        bean对象序列化  必须实现 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;
            this.sumFlow = upFlow + downFlow;//总流量
        }
        public void set(long upFlow, long downFlow) {
            this.upFlow = upFlow;
            this.downFlow = downFlow;
            this.sumFlow = upFlow + downFlow;
        }
        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;
        }
        /**
         * 序列化与反序列化
         * 序列化:将对象转化为二进制序列在网络中传播或在磁盘中保存
         * (将序列化写入IO流)
       
         * 反序列化:在网络或磁盘中将二进制序列转换为对象
         * (在IO流序列化中恢复(读取)对象)
         */
        //序列化
        @Override
        public void write(DataOutput out) throws IOException { //写
            out.writeLong(upFlow);
            out.writeLong(downFlow);
            out.writeLong(sumFlow);
        }
        //反序列化(注意反序列化的顺序和序列化的顺序完全一致)
        @Override
        public void readFields(DataInput in) throws IOException { //读
            this.upFlow = in.readLong();
            this.downFlow = in.readLong();
            this.sumFlow = in.readLong();
        }
        @Override
        public String toString() { //自定义输出方式
            return upFlow + "\t" + downFlow + "\t" + sumFlow;
        }
}

2.FlowMapper类代码:

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
public class FlowMapper extends Mapper<LongWritable, Text,Text,FlowBean>{
    FlowBean bean = new FlowBean();
    Text k = new Text();
    @Override
    protected void map(LongWritable key, Text value,Context context) throws IOException, InterruptedException {
            //1 获取一行数据,并将它转换为字符串
//比如2  13846544121    192.196.100.2    www.xiaosh.com   264      0            200
            String line = value.toString();
            //2 以制表符截取字段
//截取为[2,13846544121,192.196.100.2,www.xiaosh.com,264,0,200]字符串这样的数组
            String[] words = line.split("\t");
            //3 获取所需信息:13846544121   264   0
            String phoneNum = words[1]; //电话号码在数组第2个位置
            long upFlow = Long.parseLong(words[words.length - 3]);//上行流量在数组倒数第3个
            long downFlow = Long.parseLong(words[words.length - 2]);//下行流量在数组倒数第2个
            //4 封装到k,v中
             k.set(phoneNum);
             bean.set(upFlow,downFlow);
            //4 写出去
            context.write(k,bean);
    }
}

3.FlowReducer类代码:

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class FlowReducer extends Reducer<Text,FlowBean,Text,FlowBean> {
    @Override
    protected void reduce(Text key, Iterable<FlowBean> values, Context context)throws IOException, InterruptedException{
        // 计算总的流量
        long sumUpFlow = 0;
        long sumDownFlow = 0;
        for (FlowBean bean : values) { //遍历每个values,将其中的上行流量下行流量进行累加
            sumUpFlow += bean.getUpFlow();
            sumDownFlow += bean.getDownFlow();
        }
        //输出
        context.write(key, new FlowBean(sumUpFlow, sumDownFlow));
    }
}

4.FlowDriver类代码:

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;
public class FlowDriver {
    public static void main(String[] args) throws Exception {
            Configuration configuration = new Configuration();
            //1、获取job信息
            Job job = Job.getInstance( configuration);
            //2、获取jar的存储路径
            job.setJarByClass(FlowDriver.class);
            //3、关联map和reduce的class类
            job.setMapperClass(FlowMapper.class);
            job.setReducerClass(FlowReducer.class);
            //4、设置map阶段输出的key和value类型
            job.setMapOutputKeyClass(Text.class);
            job.setMapOutputValueClass(FlowBean.class);
            //5、设置最后输出数据的key和value类型
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(FlowBean.class);
            //6、设置输入数据的路径和输出数据的路径
            FileInputFormat.setInputPaths(job,new Path("hdfs://hadoop01:9000/bigdata/input.txt"));
            FileOutputFormat.setOutputPath(job,new Path("hdfs://hadoop01:9000/bigdata/mr02"));
            //7、提交
            boolean result = job.waitForCompletion(true);
            System.exit(result ? 0 : 1);
  //区别在于:system.exit(0):正常退出,程序正常执行结束退出
   // system.exit(1):是非正常退出,就是说无论程序正在执行与否,都退出
    }
}

运行截图(格式为:手机号 上+下=总):
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值