Hadoop学习视频心得(四)MapReduce的InputFormat阶段

1、Hadoop序列化

1)、概述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LVXHdMt7-1609230843359)(file:///C:\Users\XIAOYO~1\AppData\Local\Temp\ksohtml2020\wps2.png)]

2)、提问:为什么不用java序列化框架?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LNbfq15C-1609230843361)(file:///C:\Users\XIAOYO~1\AppData\Local\Temp\ksohtml2020\wps3.png)]

3)、举例:统计每一个手机号耗费的总上行流量、下行流量、总流量

​ mapper–>reduce需要经历网络传输,数据需要序列化,类似于把数据装入一个容器内(序列化的时候框架会调用write方法),reduce拿到这个数据后要先打开这个容器(反序列化的时候框架调用readFilelds方法),会创建一个对象,让传过来的数据填充这个类(注意会顺序的放顺序的拿)

①、Bean类的书写

public class FlowBean implements Writable {
    private long upFlow;
    private long downFlow;
    private long sumFlow;

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

    @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;
    }

    /**
     * 序列化:将对象数据写到框架指定的地方
     *
     * @param dataOutput 数据的容器
     * @throws IOException
     */
    public void write(DataOutput dataOutput) throws IOException {
        dataOutput.writeLong(upFlow);
        dataOutput.writeLong(downFlow);
        dataOutput.writeLong(sumFlow);
    }

    /**
     * 反序列化:从框架指定的地方读取数据填充对象
     *
     * @param dataInput 数据的容器
     * @throws IOException
     */
    public void readFields(DataInput dataInput) throws IOException {
        this.upFlow = dataInput.readLong();
        this.downFlow = dataInput.readLong();
        this.sumFlow = dataInput.readLong();
    }
}

②、Mapper的书写

public class FlowMapper extends Mapper<LongWritable, Text, Text, FlowBean> {

    private Text phone = new Text();
    private FlowBean flow = new FlowBean();

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //1、拿到一行数据
        String line = value.toString();
        //2、切分
        String[] fileds = line.split("\t");
        //3、封装
        phone.set(fileds[1]);
        /**
         * 这里解释一下为什么是length-3、-2,因为这个文件正着数位置不一定所以倒着来
         */
        flow.set(Long.parseLong(fileds[fileds.length - 3]),//upFlow
                Long.parseLong(fileds[fileds.length - 2])//downFlow
        );

        context.write(phone, flow);
    }
}

③、Reducer的书写

public class FlowReducer extends Reducer<Text, FlowBean, Text, FlowBean> {

    private FlowBean flow = new FlowBean();

    @Override
    protected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException {
        //累加流量
        long sumUpFlow = 0;
        long sumDownFlow = 0;


        for (FlowBean value : values) {
            sumUpFlow += value.getUpFlow();
            sumDownFlow += value.getDownFlow();
        }

        //封装Flow类型
        flow.set(sumUpFlow, sumDownFlow);

        context.write(key, flow);
    }
}

④、Driver的书写

​ 这边我是直接把要处理的文件放在了d:/DATA/input目录下,然后把处理完的文件放在了d:/DATA/output下(注意这个处理完的文件夹不能有,得让它处理自己创建)

public class FlowDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        Job job = Job.getInstance(new Configuration());

        job.setJarByClass(FlowDriver.class);

        job.setMapperClass(FlowMapper.class);
        job.setReducerClass(FlowReducer.class);

        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(FlowBean.class);

        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);

        FileInputFormat.setInputPaths(job, new Path("d:/DATA/input"));
        FileOutputFormat.setOutputPath(job, new Path("d:/DATA/output"));

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

⑤、附录要处理的文件内容(txt)

1 13736230513 192.196.100.1 www.atguigu.com 2481 24681 200
2 13846544121 192.196.100.2 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.atguigu.com 1527 2106 200
6 84188413 192.168.100.3 www.atguigu.com 4116 1432 200
7 13590439668 192.168.100.4 1116 954 200
8 15910133277 192.168.100.5 www.hao123.com 3156 2936 200
9 13729199489 192.168.100.6 240 0 200
10 13630577991 192.168.100.7 www.shouhu.com 6960 690 200
11 15043685818 192.168.100.8 www.baidu.com 3659 3538 200
12 15959002129 192.168.100.9 www.atguigu.com 1938 180 500
13 13560439638 192.168.100.10 918 4938 200
14 13470253144 192.168.100.11 180 180 200
15 13682846555 192.168.100.12 www.qq.com 1938 2910 200
16 13992314666 192.168.100.13 www.gaga.com 3008 3720 200
17 13509468723 192.168.100.14 www.qinghua.com 7335 110349 404
18 18390173782 192.168.100.15 www.sogou.com 9531 2412 200
19 13975057813 192.168.100.16 www.baidu.com 11058 48243 200
20 13768778790 192.168.100.17 120 120 200
21 13568436656 192.168.100.18 www.alibaba.com 2481 24681 200
22 13568436656 192.168.100.19 1116 954 200

2、InputFormat数据输入

​ InputFormat的作用:1、切片 2、将数据变成<k,v>值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S6kvEMWU-1609230843362)(file:///C:\Users\XIAOYO~1\AppData\Local\Temp\ksohtml5284\wps1.png)]

1)、切片与MapTask并行度决定机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Galf2xpR-1609230843363)(file:///C:\Users\XIAOYO~1\AppData\Local\Temp\ksohtml5284\wps2.png)]

注意:上图的第四点,是针对每个文件(假如10个文件每个文件10M,那就应该切分成10个切片)

2)、Job提交流程源码和切片源码详解

①、Job提交流程源码详解
waitForCompletion()

submit();

// 1建立连接
	connect();	
		// 1)创建提交Job的代理
		new Cluster(getConfiguration());
			// (1)判断是本地yarn还是远程
			initialize(jobTrackAddr, conf); 

// 2 提交job
submitter.submitJobInternal(Job.this, cluster)
	// 1)创建给集群提交数据的Stag路径
	Path jobStagingArea = JobSubmissionFiles.getStagingDir(cluster, conf);

	// 2)获取jobid ,并创建Job路径
	JobID jobId = submitClient.getNewJobID();

	// 3)拷贝jar包到集群
copyAndConfigureFiles(job, submitJobDir);	
	rUploader.uploadFiles(job, jobSubmitDir);

// 4)计算切片,生成切片规划文件
writeSplits(job, submitJobDir);
		maps = writeNewSplits(job, jobSubmitDir);
		input.getSplits(job);

// 5)向Stag路径写XML配置文件
writeConf(conf, submitJobFile);
	conf.writeXml(out);

// 6)提交Job,返回提交状态
status = submitClient.submitJob(jobId, submitJobDir.toString(), job.getCredentials());
②、FileInputFormat切片源码解析(input.getSplits(job))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jxnduo0L-1609230843364)(file:///C:\Users\XIAOYO~1\AppData\Local\Temp\ksohtml5284\wps3.png)]

​ 这边可能会有疑问,如果切片在一行中间断开怎么办?这边LinerRecordReader里面做了处理会将这一行都归于上个切片,下一个从下一行开始。

3)、FileInputFormat实现类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yFfABd1A-1609230843365)(file:///C:\Users\XIAOYO~1\AppData\Local\Temp\ksohtml5284\wps4.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MCDgKPNs-1609230843366)(file:///C:\Users\XIAOYO~1\AppData\Local\Temp\ksohtml5284\wps5.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v0sCPXDZ-1609230843366)(file:///C:\Users\XIAOYO~1\AppData\Local\Temp\ksohtml5284\wps6.png)]

​ 其中TextInputFormat和NLineInputFormat的返回值相同,而NLineInputFormat的不同之处就是是按照指定的行数来切分!

4)、小文件的处理——CombineTextInputFormat切片机制

①、应用场景:

​ CombineTextInputFormat用于小文件过多的场景,它可以将多个小文件从逻辑上规划到一个切片中,这样,多个小文件就可以交给一个MapTask处理。

②、虚拟存储切片最大值设置

​ CombineTextInputFormat.setMaxInputSplitSize(job, 4194304);// 4m

注意:虚拟存储切片最大值设置最好根据实际的小文件大小情况来设置具体的值。

③、切片机制

​ 生成切片过程包括:虚拟存储过程和切片过程二部分。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yrtwm7Ju-1609230843367)(file:///C:\Users\XIAOYO~1\AppData\Local\Temp\ksohtml5284\wps7.png)]

​ 注意:如果是10M文件会怎么处理?会先将4M划分一块,剩下的6M均分为3M一块、3M一块,总计:4M、3M、3M。

5)、自定义InputFormat

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wp6JJrER-1609230843367)(file:///C:\Users\XIAOYO~1\AppData\Local\Temp\ksohtml5284\wps8.png)]

举例说明:

​ 题目:将多个小文件合并成一个SequenceFile文件(SequenceFile文件是Hadoop用来存储二进制形式的key-value对的文件格式),SequenceFile里面存储着多个文件,存储的形式为文件路径+名称为key,文件内容为value。

①、MyInputFormat.java
public class MyInputFormat extends FileInputFormat<Text, BytesWritable> {

    /**
     * 返回一个自定义的RecordReader
     * @param split
     * @param context
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public RecordReader<Text, BytesWritable> createRecordReader(InputSplit split, TaskAttemptContext context) throws IOException, InterruptedException {
        return new MyRecordReader();
    }
}
②、MyRecordReader.java
/**
 * 负责将整个文件转换成一组key,value对(要么不读要么全读)
 */
public class MyRecordReader extends RecordReader<Text, BytesWritable> {

    //表示文件读的状态,默认为false,表示文件还没读
    private boolean isRead = false;

    //kv对
    private Text key = new Text();
    private BytesWritable value = new BytesWritable();

    private FSDataInputStream inputStream;
    private FileSplit fs;

    /**
     * 初始化方法,一般执行一些初始化操作
     *
     * @param split
     * @param context
     * @throws IOException
     * @throws InterruptedException
     */
    public void initialize(InputSplit split, TaskAttemptContext context) throws IOException, InterruptedException {
        //开流

        fs = (FileSplit) split;

        FileSystem fileSystem = FileSystem.get(context.getConfiguration());
        inputStream = fileSystem.open(fs.getPath());
    }

    /**
     * 读取下一组的key,value对
     *
     * @return 是否读到
     * @throws IOException
     * @throws InterruptedException
     */
    public boolean nextKeyValue() throws IOException, InterruptedException {
        if (!isRead) {
            //读取这个文件
            //填充key
            key.set(fs.getPath().toString());

            // 填充value
            byte[] buffer = new byte[(int) fs.getLength()];
            int read = inputStream.read(buffer);

            value.set(buffer, 0, buffer.length);
            //标记文件读完
            isRead = true;
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获取当前的key
     *
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public Text getCurrentKey() throws IOException, InterruptedException {
        return key;
    }

    /**
     * 获取当前读到的value
     *
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public BytesWritable getCurrentValue() throws IOException, InterruptedException {
        return value;
    }

    /**
     * 显示进度
     *
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public float getProgress() throws IOException, InterruptedException {
        return isRead ? 1 : 0;
    }

    /**
     * 关闭方法,一般用来关闭资源
     *
     * @throws IOException
     */
    public void close() throws IOException {
        //关流
        IOUtils.closeStream(inputStream);
    }
}
③、MyInputDriver.java
public class MyInputDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        BasicConfigurator.configure();
        Job job = Job.getInstance(new Configuration());

        job.setJarByClass(MyInputDriver.class);

        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(BytesWritable.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(BytesWritable.class);

        job.setInputFormatClass(MyInputFormat.class);
        job.setOutputFormatClass(SequenceFileOutputFormat.class);

        FileInputFormat.setInputPaths(job, new Path("d:/DATA/input"));
        FileOutputFormat.setOutputPath(job, new Path("d:/DATA/output"));

        boolean b = job.waitForCompletion(true);
        System.exit(b ? 0 : 1);
    }
}
④、附带题目文件内容(文件名+文件内容)

在这里插入图片描述

1.txt

yongpeng weidong weinan
sanfeng luozong xiaoming

2.txt

longlong fanfan
mazong kailun yuhang yixin
longlong fanfan
mazong kailun yuhang yixin

3.txt

shuaige changmo zhenqiang
dongli lingu xuanxuan

⑤、最终结果截图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WSAwRstW-1609230843368)(C:\Users\xiaoyoupei\AppData\Roaming\Typora\typora-user-images\image-20201229163308316.png)]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

友培

数据皆开源!

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

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

打赏作者

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

抵扣说明:

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

余额充值