MapReduce自定义InputFormat和OutputFormat案例


自定义InputFormat 合并小文件

  • 需求

    无论hdfs还是mapreduce,存放小文件会占用元数据信息,白白浪费内存,实践中,又难免面临处理大量小文件的场景

  • 优化小文件的三种方式

    1.在数据采集的时候,就将小文件或小批数据合成大文件再上传HDFS

    2.在业务处理之前,在HDFS上使用mapreduce程序对小文件进行合并

    3.在mapreduce处理时,可采用combineInputFormat提高效率

  • 用代码实现第二种方式

自定义InputFormat

  1. package cn.itcast.demo3;
  2. import jdk.nashorn.internal.ir.Splittable;
  3. import org.apache.hadoop.fs.Path;
  4. import org.apache.hadoop.io.BytesWritable;
  5. import org.apache.hadoop.io.NullWritable;
  6. import org.apache.hadoop.mapreduce.*;
  7. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
  8. import java.io.IOException;
  9. import java.util.List;
  10. public class MyInputFormat extends FileInputFormat<NullWritable, BytesWritable> {
  11. @Override
  12. public RecordReader<NullWritable, BytesWritable> createRecordReader(InputSplit split, TaskAttemptContext context) throws IOException, InterruptedException {
  13. MyRecordReader myRecordReader = new MyRecordReader();
  14. myRecordReader.initialize(split, context);
  15. return myRecordReader;
  16. }
  17. /**
  18. * 表示我们的文件是否可切分
  19. * 返回false表示我们的文件不可切分,读取文件时会一次性将文件内容全部读取出来
  20. *
  21. * @param context
  22. * @param filename
  23. * @return
  24. */
  25. @Override
  26. protected boolean isSplitable(JobContext context, Path filename) {
  27. return false;
  28. }
  29. }

自定义RecordReader

  1. package cn.itcast.demo3;
  2. import org.apache.hadoop.conf.Configuration;
  3. import org.apache.hadoop.fs.FSDataInputStream;
  4. import org.apache.hadoop.fs.FileSystem;
  5. import org.apache.hadoop.fs.Path;
  6. import org.apache.hadoop.io.BytesWritable;
  7. import org.apache.hadoop.io.IOUtils;
  8. import org.apache.hadoop.io.NullWritable;
  9. import org.apache.hadoop.mapreduce.InputSplit;
  10. import org.apache.hadoop.mapreduce.RecordReader;
  11. import org.apache.hadoop.mapreduce.TaskAttemptContext;
  12. import org.apache.hadoop.mapreduce.lib.input.FileSplit;
  13. import java.io.IOException;
  14. import java.io.InputStream;
  15. public class MyRecordReader extends RecordReader<NullWritable, BytesWritable> {
  16. //定义文件切片
  17. private FileSplit fileSplit;
  18. //定义文件configuration
  19. private Configuration configuration;
  20. //定义v2
  21. private BytesWritable bytesWritable = new BytesWritable();
  22. //定义下面nextKeyValue返回值为false
  23. private boolean nextKeyValue = false;
  24. /**
  25. * 初始化方法
  26. * 这里可以拿到文件切片,也就意味着可以拿到文件,将文件转换为字节数组
  27. *
  28. * @param split
  29. * @param context
  30. * @throws IOException
  31. * @throws InterruptedException
  32. */
  33. @Override
  34. public void initialize(InputSplit split, TaskAttemptContext context) throws IOException, InterruptedException {
  35. //获取文件切片
  36. this.fileSplit = (FileSplit) split;
  37. //获取文件Configuration
  38. this.configuration = context.getConfiguration();
  39. }
  40. /**
  41. * 返回true,表示文件读取完成,不会再往下继续读取文件
  42. * 返回false,表示会继续往下读取文件
  43. *
  44. * @return
  45. * @throws IOException
  46. * @throws InterruptedException
  47. */
  48. @Override
  49. public boolean nextKeyValue() throws IOException, InterruptedException {
  50. if (!nextKeyValue) {
  51. //根据文件的切片,将文件的内容全部读取出来,封装到BytesWritable中
  52. byte[] fileContent = new byte[(int) fileSplit.getLength()];
  53. //获取文件切片路径
  54. Path path = fileSplit.getPath();
  55. //获取文件系统
  56. FileSystem fileSystem = path.getFileSystem(configuration);
  57. //打开文件输入流
  58. FSDataInputStream inputStream = fileSystem.open(path);
  59. //将输入流转到字节数组中
  60. IOUtils.readFully(inputStream, fileContent, 0, (int) fileSplit.getLength());
  61. bytesWritable.set(fileContent, 0, fileContent.length);
  62. //将读取文件的标识设置为true,表示文件已经读取完成,不需要继续读取
  63. nextKeyValue = true;
  64. IOUtils.closeStream(inputStream);
  65. return nextKeyValue;
  66. }
  67. return false;
  68. }
  69. /**
  70. * 用来返回k1的值
  71. *
  72. * @return
  73. * @throws IOException
  74. * @throws InterruptedException
  75. */
  76. @Override
  77. public NullWritable getCurrentKey() throws IOException, InterruptedException {
  78. return NullWritable.get();
  79. }
  80. /**
  81. * 用来返回v1的值
  82. *
  83. * @return
  84. * @throws IOException
  85. * @throws InterruptedException
  86. */
  87. @Override
  88. public BytesWritable getCurrentValue() throws IOException, InterruptedException {
  89. return bytesWritable;
  90. }
  91. /**
  92. * 不太需要注意,就是用来读取运行进度的
  93. *
  94. * @return
  95. * @throws IOException
  96. * @throws InterruptedException
  97. */
  98. @Override
  99. public float getProgress() throws IOException, InterruptedException {
  100. return nextKeyValue ? 1.0F : 0.0F;
  101. }
  102. /**
  103. * 用来读取完后释放资源的,了解即可
  104. *
  105. * @throws IOException
  106. */
  107. @Override
  108. public void close() throws IOException {
  109. }
  110. }

定义一个Mapper类

  1. package cn.itcast.demo3;
  2. import org.apache.hadoop.io.BytesWritable;
  3. import org.apache.hadoop.io.NullWritable;
  4. import org.apache.hadoop.io.Text;
  5. import org.apache.hadoop.mapreduce.InputSplit;
  6. import org.apache.hadoop.mapreduce.Mapper;
  7. import org.apache.hadoop.mapreduce.lib.input.FileSplit;
  8. import java.io.IOException;
  9. public class MyMapperInput extends Mapper<NullWritable, BytesWritable, Text, BytesWritable> {
  10. @Override
  11. protected void map(NullWritable key, BytesWritable value, Context context) throws IOException, InterruptedException {
  12. //获取文件切片
  13. FileSplit inputSplit = (FileSplit) context.getInputSplit();
  14. //获取文件名称
  15. String name = inputSplit.getPath().getName();
  16. //输出k2,v2
  17. context.write(new Text(name), value);
  18. }
  19. }

程序main函数入口

  1. package cn.itcast.demo3;
  2. import org.apache.hadoop.conf.Configuration;
  3. import org.apache.hadoop.conf.Configured;
  4. import org.apache.hadoop.fs.Path;
  5. import org.apache.hadoop.io.BytesWritable;
  6. import org.apache.hadoop.io.Text;
  7. import org.apache.hadoop.mapreduce.Job;
  8. import org.apache.hadoop.util.Tool;
  9. import org.apache.hadoop.util.ToolRunner;
  10. public class OwnInputFormatMain extends Configured implements Tool {
  11. @Override
  12. public int run(String[] args) throws Exception {
  13. //创建job对象
  14. Job job = Job.getInstance(super.getConf(), "ownInputFormat");
  15. //输入数据,设置输入路径,注意这里是自动以的InputFormat
  16. job.setInputFormatClass(MyInputFormat.class);
  17. MyInputFormat.addInputPath(job, new Path("file:////Volumes/赵壮备份/大数据离线课程资料/5.大数据离线第五天/自定义inputformat_小文件合并/input"));
  18. //自定义map逻辑
  19. job.setMapperClass(MyMapperInput.class);
  20. //设置k2,v2输出类型
  21. job.setMapOutputKeyClass(Text.class);
  22. job.setMapOutputValueClass(BytesWritable.class);
  23. //虽然没有reducer,但是不设置reduce输出类型,默认的是<LongWritable,Text>
  24. job.setOutputKeyClass(Text.class);
  25. job.setOutputValueClass(BytesWritable.class);
  26. //输出数据,设置输出路径
  27. job.setOutputFormatClass(SequenceFileOutputFormat.class);
  28. SequenceFileOutputFormat.setOutputPath(job,new Path("file:////Volumes/赵壮备份/大数据离线课程资料/5.大数据离线第五天/自定义inputformat_小文件合并/sequence_output"));
  29. //提交任务到集群
  30. boolean b = job.waitForCompletion(true);
  31. return b ? 0 : 1;
  32. }
  33. public static void main(String[] args) throws Exception {
  34. int run = ToolRunner.run(new Configuration(), new OwnInputFormatMain(), args);
  35. System.exit(run);
  36. }
  37. }

自定义OutputFormat 将一个文件中的数据分发到不同文件

  • 需求

    将订单的好评与差评区分开来,并将最终的数据发送到不同的文件夹下面去,其中数据第九个字段表示好评,中评,差评。0:好评,1:中评,2:差评

  • 代码实现

自定义OutputFormat

  1. package cn.itcast.demo4;
  2. import org.apache.hadoop.conf.Configuration;
  3. import org.apache.hadoop.fs.FSDataOutputStream;
  4. import org.apache.hadoop.fs.FileSystem;
  5. import org.apache.hadoop.fs.Path;
  6. import org.apache.hadoop.io.NullWritable;
  7. import org.apache.hadoop.io.Text;
  8. import org.apache.hadoop.mapreduce.RecordWriter;
  9. import org.apache.hadoop.mapreduce.TaskAttemptContext;
  10. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
  11. import java.io.IOException;
  12. public class MyOutputFormat extends FileOutputFormat<Text, NullWritable> {
  13. @Override
  14. public RecordWriter<Text, NullWritable> getRecordWriter(TaskAttemptContext context) throws IOException, InterruptedException {
  15. //从这个方法里就可以获取一个configuration
  16. Configuration configuration = context.getConfiguration();
  17. //获取文件系统
  18. FileSystem fileSystem = FileSystem.get(configuration);
  19. //设置好评文件的输出路径
  20. Path goodComment = new Path("/Volumes/赵壮备份/大数据离线课程资料/5.大数据离线第五天/自定义outputformat/myGoodComment/1.txt");
  21. //设置差评文件的输出路径
  22. Path badComment = new Path("/Volumes/赵壮备份/大数据离线课程资料/5.大数据离线第五天/自定义outputformat/myBadComment/1.txt");
  23. //获取文件输出流
  24. FSDataOutputStream fsDataOutputStream = fileSystem.create(goodComment);
  25. FSDataOutputStream fsDataOutputStream1 = fileSystem.create(badComment);
  26. MyRecordWriter myRecordWriter = new MyRecordWriter(fsDataOutputStream, fsDataOutputStream1);
  27. return myRecordWriter;
  28. }
  29. }

自定义RecordWriter

  1. package cn.itcast.demo4;
  2. import org.apache.hadoop.fs.FSDataOutputStream;
  3. import org.apache.hadoop.io.IOUtils;
  4. import org.apache.hadoop.io.NullWritable;
  5. import org.apache.hadoop.io.Text;
  6. import org.apache.hadoop.mapreduce.RecordWriter;
  7. import org.apache.hadoop.mapreduce.TaskAttemptContext;
  8. import java.io.IOException;
  9. public class MyRecordWriter extends RecordWriter<Text, NullWritable> {
  10. //使用无参和带参构造调用goodStream和badStream
  11. private FSDataOutputStream goodStream;
  12. private FSDataOutputStream badStream;
  13. public MyRecordWriter() {
  14. }
  15. public MyRecordWriter(FSDataOutputStream goodStream, FSDataOutputStream badStream) {
  16. this.goodStream = goodStream;
  17. this.badStream = badStream;
  18. }
  19. /**
  20. * 这个write方法就是往外写出去数据
  21. *
  22. * @param key 可以根据这个key,来判断文件究竟往哪个目录下写
  23. * @param value
  24. * @throws IOException
  25. * @throws InterruptedException
  26. */
  27. @Override
  28. public void write(Text key, NullWritable value) throws IOException, InterruptedException {
  29. //分割导入的数据
  30. String[] split = key.toString().split("\t");
  31. //获取评论状态 0:好评 1:中评 2:差评;
  32. //判断评论状态,如果小于等于1则写到好评文件中,否则写到差评文件中
  33. if (Integer.parseInt(split[9]) <= 1) {
  34. goodStream.write(key.toString().getBytes());
  35. goodStream.write("\r\n".getBytes());
  36. } else {
  37. badStream.write(key.toString().getBytes());
  38. badStream.write("\r\n".getBytes());
  39. }
  40. }
  41. @Override
  42. public void close(TaskAttemptContext taskAttemptContext) throws IOException, InterruptedException {
  43. IOUtils.closeStream(goodStream);
  44. IOUtils.closeStream(badStream);
  45. }
  46. }

定义一个Mapper类

  1. package cn.itcast.demo4;
  2. import org.apache.hadoop.io.LongWritable;
  3. import org.apache.hadoop.io.NullWritable;
  4. import org.apache.hadoop.io.Text;
  5. import org.apache.hadoop.mapreduce.Mapper;
  6. import java.io.IOException;
  7. public class MyOutputMapper extends Mapper<LongWritable, Text, Text, NullWritable> {
  8. @Override
  9. protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
  10. context.write(value, NullWritable.get());
  11. }
  12. }

程序main函数入口

  1. package cn.itcast.demo4;
  2. import org.apache.hadoop.conf.Configuration;
  3. import org.apache.hadoop.conf.Configured;
  4. import org.apache.hadoop.fs.Path;
  5. import org.apache.hadoop.io.NullWritable;
  6. import org.apache.hadoop.io.Text;
  7. import org.apache.hadoop.mapreduce.Job;
  8. import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
  9. import org.apache.hadoop.util.Tool;
  10. import org.apache.hadoop.util.ToolRunner;
  11. public class MyOutputMain extends Configured implements Tool {
  12. @Override
  13. public int run(String[] args) throws Exception {
  14. //创建job对象
  15. Job job = Job.getInstance(super.getConf(), "OutputFormat");
  16. //输入数据,设置输入路径
  17. job.setInputFormatClass(TextInputFormat.class);
  18. TextInputFormat.setInputPaths(job, new Path("file:////Volumes/赵壮备份/大数据离线课程资料/5.大数据离线第五天/自定义outputformat/input/ordercomment.csv"));
  19. //自定义map逻辑
  20. job.setMapperClass(MyOutputMapper.class);
  21. //设置k2,v2输出类型
  22. job.setMapOutputKeyClass(Text.class);
  23. job.setMapOutputValueClass(NullWritable.class);
  24. //输出数据,设置输出路径,这里的输出路径不是真正的输出路径
  25. job.setOutputFormatClass(MyOutputFormat.class);
  26. MyOutputFormat.setOutputPath(job, new Path("file:////Volumes/赵壮备份/大数据离线课程资料/5.大数据离线第五天/自定义outputformat/output"));
  27. //提交任务至集群
  28. boolean b = job.waitForCompletion(true);
  29. return b ? 0 : 1;
  30. }
  31. public static void main(String[] args) throws Exception {
  32. int run = ToolRunner.run(new Configuration(), new MyOutputMain(), args);
  33. System.exit(run);
  34. }
  35. }

【Hadoop离线基础总结】MapReduce自定义InputFormat和OutputFormat案例的更多相关文章

  1. MapReduce自定义InputFormat和OutputFormat

    一.自定义InputFormat 需求:将多个小文件合并为SequenceFile(存储了多个小文件) 存储格式:文件路径+文件的内容 c:/a.txt I love Beijing c:/b.txt ...

  2. 自定义InputFormat和OutputFormat案例

    一.自定义InputFormat InputFormat是输入流,在前面的例子中使用的是文件输入输出流FileInputFormat和FileOutputFormat,而FileInputFormat ...

  3. 【Hadoop离线基础总结】Hue的简单介绍和安装部署

    目录 Hue的简单介绍 概述 核心功能 安装部署 下载Hue的压缩包并上传到linux解压 编译安装启动 启动Hue进程 hue与其他框架的集成 Hue与Hadoop集成 Hue与Hive集成 Hue ...

  4. 【Hadoop离线基础总结】oozie的安装部署与使用

    目录 简单介绍 概述 架构 安装部署 1.修改core-site.xml 2.上传oozie的安装包并解压 3.解压hadooplibs到与oozie平行的目录 4.创建libext目录,并拷贝依赖包 ...

  5. 【Hadoop离线基础总结】impala简单介绍及安装部署

    目录 impala的简单介绍 概述 优点 缺点 impala和Hive的关系 impala如何和CDH一起工作 impala的架构及查询计划 impala/hive/spark 对比 impala的安 ...

  6. 【Hadoop离线基础总结】Hive调优手段

    Hive调优手段 最常用的调优手段 Fetch抓取 MapJoin 分区裁剪 列裁剪 控制map个数以及reduce个数 JVM重用 数据压缩 Fetch的抓取 出现原因 Hive中对某些情况的查询不 ...

  7. 【Hadoop离线基础总结】流量日志分析网站整体架构模块开发

    目录 数据仓库设计 维度建模概述 维度建模的三种模式 本项目中数据仓库的设计 ETL开发 创建ODS层数据表 导入ODS层数据 生成ODS层明细宽表 统计分析开发 流量分析 受访分析 访客visit分 ...

  8. 【Hadoop离线基础总结】Sqoop常用命令及参数

    目录 常用命令 常用公用参数 公用参数:数据库连接 公用参数:import 公用参数:export 公用参数:hive 常用命令&参数 从关系表导入--import 导出到关系表--expor ...

  9. 【Hadoop离线基础总结】Sqoop数据迁移

    目录 Sqoop介绍 概述 版本 Sqoop安装及使用 Sqoop安装 Sqoop数据导入 导入关系表到Hive已有表中 导入关系表到Hive(自动创建Hive表) 将关系表子集导入到HDFS中 sq ...

随机推荐

  1. Python logging日志打印

    1.logging常用函数Logger.setLevel():设置日志级别Logger.addHandler()和Logger.removeHandler():添加和删除一个handlerLogger ...

  2. Git敏捷开发--stash命令

    save 执行git stash,默认以commit info保存当前的stash信息 当在某个commit下,执行多次stash时,无法友好地区分每个stash的改动.save 命令可以清晰地标识每 ...

  3. CTFHub web技能树 RCE

    一个简单的ping,还没有过滤,源码也给出来了 ls一下 127.0.0.1 & ls 有一个可疑的php文件,,,但是直接访问和 cat 都出不来... 试了几下反弹shell,没成功... ...

  4. Servlet 和 Servlet容器

    Servlet 很多同学可能跟我一样始终没有搞清楚到底什么是 Servlet,什么是 Servlet 容器.网上看了很多帖子,或许人家说的很清楚,但是自己的那个弯弯就是拐不过来. 想了很久说一下自己的 ...

  5. bm25算法和tfidf

  6. pytorch 中HWC转CHW

    import torch import numpy as np from torchvision.transforms import ToTensor t = torch.tensor(np.aran ...

  7. deepin下深度终端使用ssh-agent(xshell中的xagent功能)

    背景:从windows10换到deepin后,在连接公司的服务器遇到了问题:windows下用的是xshell,开启xagent后,可直接从公司的跳转板上连接生产服务器:在deepin的深度终端上,从 ...

  8. Querying for Event Information

    https://docs.microsoft.com/zh-cn/windows/desktop/EventLog/querying-for-event-source-messages #includ ...

  9. Inno setup: check for new updates

    Since you've decided to use a common version string pattern, you'll need a function which will parse ...

  10. C# 9 新特性:代码生成器、编译时反射

    前言 今天 .NET 官方博客宣布 C# 9 Source Generators 第一个预览版发布,这是一个用户已经喊了快 5 年特性,今天终于发布了. 简介 Source Generators 顾名 ...