从reducer访问映射器的计数器
我需要从我的reducer中的mapper访问计数器。 这可能吗? 如果是这样怎么办?
举个例子:我的映射器是:
public class CounterMapper extends Mapper { static enum TestCounters { TEST } @Override protected void map(Text key, Text value, Context context) throws IOException, InterruptedException { context.getCounter(TestCounters.TEST).increment(1); context.write(key, value); } }
我的减速机是
public class CounterReducer extends Reducer { @Override protected void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException { Counter counter = context.getCounter(CounterMapper.TestCounters.TEST); long counterValue = counter.getValue(); context.write(key, new LongWritable(counterValue)); } }
counterValue总是0.我做错了什么或这是不可能的?
在Reducer的configure(JobConf)中,您可以使用JobConf对象来查找reducer自己的作业ID。 有了它,您的reducer可以创建自己的JobClient – 即与jobtracker的连接 – 并查询此作业的计数器(或任何相关的工作)。
// in the Reducer class... private long mapperCounter; @Override public void configure(JobConf conf) { JobClient client = new JobClient(conf); RunningJob parentJob = client.getJob(JobID.forName( conf.get("mapred.job.id") )); mapperCounter = parentJob.getCounters().getCounter(MAP_COUNTER_NAME); }
现在您可以在reduce()方法本身中使用mapperCounter。
你真的需要一个try-catch。 我正在使用旧的API,但它不应该很难适应新的API。
请注意,映射器的计数器应该在任何减速器开始之前完成,所以与Justin Thomas的评论相反,我相信你应该得到准确的值(只要减速器没有递增相同的计数器!)
在新API上实现了Jeff G的解决方案:
@Override public void setup(Context context) throws IOException, InterruptedException{ Configuration conf = context.getConfiguration(); Cluster cluster = new Cluster(conf); Job currentJob = cluster.getJob(context.getJobID()); mapperCounter = currentJob.getCounters().findCounter(COUNTER_NAME).getValue(); }
map / reduce的重点是并行化作业。 将会有许多独特的映射器/缩减器,因此除了map / reduce对的运行之外,该值无论如何都不正确。
他们有一个单词计数示例:
http://wiki.apache.org/hadoop/WordCount
您可以将context.write(word,one)更改为context.write(line,one)
全局计数器值永远不会广播回每个映射器或缩减器。 如果您希望映射器记录的#可用于reducer,则需要依赖某些外部机制来执行此操作。
我问了这个问题 ,但我没有解决我的问题。 但是,我想到了另一种解决方案。 在映射器中,对字数进行计数,并且可以在运行映射器末尾的清除函数中使用最小键(因此该值在头部)写入中间输出。 在reducer中,通过在head中添加值来计算单词数。 下面提供了示例代码及其输出的一部分。
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.Reducer; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.input.TextInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat; import java.io.IOException; import java.util.StringTokenizer; /** * Created by tolga on 1/26/16. */ public class WordCount { static enum TestCounters { TEST } public static class Map extends Mapper
文本文件:
Turgut Özal University is a private university located in Ankara, Turkey. It was established in 2008 by the Turgut Özal Thought and Action Foundation and is named after former Turkish president Turgut Özal.
中间产出
**! 33** 2008 1 Action 1 Ankara, 1 Foundation 1 It 1 Thought 1 Turgut 1 Turgut 1 Turgut 1
itzhaki答案的改进
不再支持findCounter(COUNTER_NAME)
– https://hadoop.apache.org/docs/r2.7.0/api/org/apache/hadoop/mapred/Counters.html
@Override public void setup(Context context) throws IOException, InterruptedException{ Configuration conf = context.getConfiguration(); Cluster cluster = new Cluster(conf); Job currentJob = cluster.getJob(context.getJobID()); mapperCounter = currentJob.getCounters().findCounter(GROUP_NAME, COUNTER_NAME).getValue(); }
在调用计数器时指定GROUP_NAME
。 例如
context.getCounter("com.example.mycode", "MY_COUNTER").increment(1);
然后
mapperCounter = currentJob.getCounters().findCounter("com.example.mycode", "MY_COUNTER").getValue();
另外,一个重要的一点是,如果计数器不存在,它将初始化一个值为0的计数器。