Java 访问映射器';从减速器上拆下计数器
我需要从减速器中的映射器访问计数器。这可能吗?如果是的话,是如何做到的 例如: 我的绘图程序是:Java 访问映射器';从减速器上拆下计数器,java,hadoop,Java,Hadoop,我需要从减速器中的映射器访问计数器。这可能吗?如果是的话,是如何做到的 例如: 我的绘图程序是: public class CounterMapper extends Mapper<Text,Text,Text,Text> { static enum TestCounters { TEST } @Override protected void map(Text key, Text value, Context context)
public class CounterMapper extends Mapper<Text,Text,Text,Text> {
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);
}
}
公共类计数器映射器扩展映射器{
静态枚举测试计数器{TEST}
@凌驾
受保护的空映射(文本键、文本值、上下文)
抛出IOException、InterruptedException{
getCounter(TestCounters.TEST).increment(1);
编写(键、值);
}
}
我的减速机是
public class CounterReducer extends Reducer<Text,Text,Text,LongWritable> {
@Override
protected void reduce(Text key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
Counter counter = context.getCounter(CounterMapper.TestCounters.TEST);
long counterValue = counter.getValue();
context.write(key, new LongWritable(counterValue));
}
}
公共类计数器减速机扩展减速机{
@凌驾
受保护的void reduce(文本键、Iterable值、上下文)
抛出IOException、InterruptedException{
计数器计数器=context.getCounter(CounterMapper.TestCounters.TEST);
长计数器值=counter.getValue();
write(key,新的LongWritable(counterValue));
}
}
计数器值始终为0。
我做错了什么,还是这根本不可能?map/reduce的整个要点是并行化作业。将有许多唯一的映射器/还原器,因此该值无论如何都不正确,除非运行映射/还原对 他们有一个单词计数示例:
您可以将context.write(字,一)更改为context.write(行,一)全局计数器值永远不会广播回每个映射器或还原器。如果希望还原程序可以使用映射程序记录的#,则需要依赖一些外部机制来完成此操作。在还原程序的配置(JobConf)中,可以使用JobConf对象查找还原程序自己的作业id。这样,您的reducer可以创建自己的JobClient(即到jobtracker的连接),并查询此作业(或任何与此相关的作业)的计数器 现在,您可以在reduce()方法内部使用mapperCounter 你真的需要在这里试一试。我正在使用旧的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();
}
我问了,但我还没有解决我的问题。然而,我想到了另一个解决办法。在映射器中,字数是计数的,可以在映射器末尾运行的清除函数中使用最小键(以便该值位于head中)将其写入中间输出。在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<Object, Text, Text, LongWritable> {
private final static LongWritable one = new LongWritable(1);
private Text word = new Text();
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
StringTokenizer tokenizer = new StringTokenizer(line);
while (tokenizer.hasMoreTokens()) {
word.set(tokenizer.nextToken());
context.write(word, one);
context.getCounter(TestCounters.TEST).increment(1);
}
}
@Override
protected void cleanup(Context context) throws IOException, InterruptedException {
context.write(new Text("!"),new LongWritable(context.getCounter(TestCounters.TEST).getValue()));
}
}
public static class Reduce extends Reducer<Text, LongWritable, Text, LongWritable> {
public void reduce(Text key, Iterable<LongWritable> values, Context context)
throws IOException, InterruptedException {
int sum = 0;
for (LongWritable val : values) {
sum += val.get();
}
context.write(key, new LongWritable(sum));
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = new Job(conf, "WordCount");
job.setJarByClass(WordCount.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
job.setMapperClass(Map.class);
job.setReducerClass(Reduce.class);
job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);
}
}
中间输出
**! 33**
2008 1
行动1
安卡拉,1
基金会1
It 1
想法1
特古特1号
特古特1号
Turgut 1
伊扎基答案的改进
findCounter(计数器名称)
不再受支持-
调用计数器时,指定组名称。e、 g
context.getCounter("com.example.mycode", "MY_COUNTER").increment(1);
然后
还有一点很重要,如果计数器不存在,它将用值0初始化计数器。作业跟踪器跟踪计数器。还原程序中不提供映射程序中的计数器,这似乎与直觉相反,但在
Hadoop
还原程序中,还原程序可以在所有映射程序完成之前开始执行。在这种情况下,计数器的值可以在减速机中的不同时间读取。要了解还原程序如何在映射程序完成执行之前启动,请访问以下帖子:@abhinavkulkarni实际上,只有还原程序的洗牌阶段才能在所有映射程序启动之前启动,这与计数器无关。因此,当reducer的reduce阶段开始时,所有映射器计数器都是正确的。在同一篇文章中:“另一方面,排序和减少只能在所有映射器都完成后启动。”我尝试了这一点,但在下面的行mapperCounter=currentJob.getCounters().findCounter(COUNTER_NAME)中出现了java空点异常错误,在该行中,我用我的自定义计数器替换了COUNTER_NAME。看起来cluster.getJob(context.getJobID())
在hadoop的独立操作中不起作用。在单节点群集模式下运行时,这对我有效。您从何处导入Cluster
?Intellij IDEA建议我导入org.apache.commons.math.stat.clustering.Cluster
,而此导入不接受`配置作为构造函数的参数彼得。
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.
@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();
}
context.getCounter("com.example.mycode", "MY_COUNTER").increment(1);
mapperCounter = currentJob.getCounters().findCounter("com.example.mycode", "MY_COUNTER").getValue();