使用Partitioner实现输出到多个文件
1、需求
按学生的年龄段,将数据输出到不同的文件。这里我们分为三个年龄段:小于等于20岁、大于20岁小于等于50岁和大于50岁
2、实现
1、编写Partitioner,代码如下
public static class StudentPartitioner extends Partitioner<IntWritable, Text> { @Override public int getPartition(IntWritable key, Text value, int numReduceTasks) { // 学生年龄 int ageInt = key.get(); // 默认指定分区 0 if (numReduceTasks == 0) return 0; if (ageInt <= 20) { // 年龄小于等于20,指定分区0 return 0; }else if (ageInt <= 50) { // 年龄大于20,小于等于50,指定分区1 return 1; }else{ // 剩余年龄,指定分区2 return 2; } } }
2、编写mapper
public static class StudentMapper extends Mapper<LongWritable, Text, IntWritable, Text>{ @Override protected void map(LongWritable key, Text value,Context context) throws IOException, InterruptedException { String[] studentArr = value.toString().split("\t"); if(StringUtils.isNotBlank(studentArr[1])){ /* * 姓名 年龄(中间以tab分割) * 张明明 45 */ // 年龄 IntWritable pKey = new IntWritable(Integer.parseInt(studentArr[1].trim())); // 以年龄作为key输出 context.write(pKey, value); } } }
3、编写reducer
public static class StudentReducer extends Reducer<IntWritable, Text, NullWritable, Text> { @Override protected void reduce(IntWritable key, Iterable<Text> values,Context context) throws IOException, InterruptedException { for(Text value : values){ context.write(NullWritable.get(), value); } } }
4、一些运行代码
@Override public int run(String[] arg0) throws Exception { // 读取配置文件 Configuration conf = new Configuration(); Path mypath = new Path(arg0[1]); FileSystem hdfs = mypath.getFileSystem(conf); if (hdfs.isDirectory(mypath)) { hdfs.delete(mypath, true); } // 新建一个任务 Job job = new Job(conf, "PartitionerDemo"); // 设置主类 job.setJarByClass(StudentPartitioner.class); // 输入路径 FileInputFormat.addInputPath(job, new Path(arg0[0])); // 输出路径 FileOutputFormat.setOutputPath(job, new Path(arg0[1])); // Mapper job.setMapperClass(StudentMapper.class); // Reducer job.setReducerClass(StudentReducer.class); // mapper输出格式 job.setMapOutputKeyClass(IntWritable.class); job.setMapOutputValueClass(Text.class); // reducer输出格式 job.setOutputKeyClass(NullWritable.class); job.setOutputValueClass(Text.class); //设置Partitioner类 job.setPartitionerClass(StudentPartitioner.class); // reduce个数设置为3 job.setNumReduceTasks(3); //提交任务 return job.waitForCompletion(true)?0:1; }
public static void main(String[] args0) throws Exception { // 数据输入路径和输出路径 // String[] args0 = { // "hdfs://ljc:9000/buaa/student/student.txt", // "hdfs://ljc:9000/buaa/student/out/" // }; int ec = ToolRunner.run(new Configuration(), new StudentAgePartitionerDemo(), args0); System.exit(ec); }
3、总结
Partitioner适用于事先知道分区数的情况下,比如像上面这个需求
缺点:
1、在作业运行之前需要知道分区数,也就是年龄段的个数,如果分区数未知,就无法操作。
2、一般来说,让应用程序来严格限定分区数并不好,因为可能导致分区数少或分区不均