使用Hadoop计数器 - 多个作业

3

我正在使用Hadoop制作一个mapreduce项目。目前我有3个顺序作业。

我想要使用Hadoop计数器,但问题是我想在第一个作业中进行实际计数,然后在第三个作业的reducer中访问计数器值。

我该如何实现这个功能?我应该在哪里定义enum?我需要通过第二个作业传递它吗?如果能够看到一些代码示例,将会很有帮助,因为我还没有找到任何相关资料。

注意:我正在使用Hadoop 2.7.2

编辑:我已经尝试了这里解释的方法,但并没有成功。我的情况不同,因为我想从另一个作业中访问计数器。(不是从mapper到reducer)。

我尝试做的事情:

public static void startFirstJob(String inputPath, String outputPath) throws IOException, ClassNotFoundException, InterruptedException {
    Configuration conf = new Configuration();
    Job job = Job.getInstance(conf, "wordCount");
    job.setJarByClass(WordCount.class);
    job.setMapperClass(WordCountMapper.class);
    job.setCombinerClass(WordCountReducer.class);
    job.setReducerClass(WordCountReducer.class);
    job.setOutputKeyClass(Text.class);
    job.setOutputValueClass(LongWritable.class);
    job.setInputFormatClass(SequenceFileInputFormat.class);
    job.setOutputFormatClass(SequenceFileOutputFormat.class);
    FileInputFormat.addInputPath(job, new Path(inputPath));
    FileOutputFormat.setOutputPath(job, new Path(outputPath));
    job.waitForCompletion(true);
}

在另一个类中定义了计数器枚举:

public class CountersClass {
    public static enum N_COUNTERS {
        SOMECOUNT
    }
}

尝试读取计数器:

Cluster cluster = new Cluster(context.getConfiguration());
Job job = cluster.getJob(JobID.forName("wordCount"));
Counters counters = job.getCounters();
CountersClass.N_COUNTERS mycounter = CountersClass.N_COUNTERS.valueOf("SOMECOUNT");
Counter c1 = counters.findCounter(mycounter);
long N_Count = c1.getValue();

我认为在reduce作业中使用计数器并不是一个好主意。请参考http://stackoverflow.com/questions/8009802/is-there-a-way-to-access-number-of-successful-map-tasks-from-a-reduce-task-in-an/。 - tworec
是的,我已经看到了并尝试过这种方法。但在那种情况下,他想要在reducer(同一作业中)内获取计数器。这与我的情况不同。 - A. Sarid
2个回答

5

经典的解决方案是将任务计数器的值放入需要访问它的后续作业的配置中:

因此,请确保在计数作业的mapper/reducer中正确地递增它:

context.getCounter(CountersClass.N_COUNTERS.SOMECOUNT).increment(1);

然后在计算作业完成之后:

job.waitForCompletion(true);

Counter someCount = job.getCounters().findCounter(CountersClass.N_COUNTERS.SOMECOUNT);

//put counter value into conf object of the job where you need to access it
//you can choose any name for the conf key really (i just used counter enum name here)
job2.getConfiguration().setLong(CountersClass.N_COUNTERS.SOMECOUNT.name(), someCount.getValue());

下一步是在另一个作业的mapper/reducer中访问它。只需覆盖setup()方法即可。 例如:
private long someCount;

@Override
protected void setup(Context context) throws IOException,
    InterruptedException {
  super.setup(context);
  this.someCount  = context.getConfiguration().getLong(CountersClass.N_COUNTERS.SOMECOUNT.name(), 0));
}

谢谢!如果我在这个“枚举”中有多个计数器怎么办?我能否只用“setEnum”和“getEnum”替换“setLong”和“getLong”?还是我需要按照您所说的为所有计数器做同样的操作? - A. Sarid
1
每个枚举项应对应一个单独的配置键。您仍然可以使用 setLong getLong 按其各自的键访问它们。 - yurgis
我知道这是一个老问题。但是假设作业在一些延迟后开始,当在集群上运行时,延迟的作业会不会覆盖早期启动的作业所设置的计数器? - user238607
以上答案假设从同一个 JVM 实例的驱动程序执行 2 个作业。如果你需要访问之前作业的计数器,最好将其结果存储在某个地方以便稍后访问。 - yurgis

2
在你的第一份工作结束时获取计数器,将其值写入文件并在后续工作中读取。如果要从reducer中读取,请将其写入HDFS;如果要在应用程序代码中读取和初始化,请将其写入本地文件。
以下是示例代码: Counters counters = job.getCounters(); Counter c1 = counters.findCounter(COUNTER_NAME); System.out.println(c1.getDisplayName()+":"+c1.getValue()); 读写文件是基础教程的一部分。

可能是一个选项。您能否添加所需的代码部分?谢谢。 - A. Sarid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接