• 向任务传递定制参数
  • 获取任务待定的信息
  • 生成多个输出
  • 与关系数据库交互
  • 让输出做全局排序
 
1、向任务传递作业定制的参数
 
     在编写Mapper和Reducer时,通常会想让一些地方可以配置。例如第5章的联结程序被固定地写为取第一个数据列作为联结键。如果用户可以在运行时指定某个列作为联结键,就会让程序更具普适性。hadoop自身使用一个配置对象来存储所有作业的配置属性。你也可以使用这个对象将参数传递到Mapper和Reducer。
 
     我们已经知道MapReduce的driver是如何用属性来配置JobConf对象的,这些属性包括输入格式、输出格式、Mapper类等。若要引入自己的属性,需要在这个配置对象中,给属性一个唯一的名称并设置它的值。这个配置对象会被传递给所有的TaskTracker,然后作业中的所有任务就能够看到配置对象中的属性。Mapper和Reducer也就可以读取该配置对象并获取它的属性值。
 
     Configuration类(JobConf的父类)有许多通用的setter方法。属性采用键/值对的形式,键必须是一个String,而值可以是常用类型的任意一个。常用setter方法的签名为:
     public void set(String name, String value);
     public void setBoolean(String name, Boolean value);
     public void setInt(String name, Int value);
     public void setLong(String name, Long value);
     public void setStrings(String name, String... values);
请注意在hadoop内部,所有的属性都存为字符串。在set(String, String)方法之外的所有其他方法都是它的便捷方法。
 
     Driver会首先设置配置对象中的属性,让它们在所有任务中可见。Mapper和Reducer可以访问configure()方法中的配置对象。任务初始化时会调用configure(),它已经被覆写为可以提取和存储你设置的属性。之后,map()和reduce()方法会访问这些属性的副本。示例,调用新的属性myjob.myproperty,用一个由用户指定的整数值:
     public int run(String[] args) throws Exception {
          Configuration conf = getConf();
          JobConf job = new JobConf(conf, MyJob.class);
          ...
          job.setInt(“myjob.myproperty”, Integer.parseInt(args[2]));
          JobClient.runJob(job);
          return 0;
     }
 
在MapClass中,configure()方法取出属性值,并将它存储在对象的范围中。Configuration类的getter方法需要指定默认的值,如果所请求的属性未在配置对象中设置,就会返回默认值。在这个例子中,我们取默认值为0:
     public static class MapClass extends MapReduceBase
          implements Mapper<Text, Text, Text, Text> {
          int myproperty;
          public void configure(JobConf job) {
               myproperty = job.getInt(“myjob.myproperty”,0);
          }
          ...
     }
 
如果你希望在Reducer中使用该属性,Reducer也必须检索这个属性:
     public static class Reduce extends MapReduceBase
          implements Reducer<Text, Text, Text, Text> {
          int myproperty;
          public void configure(JobConf job) {
               myproperty = job.getInt(“myjob.myproperty”,0);
          }
          ...
     }
 
Configuration类中getter方法的列表比setter方法更长,几乎所有的getter方法都需要将参数设置为默认值。唯一例外是get(String),如果没有设置特定的名称,它就会返回null:
     public String get(String name)
     public String get(String name, String defaultValue)
     public Boolean getBoolean(String name, Boolean defaultValue)
     public float getFloat(String name, Float defaultValue)
     public Int getInt(String name, Int defaultValue)
     public Long getLong(String name, Long defaultValue)
     public String[] getStrings(String name, String... defaultValue)
 
     既然我们的job类实现了Tool接口并使用了ToolRunner,我们还可以让用户直接使用通用的选项来配置定制化的属性,方法与用户设置hadoop的配置属性相同:
     hadoop jar MyJob.jar MyJob -D myjob.myproperty=1 input output
 
     我们可以将driver中总是需要用户通过参数来设定属性值的那行代码删掉。如果在大多数时间里默认值是可用的,这样做会让用户感觉更加方便。当你允许用户设定属性时,在driver中最好对用户的输入进行验证:
     public int run(String[] args) throws Exception {
          Configuration conf = getConf();
          JobConf job = new JobConf(conf, MyJob.class);
          ...
          Int myproperty = job.getInt(“myjob.myproperty”, 0);
          if (my property < 0) {
               System.err.println(“Invalid myjob.myproperty”+myproperty);
                    System.exit(0);
          }
          JobClient.runJob(job);
          return 0;
     }
 
2、探查任务特定信息
 
     除了获取自定义属性和全局配置之外,我们还可以使用配置对象上的getter方法获得当前任务和作业状态的一些信息:
     this.inputFile = job.get(“map.input.file”);    //获得当前map任务的文件路径
     this.inputTag = generateInputTag(this.inputFile);    //在data join软件包的DataJoinMapperBase中,configure()方法中用一个标签来表示数据源
 
在配置对象中可获得的任务特定状态信息:
 
属性
类型
描述
mapred.job.id String 作业ID
mapred.jar String 作业目录中jar的位置
job.local.dir String 作业的本地空间
mapred.tip.id String 任务ID
mapred.task.id String 任务重试ID
mapred.task.is.map Boolean 标志量,表示是否为一个map任务
mapred.task.partition Int 作业内部的任务ID
map.input.file String Mapper读取的文件路径
map.input.start Long 当前Mapper输入分片的文件偏移量
map.input.length Long 当前Mapper输入分片的字节数
mapred.work.output.dir String 任务的工作(即临时)输出目录
 
3、划分为多个输出文件
 
     在有些有些场景中,输出多组文件或把一个数据集分为多个数据集更为方便。MultipleOutputFormat提供了一个建党的方法,将相似的记录结组为不同的数据集。在写每条记录之前,这个OutputFormat类调用一个内部方法来确定要写入的文件名。更具体地说,你将扩展MultipleOutputFormat的某个特定子类,并实现generateFileNameForKeyValue()方法。你扩展的子类将决定输出的格式,例如MultipleTextOutputFormat将输出文本文件,而MultipleSequenceFileOutputFormat将输出序列文件。
 
     无论哪种情况,你会覆写下面的方法以返回每个输出键/值对的文件名:
     protected String generateFileNameForKeyValue(K key, V value, String name)
 

代码清单 根据国家将专利元数据分割到多个目录中
 
 import java.io.IOException;
import java.util.Iterator; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.SequenceFileInputFormat;
import org.apache.hadoop.mapred.SequenceFileOutputFormat;
import org.apache.hadoop.mapred.KeyValueTextInputFormat;
import org.apache.hadoop.mapred.TextInputFormat;
import org.apache.hadoop.mapred.TextOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.mapred.lib.MultipleTextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner; public class MultiFile extends Configured implements Tool { public static class MapClass extends MapReduceBase
implements Mapper<LongWritable, Text, NullWritable, Text> { public void map(LongWritable key, Text value,
OutputCollector<NullWritable, Text> output,
Reporter reporter) throws IOException { output.collect(NullWritable.get(), value);
}
} public static class PartitionByCountryMTOF
extends MultipleTextOutputFormat<NullWritable,Text>
{
protected String generateFileNameForKeyValue(NullWritable key,
Text value,
String inputfilename)
{
String[] arr = value.toString().split(",", -1);
String country = arr[4].substring(1,3);
return country+"/"+inputfilename;
}
} public int run(String[] args) throws Exception {
// Configuration processed by ToolRunner
Configuration conf = getConf(); // Create a JobConf using the processed conf
JobConf job = new JobConf(conf, MultiFile.class); // Process custom command-line options
Path in = new Path(args[0]);
Path out = new Path(args[1]);
FileInputFormat.setInputPaths(job, in);
FileOutputFormat.setOutputPath(job, out); // Specify various job-specific parameters
job.setJobName("MultiFile");
job.setMapperClass(MapClass.class); job.setInputFormat(TextInputFormat.class);
job.setOutputFormat(PartitionByCountryMTOF.class);
job.setOutputKeyClass(NullWritable.class);
job.setOutputValueClass(Text.class); job.setNumReduceTasks(0); // Submit the job, then poll for progress until the job is complete
JobClient.runJob(job); return 0;
} public static void main(String[] args) throws Exception {
// Let ToolRunner handle generic command-line options
int res = ToolRunner.run(new Configuration(), new MultiFile(), args); System.exit(res);
}
}
 

 
     MutipleOutputFormat很简单,可以按行拆分输入数据,但如果想按列拆分会该怎样做呢?我们可以在hadoop 0.19版本zhong引入的MutipleOutputs,以获得更强的能力。
     
     MutipleOutputs所采用的方法不同于MutipleOutputFormat。它不是要求给每条记录请求文件名,而是创建多个OutputCollector,每个OutputCollector可以有自己的OutputFormat和键/值对的类型。MapReduce程序将决定如何向每个OutputCollector输出数据。
 

代码清单 将输入数据的不同列提取为不同文件的程序
 
 import java.io.IOException;
import java.util.Iterator; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.SequenceFileInputFormat;
import org.apache.hadoop.mapred.SequenceFileOutputFormat;
import org.apache.hadoop.mapred.KeyValueTextInputFormat;
import org.apache.hadoop.mapred.TextInputFormat;
import org.apache.hadoop.mapred.TextOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.mapred.lib.MultipleTextOutputFormat;
import org.apache.hadoop.mapred.lib.MultipleOutputs;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner; public class MultiFile extends Configured implements Tool { public static class MapClass extends MapReduceBase
implements Mapper<LongWritable, Text, NullWritable, Text> { private MultipleOutputs mos;
private OutputCollector<NullWritable, Text> collector; public void configure(JobConf conf) {
mos = new MultipleOutputs(conf);
} public void map(LongWritable key, Text value,
OutputCollector<NullWritable, Text> output,
Reporter reporter) throws IOException { String[] arr = value.toString().split(",", -1);
String chrono = arr[0] + "," + arr[1] + "," + arr[2];
String geo = arr[0] + "," + arr[4] + "," + arr[5]; collector = mos.getCollector("chrono", reporter);
collector.collect(NullWritable.get(), new Text(chrono));
collector = mos.getCollector("geo", reporter);
collector.collect(NullWritable.get(), new Text(geo));
} public void close() throws IOException {
mos.close();
}
} public int run(String[] args) throws Exception {
// Configuration processed by ToolRunner
Configuration conf = getConf(); // Create a JobConf using the processed conf
JobConf job = new JobConf(conf, MultiFile.class); // Process custom command-line options
Path in = new Path(args[0]);
Path out = new Path(args[1]);
FileInputFormat.setInputPaths(job, in);
FileOutputFormat.setOutputPath(job, out); // Specify various job-specific parameters
job.setJobName("MultiFile");
job.setMapperClass(MapClass.class); job.setInputFormat(TextInputFormat.class);
// job.setOutputFormat(PartitionByCountryMTOF.class);
job.setOutputKeyClass(NullWritable.class);
job.setOutputValueClass(Text.class);
job.setNumReduceTasks(0); MultipleOutputs.addNamedOutput(job,
"chrono",
TextOutputFormat.class,
NullWritable.class,
Text.class);
MultipleOutputs.addNamedOutput(job,
"geo",
TextOutputFormat.class,
NullWritable.class,
Text.class); // Submit the job, then poll for progress until the job is complete
JobClient.runJob(job); return 0;
} public static void main(String[] args) throws Exception {
// Let ToolRunner handle generic command-line options
int res = ToolRunner.run(new Configuration(), new MultiFile(), args); System.exit(res);
}
}
 

 
4、以数据库作为输入输出
 
     虽然有可能建立一个MapReduce程序通过直接查询数据库来取得输入数据,而不是从HDFS中读取文件,但其性能不甚理想。更多时候,你需要将数据集从数据库复制到HDFS中。你可以很容易地通过标准的数据库工具dump,来取得一个flat文件,然后使用HDFS的shell文件put将它上传到HDFS中。但是有时更合理的做法是让MapReduce程序直接写入数据库。
 
     DBOutputFormat是用于访问数据库的关键类。你可以通过在DBConfiguration中的静态方法configureDB()做到这一点:
     public static void configureDB(Jobconf job, String driverClass, String dbUrl, String userName, String passwd)
 
     之后,你要指定将写入的表,以及那里有哪些字段。这是通过在DBOutputForamt中的静态setOutput()方法做到的:
     public static void setOutput(Jobconf job, String tableName, String… fieldNames)
 
     你的driver应该包含如下样式的几行代码:
     conf.setOutputFormat(DBOutputFormat.class);
     DBConfiguration.configureDB(job,
                                                     “com.mysql.jdbc.Driver”,
                                                     “jdbc.mysql://db.host.com/mydb”,
                                                     “username”,
                                                     “password" ) ;
     DBOutputFormat.setOutput(job, “Events”, “event_id”, “time");
 
使用DBOutputFormat将强制你输出的键实现DBWritable接口。只有这个键会被写入到数据库中。通常,键必须实现Writable接口。在Writable中write()方法用DataOutput,而DBWritable中的write()方法用PreparedStatement。类似的,用在Writable中的readFields()方法采用DataInput,而DBWritable中的readFields()采用ResultSet。除非你打算使用DBInputFormat直接从数据库中读取输入的数据,否则在DBWritable中的readFields()将永远不会被调用。
    
     public class EventsDBWritable implements Writable, DBWritable {
          private int id;
          private long timestamp;
 
          public void write(DataOutput out) throws IOException {
               out.writeInt(id);
               out.writeLong(timestamp);
          }
 
          public void readFields(DataInput in) throws IOException {
               id = in.readInt();
               timestamp = in.readLong();
          }
 
     public void write(PreparedStatement statement) throws IOException {
               statement.setInt(1, id);
               statement.setLong(2, timestamp);
          }
 
     public void readFields(ResultSet resultSet) throws IOException {
               id = resultSet.getInt(1);
               timestamp = resultSet.getLong(2);
          }
     }
 
5、保持输出的顺序
 
     请记住MapReduce框架并不能保证reducer输出的顺序,它只是已经排序好的输入以及reducer所执行的典型操作类型的一种副产品。对于某些应用,这种排序是没有必要的。
 
     Partitioner的任务是确定地为每个键分配一个reducer,相同键的所有记录都结成组并在reduce阶段被集中处理。Partitioner的一个重要设计需求是在reducer之间达到负载均衡。Partitioner默认使用散列函数来均匀、随机地将键分配给reducer。如果视线知道键是大致均匀分布的,我们就可以使用一个partitioner给每个reducer分配一个键的范围,仍然可以确保reducer的负载是相对均衡的。
 
TotalOrderPartitioner是一个可以保证在输入分区之间,而不仅仅是分区内部排序的partitioner。这种类利用一个排好序的分区键组读取一个序列文件,并进一步将不同区域的键分配到reducer上。 
 
 [转载请注明] http://www.cnblogs.com/zhengrunjian/ 

[Hadoop in Action] 第7章 细则手册的更多相关文章

  1. [Hadoop in Action] 第6章 编程实践

    Hadoop程序开发的独门绝技 在本地,伪分布和全分布模式下调试程序 程序输出的完整性检查和回归测试 日志和监控 性能调优   1.开发MapReduce程序   [本地模式]        本地模式 ...

  2. [Hadoop in Action] 第5章 高阶MapReduce

    链接多个MapReduce作业 执行多个数据集的联结 生成Bloom filter   1.链接MapReduce作业   [顺序链接MapReduce作业]   mapreduce-1 | mapr ...

  3. [Hadoop in Action] 第4章 编写MapReduce基础程序

    基于hadoop的专利数据处理示例 MapReduce程序框架 用于计数统计的MapReduce基础程序 支持用脚本语言编写MapReduce程序的hadoop流式API 用于提升性能的Combine ...

  4. [hadoop in Action] 第3章 Hadoop组件

    管理HDFS中的文件 分析MapReduce框架中的组件 读写输入输出数据   1.HDFS文件操作   [命令行方式]   Hadoop的文件命令采取的形式为: hadoop fs -cmd < ...

  5. [Hadoop in Action] 第2章 初识Hadoop

    Hadoop的结构组成 安装Hadoop及其3种工作模式:单机.伪分布和全分布 用于监控Hadoop安装的Web工具   1.Hadoop的构造模块   (1)NameNode(名字节点)       ...

  6. [Hadoop in Action] 第1章 Hadoop简介

    编写可扩展.分布式的数据密集型程序和基础知识 理解Hadoop和MapReduce 编写和运行一个基本的MapReduce程序   1.什么是Hadoop   Hadoop是一个开源的框架,可编写和运 ...

  7. Hadoop专业解决方案-第12章 为Hadoop应用构建企业级的安全解决方案

    一.前言: 非常感谢Hadoop专业解决方案群:313702010,兄弟们的大力支持,在此说一声辛苦了,春节期间,项目进度有所延迟,不过元宵节以后大家已经步入正轨, 目前第12章 为Hadoop应用构 ...

  8. Hadoop专业解决方案-第1章 大数据和Hadoop生态圈

    一.前言: 非常感谢Hadoop专业解决方案群:313702010,兄弟们的大力支持,在此说一声辛苦了,经过两周的努力,已经有啦初步的成果,目前第1章 大数据和Hadoop生态圈小组已经翻译完成,在此 ...

  9. Hadoop专业解决方案-第13章 Hadoop的发展趋势

    一.前言: 非常感谢Hadoop专业解决方案群:313702010,兄弟们的大力支持,在此说一声辛苦了,经过两周的努力,已经有啦初步的成果,目前第13章 Hadoop的发展趋势小组已经翻译完成,在此对 ...

随机推荐

  1. python3  threading初体验

    python3中thread模块已被废弃,不能在使用thread模块,为了兼容性,python3将thread命名为_thread.python3中我们可以使用threading进行代替. threa ...

  2. Sublime的使用

    1.一个可扩展性强的编辑工具 2.如何安装扩展 通过View->Show Console菜单打开命令行. 按图操作: 在控制台输入,然后回车: import urllib.request,os; ...

  3. OpenCASCADE AIS Manipulator

    OpenCASCADE AIS Manipulator eryar@163.com Abstract. OpenCASCADE7.1.0 introduces new built-in interac ...

  4. 谈谈一些有趣的CSS题目(十)-- 结构性伪类选择器

    开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...

  5. pdo的使用

    PHP 数据对象 (PDO) 扩展为PHP访问数据库定义了一个轻量级的一致接口. PDO 提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据. PDO随 ...

  6. 我这么玩Web Api(二):数据验证,全局数据验证与单元测试

    目录 一.模型状态 - ModelState 二.数据注解 - Data Annotations 三.自定义数据注解 四.全局数据验证 五.单元测试   一.模型状态 - ModelState 我理解 ...

  7. 代码的坏味道(16)——纯稚的数据类(Data Class)

    坏味道--纯稚的数据类(Data Class) 特征 纯稚的数据类(Data Class) 指的是只包含字段和访问它们的getter和setter函数的类.这些仅仅是供其他类使用的数据容器.这些类不包 ...

  8. 调用微信退款接口或发红包接口时出现System.Security.Cryptography.CryptographicException: 出现了内部错误 解决办法

    我总结了一下出现证书无法加载的原因有以下三个 1.证书密码不正确,微信证书密码就是商户号 解决办法:请检查证书密码是不是和商户号一致 2.IIS设置错误,未加载用户配置文件 解决办法:找到网站使用的应 ...

  9. Android开发学习—— Fragment

    #Fragment* 用途:在一个Activity里切换界面,切换界面时只切换Fragment里面的内容* 生命周期方法跟Activity一致,可以理解把其为就是一个Activity* 定义布局文件作 ...

  10. Microsoft Visual Studio 2017 for Mac Preview 下载+安装+案例Demo

    目录: 0. 前言 1. 在线安装器 2. 安装VS 3. HelloWorld 4. ASP.NET MVC 5. 软件下载 6. 结尾 0. 前言: 工作原因,上下班背着我的雷神,一个月瘦了10斤 ...