目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages

MapReduce精通(一)

MapReduce入门

MapReduce定义

MapReduce优缺点

优点

缺点

MapReduce核心思想

总结:分析WordCount数据流走向深入理解MapReduce核心思想。

MapReduce进程

MapReduce编程规范

用户编写的程序分成三个部分:Mapper、Reducer和Driver。

WordCount案例实操

  1. 需求

    在给定的文本文件中统计输出每一个单词出现的总次数

    1. 输入数据

    2. 期望输出数据

      hadoop 2

      hello 4

      spark 1

      world 3

  2. 需求分析

    按照MapReduce编程规范,分别编写Mapper,Reducer,Driver,如图所示。

  3. 环境准备

    1. 创建maven工程

    2. 在pom.xml文件中添加如下依赖

      1. <dependencies>
      2. <dependency>
      3. <groupId>junit</groupId>
      4. <artifactId>junit</artifactId>
      5. <version>RELEASE</version>
      6. </dependency>
      7. <dependency>
      8. <groupId>org.apache.logging.log4j</groupId>
      9. <artifactId>log4j-core</artifactId>
      10. <version>2.8.2</version>
      11. </dependency>
      12. <dependency>
      13. <groupId>org.apache.hadoop</groupId>
      14. <artifactId>hadoop-common</artifactId>
      15. <version>2.7.2</version>
      16. </dependency>
      17. <dependency>
      18. <groupId>org.apache.hadoop</groupId>
      19. <artifactId>hadoop-client</artifactId>
      20. <version>2.7.2</version>
      21. </dependency>
      22. <dependency>
      23. <groupId>org.apache.hadoop</groupId>
      24. <artifactId>hadoop-hdfs</artifactId>
      25. <version>2.7.2</version>
      26. </dependency>
      27. </dependencies>
    3. 在项目的src/main/resources目录下,新建一个文件,命名为“log4j.properties”,在文件中填入

      1. log4j.rootLogger=INFO, stdout
      2. log4j.appender.stdout=org.apache.log4j.ConsoleAppender
      3. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
      4. log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
      5. log4j.appender.logfile=org.apache.log4j.FileAppender
      6. log4j.appender.logfile.File=target/spring.log
      7. log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
      8. log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
  4. 编写程序

    1. 编写Mapper类

      1. public class WordcountMapper extends Mapper<LongWritable, Text, Text, IntWritable{
      2. Text k = new Text();
      3. IntWritable v = new IntWritable(1);
      4. @Override
      5. protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
      6. // 1 获取一行
      7. String line = value.toString();
      8. // 2 切割
      9. String[] words = line.split(" ");
      10. // 3 输出
      11. for (String word : words) {
      12. k.set(word);
      13. context.write(k, v);
      14. }
      15. }
      16. }

      说明:

      注意:导包时,导入 org.apache.hadoop.mapreduce包下的类(2.0的新api)

      1. 自定义的类必须符合MR的Mapper的规范

      2. 在MR中,只能处理key-value格式的数据

        KEYIN, VALUEIN: mapper输入的k-v类型。 由当前Job的InputFormat的RecordReader决定!封装输入的key-value由RR自动进行。

        KEYOUT, VALUEOUT: mapper输出的k-v类型: 自定义

      3. InputFormat的作用:

        1. 验证输入目录中文件格式,是否符合当前Job的要求

        2. 生成切片,每个切片都会交给一个MapTask处理

        3. 提供RecordReader,由RR从切片中读取记录,交给Mapper进行处理

          方法: List getSplits: 切片

          RecordReader<K,V> createRecordReader: 创建RR

          默认hadoop使用的是TextInputFormat

          1. **TextInputFormat使用LineRecordReader**
          2. **LineRecordReader Treats keys as offset in file and value as line.**(即偏移量offset当做key,每一行当做value)
      4. 在Hadoop中,如果有Reduce阶段。通常key-value都需要实现序列化协议!

        MapTask处理后的key-value,只是一个阶段性的结果!

        这些key-value需要传输到ReduceTask所在的机器!

        将一个对象通过序列化技术,序列化到一个文件中,经过网络传输到另外一台机器,再使用反序列化技术,从文件中读取数据,还原为对象是最快捷的方式!

        hadoop开发了一款轻量级的序列化协议: Wriable机制!

    2. 编写Reducer类

      1. /*
      2. * 1. Reducer需要复合Hadoop的Reducer规范
      3. *
      4. * 2. KEYIN, VALUEIN: Mapper输出的keyout-valueout
      5. * KEYOUT, VALUEOUT: 自定义
      6. */
      7. public class WordcountReducer extends Reducer<Text, IntWritable, Text,IntWritable>{
      8. int sum;
      9. IntWritable v = new IntWritable();
      10. // reduce一次处理一组数据,key相同的视为一组
      11. @Override
      12. protected void reduce(Text key, Iterable<IntWritable> values,Context context) throws IOException,InterruptedException {
      13. // 1 累加求和
      14. sum = 0;
      15. for (IntWritable count : values) {
      16. sum += count.get();
      17. }
      18. // 2 输出
      19. v.set(sum);
      20. //将累加的值写出
      21. context.write(key,v);
      22. }
      23. }
    3. 编写Driver驱动类

      1. public class WordcountDriver {
      2. public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
      3. // 输入输出路径需要根据自己电脑上实际的输入输出路径设置
      4. args = new String[] { "F:/BaiduNetdiskDownload/mrinput/wordcount", "F:/BaiduNetdiskDownload/wordcount"};
      5. //Linux上的地址
      6. //args = new String[] { "/wcinput1", "/wcoutput1"};
      7. // 1 获取配置信息以及封装任务
      8. Configuration configuration = new Configuration();
      9. Job job = Job.getInstance(configuration);
      10. // 2 设置jar加载路径
      11. job.setJarByClass(WordcountDriver.class);
      12. // 3 设置map和reduce类
      13. job.setMapperClass(WordcountMapper.class);
      14. job.setReducerClass(WordcountReducer.class);
      15. // 4 设置map输出
      16. job.setMapOutputKeyClass(Text.class);
      17. job.setMapOutputValueClass(IntWritable.class);
      18. // 5 设置Reduce输出
      19. job.setOutputKeyClass(Text.class);
      20. job.setOutputValueClass(IntWritable.class);
      21. // 6 设置输入和输出路径
      22. FileInputFormat.setInputPaths(job, new Path(args[0]));
      23. FileOutputFormat.setOutputPath(job, new Path(args[1]));
      24. // 7 提交
      25. boolean result = job.waitForCompletion(true);
      26. System.exit(result ? 0 : 1);
      27. }
      28. }
  5. 本地测试

    直接运行WordcountDriver的main方法.查看结果

  6. 集群上测试

    1. 将程序打成jar包,然后拷贝到Hadoop集群中,修改jar包名称为wc.jar

    2. 启动Hadoop集群

    3. 执行WordCount程序

      1. [atguigu@hadoop102 ~]$ hadoop fs -mkdir /wcinput1
      2. [atguigu@hadoop102 ~]$ hadoop fs -put hello.txt /wcinput1
      3. [atguigu@hadoop102 ~]$ hadoop jar wc.jar com.atguigu.mr.wordcount.WordcountDriver /wcinput1 /wcoutput1

Hadoop序列化

序列化概述

常用数据序列化类型

Java类型 Hadoop Writable类型
boolean BooleanWritable
byte ByteWritable
int IntWritable
float FloatWritable
long LongWritable
double DoubleWritable
String Text
map MapWritable
array ArrayWritable

自定义bean对象实现序列化接口(Writable)

自定义bean对象要想序列化传输,必须实现序列化接口。具体操作步骤如下

  1. 必须实现Writable接口

  2. 反序列化时,需要反射调用空参构造函数,所以必须有空参构造

    1. public FlowBean() {
    2. super();
    3. }
  3. 重写序列化方法

    1. @Override
    2. public void write(DataOutput out) throws IOException {
    3. out.writeLong(upFlow);
    4. out.writeLong(downFlow);
    5. out.writeLong(sumFlow);
    6. }
  4. 重写反序列化方法

    1. @Override
    2. public void readFields(DataInput in) throws IOException {
    3. upFlow = in.readLong();
    4. downFlow = in.readLong();
    5. sumFlow = in.readLong();
    6. }
  5. 注意反序列化的顺序和序列化的顺序完全一致

  6. 要想把结果显示在文件中,需要重写toString(),可用”\t”分开,方便后续用。

    1. @Override
    2. public String toString() {
    3. return upFlow + "\t" + downFlow + "\t" + sumFlow;
    4. }
  7. 如果需要将自定义的bean放在key中传输,则还需要实现Comparable接口,因为MapReduce框中的Shuffle过程要求对key必须能排序。

    1. @Override
    2. public int compareTo(FlowBean o) {
    3. // 倒序排列,从大到小
    4. return this.sumFlow > o.getSumFlow() ? -1 : 1;
    5. }

序列化案例实操

  1. 需求

    统计每一个手机号耗费的总上行流量、下行流量、总流量

    1. 输入数据格式

      id 手机号码 网络ip 上行流量 下行流量 网络状态码
      7 13560436666 120.196.100.99 1116 954 200
    2. 输入数据

      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. 需求分析

  3. 编写MapReduce程序

    1. 编写流量统计的Bean对象

      1. package com.atguigu.mapreduce.flowsum;
      2. import java.io.DataInput;
      3. import java.io.DataOutput;
      4. import java.io.IOException;
      5. import org.apache.hadoop.io.Writable;
      6. // 1 实现writable接口
      7. public class FlowBean implements Writable{
      8. private long upFlow ;
      9. private long downFlow;
      10. private long sumFlow;
      11. //2 反序列化时,需要反射调用空参构造函数,所以必须有
      12. public FlowBean() {
      13. super();
      14. }
      15. public FlowBean(long upFlow, long downFlow) {
      16. super();
      17. this.upFlow = upFlow;
      18. this.downFlow = downFlow;
      19. this.sumFlow = upFlow + downFlow;
      20. }
      21. //3 写序列化方法
      22. @Override
      23. public void write(DataOutput out) throws IOException {
      24. out.writeLong(upFlow);
      25. out.writeLong(downFlow);
      26. out.writeLong(sumFlow);
      27. }
      28. //4 反序列化方法
      29. //5 反序列化方法读顺序必须和写序列化方法的写顺序必须一致
      30. @Override
      31. public void readFields(DataInput in) throws IOException {
      32. this.upFlow = in.readLong();
      33. this.downFlow = in.readLong();
      34. this.sumFlow = in.readLong();
      35. }
      36. // 6 编写toString方法,方便后续打印到文本
      37. @Override
      38. public String toString() {
      39. return upFlow + "\t" + downFlow + "\t" + sumFlow;
      40. }
      41. public long getUpFlow() {
      42. return upFlow;
      43. }
      44. public void setUpFlow(long upFlow) {
      45. this.upFlow = upFlow;
      46. }
      47. public long getDownFlow() {
      48. return downFlow;
      49. }
      50. public void setDownFlow(long downFlow) {
      51. this.downFlow = downFlow;
      52. }
      53. public long getSumFlow() {
      54. return sumFlow;
      55. }
      56. public void setSumFlow(long sumFlow) {
      57. this.sumFlow = sumFlow;
      58. }
      59. }
    2. 编写Mapper类

      1. package com.atguigu.mapreduce.flowsum;
      2. import java.io.IOException;
      3. import org.apache.hadoop.io.LongWritable;
      4. import org.apache.hadoop.io.Text;
      5. import org.apache.hadoop.mapreduce.Mapper;
      6. public class FlowCountMapper extends Mapper<LongWritable, Text, Text, FlowBean>{
      7. FlowBean v = new FlowBean();
      8. Text k = new Text();
      9. @Override
      10. protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
      11. // 1 获取一行
      12. String line = value.toString();
      13. // 2 切割字段
      14. String[] fields = line.split("\t");
      15. // 3 封装对象
      16. // 取出手机号码
      17. String phoneNum = fields[1];
      18. // 取出上行流量和下行流量
      19. long upFlow = Long.parseLong(fields[fields.length - 3]);
      20. long downFlow = Long.parseLong(fields[fields.length - 2]);
      21. k.set(phoneNum);
      22. v.set(downFlow, upFlow);
      23. // 4 写出
      24. context.write(k, v);
      25. }
      26. }
    3. 编写Reducer类

      1. package com.atguigu.mapreduce.flowsum;
      2. import java.io.IOException;
      3. import org.apache.hadoop.io.Text;
      4. import org.apache.hadoop.mapreduce.Reducer;
      5. public class FlowCountReducer extends Reducer<Text, FlowBean, Text, FlowBean>{
      6. private FlowBean out_value=new FlowBean();
      7. @Override
      8. protected void reduce(Text key, Iterable<FlowBean> values, Context context)
      9. throws IOException, InterruptedException {
      10. long sumUpFlow=0;
      11. long sumDownFlow=0;
      12. // 1 遍历所用bean,将其中的上行流量,下行流量分别累加
      13. for (FlowBean flowBean : values) {
      14. sumUpFlow+=flowBean.getUpFlow();
      15. sumDownFlow+=flowBean.getDownFlow();
      16. }
      17. // 2 封装对象
      18. out_value.setUpFlow(sumUpFlow);
      19. out_value.setDownFlow(sumDownFlow);
      20. out_value.setSumFlow(sumDownFlow+sumUpFlow);
      21. // 3 写出
      22. context.write(key, out_value);
      23. }
      24. }
    4. 编写Driver驱动类

      1. package com.atguigu.mapreduce.flowsum;
      2. import java.io.IOException;
      3. import org.apache.hadoop.conf.Configuration;
      4. import org.apache.hadoop.fs.Path;
      5. import org.apache.hadoop.io.Text;
      6. import org.apache.hadoop.mapreduce.Job;
      7. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
      8. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
      9. public class FlowsumDriver {
      10. public static void main(String[] args) throws IllegalArgumentException, IOException, ClassNotFoundException, InterruptedException {
      11. // 输入输出路径需要根据自己电脑上实际的输入输出路径设置
      12. args = new String[] {"F:/BaiduNetdiskDownload/mrinput/flowbean", "F:/BaiduNetdiskDownload/flowbean"};
      13. //保证输出目录不存在
      14. FileSystem fs=FileSystem.get(conf);
      15. if (fs.exists(outputPath)) {
      16. fs.delete(outputPath, true);
      17. }
      18. // 1 获取配置信息,或者job对象实例
      19. Configuration configuration = new Configuration();
      20. Job job = Job.getInstance(configuration);
      21. // 6 指定本程序的jar包所在的本地路径
      22. job.setJarByClass(FlowsumDriver.class);
      23. // 2 指定本业务job要使用的mapper/Reducer业务类
      24. job.setMapperClass(FlowCountMapper.class);
      25. job.setReducerClass(FlowCountReducer.class);
      26. // 3 指定mapper输出数据的kv类型
      27. job.setMapOutputKeyClass(Text.class);
      28. job.setMapOutputValueClass(FlowBean.class);
      29. // 4 指定最终输出的数据的kv类型
      30. job.setOutputKeyClass(Text.class);
      31. job.setOutputValueClass(FlowBean.class);
      32. // 5 指定job的输入原始文件所在目录
      33. FileInputFormat.setInputPaths(job, new Path(args[0]));
      34. FileOutputFormat.setOutputPath(job, new Path(args[1]));
      35. // 7 将job中配置的相关参数,以及job所用的java类所在的jar包, 提交给yarn去运行
      36. boolean result = job.waitForCompletion(true);
      37. System.exit(result ? 0 : 1);
      38. }
      39. }

由于篇幅过长,[MapReduce框架原理]等以后的内容,请看下回分解!

java大数据最全课程学习笔记(5)--MapReduce精通(一)的更多相关文章

  1. java大数据最全课程学习笔记(6)--MapReduce精通(二)--MapReduce框架原理

    目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages 目录 MapReduce精通(二) MapReduce框架原理 MapReduce工作流程 InputFormat数据 ...

  2. java大数据最全课程学习笔记(1)--Hadoop简介和安装及伪分布式

    Hadoop简介和安装及伪分布式 大数据概念 大数据概论 大数据(Big Data): 指无法在一定时间范围内用常规软件工具进行捕捉,管理和处理的数据集合,是需要新处理模式才能具有更强的决策力,洞察发 ...

  3. java大数据最全课程学习笔记(2)--Hadoop完全分布式运行模式

    目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages 目录 Hadoop完全分布式运行模式 步骤分析: 编写集群分发脚本xsync 集群配置 集群部署规划 配置集群 集群单 ...

  4. java大数据最全课程学习笔记(3)--HDFS 简介及操作

    目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages 目录 HDFS 简介及操作 HDFS概述 HDFS产出背景及定义 HDFS优缺点 HDFS组成架构 HDFS文件块大小 ...

  5. 《Hadoop》大数据技术开发实战学习笔记(二)

    搭建Hadoop 2.x分布式集群 1.Hadoop集群角色分配 2.上传Hadoop并解压 在centos01中,将安装文件上传到/opt/softwares/目录,然后解压安装文件到/opt/mo ...

  6. 42步进阶学习—让你成为优秀的Java大数据科学家!

    作者 灯塔大数据 本文转自公众号灯塔大数据(DTbigdata),转载需授权 如果你对各种数据类的科学课题感兴趣,你就来对地方了.本文将给大家介绍让你成为优秀数据科学家的42个步骤.深入掌握数据准备, ...

  7. Java大数据应用领域及就业方向

    最难毕业季,2017高校毕业生达到795万,许多学生面临着毕业即失业的尴尬.面对着与日俱增的竞争形势和就业压力,很多毕业生选择去知了堂学习社区镀金,以提高自己的就业竞争力,其中Java大数据是学生选择 ...

  8. 为什么Java大数据是最火爆的编程语言?

    未来10年将是大数据,人工智能爆发的时代,到时将会有大量的企业需要借助大数据,而Java最大的优势就是它在大数据领域的地位,目前很多的大数据架构都是通过Java来完成的. 在这个Android.iOS ...

  9. Java大数据人才应用领域广,就业薪酬高

    互联网创造了大数据应用的规模化环境,大数据应用成功的案例大都是在互联网上发生的, 互联网业务提供了数据,互联网企业开发了处理软件,互联网企业的创新带来了大数据应用 的活跃,没有互联网便没有今天的大数据 ...

随机推荐

  1. 【vue】axios二次封装,更好的管理api接口和使用

    在现在的前端开发中,前后端分离开发比较主流,所以在封装方法和模块化上也是非常需要掌握的一门技巧.而axios的封装也是非常的多,下面的封装其实跟百度上搜出来的axios封装或者axios二次封装区别不 ...

  2. python多线程+生产者和消费者模型+queue使用

    多线程简介 多线程:在一个进程内部,要同时干很多事情,就需要同时执行多个子任务,我们把进程内的这些子任务叫线程. 线程的内存空间是共享的,每个线程都共享同一个进程的资源 模块: 1._thread模块 ...

  3. python 之 数据类型初接触

    python 之 数据类型初接触 标准数据类型 Python3 中有六个标准的数据类型: Number(数字) String(字符串) List(列表) Tuple(元组) Set(集合) Dicti ...

  4. python变量拷贝

    写python代码时候,如:A = 0,B = A,B = 1, 有时候会发现A变成了1,那么怎么办呢? 以下是伪代码: import copy ... X_ = copy.copy(X) ... 这 ...

  5. 部署rabbitMQ镜像集群实战测试

    部署rabbitMQ镜像集群 版本信息 rabbit MQ: 3.8.5 Erlang: 官方建议最低21.3 推荐22.x 这里用的是23 环境准备 主机规划 主机 节点 172.16.14.3 磁 ...

  6. Java 添加、提取PDF中的图片

    Spire.Cloud.SDK for Java提供了PdfImagesApi接口可用于添加图片到PDF文档addImage().提取PDF中的图片extractImages(),具体操作步骤和Jav ...

  7. 通用Mapper与分页插件的集成

    SpringBoot 是为了简化 Spring 应用的创建.运行.调试.部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖 ...

  8. (私人收藏)[开发必备]最全JQuery离线快速查找手册(可查询可学习,带实例)

    [开发必备]最全JQuery离线快速查找手册(可查询可学习,带实例) https://pan.baidu.com/s/16bUd4iA3p0c5RHbzaC60IQe4zh

  9. less的使用几个技巧

    1.层级关系 让这个box范围内的全部包进来,这样的话就完美的进行调节,再也不用到处找第几行第几个,我刚才在哪个位置给覆盖了.一看便知! .box{ width: 100%; height: 300p ...

  10. 20 个 CSS高级样式技巧汇总

    使用技巧会让人变的越来越懒,没错,我就是想让你变懒.下面是我收集的CSS高级技巧,希望你懒出境界. 1. 黑白图像 这段代码会让你的彩色照片显示为黑白照片,是不是很酷? img.desaturate ...