Как я могу узнать, является ли задача редуктором или объединителем во время выполнения в Hadoop?
Если операция, выполняемая с MapReduce, не является коммутативной и ассоциативной, объединитель не может быть таким же, как редуктор.
Например, при вычислении среднего значения сумматоры суммируют значения для ключа, а редуктор суммирует, а затем делит сумму на общее количество значений для этого ключа. Код сумматора имеет лишь небольшую модификацию. Что если бы вы могли использовать один и тот же класс как для комбинатора, так и для редуктора и иметь код, который может определить, является ли текущая задача комбинатором или редуктором? Если он узнает, что он является редуктором, он делит сумму на счет.
Что-то вроде этого:
protected void reduce(Text keyIn, Iterable<PairWritable> valuesIn,
Context context)
throws IOException, InterruptedException {
double sum = 0.0d;
long count = 0l;
for (PairWritable valueIn : valuesIn) {
sum += valueIn.getSum();
count += valueIn.getCount();
}
if (THIS_IS_A_REDUCER) {
sum /= count;
}
context.write(keyIn, new PairWritable(sum, count));
}
Можно ли сделать это? Может мир кода THIS_IS_A_REDUCER
сверху что-нибудь заменить?
Я могу определить, является ли задача маппером или редуктором по идентификатору String при попытке к выполнению задачи, но как объединители, так и редукторы, похоже, имеют сходные строковые шаблоны.
3 ответа
Я полагаю, вы могли бы допросить Context
Объект и получить идентификатор задачи. Затем, когда у вас есть идентификатор, картограф (включая сумматор) будет иметь имя "m", а редуктор - "r" в названии.
Чтобы получить идентификатор попытки задачи, используйте .getTaskAttemptID (). Я думаю, что вы должны быть в состоянии сделать context.getTaskAttemptID()
использовать это, но я не могу проверить это, чтобы быть уверенным.
Хотя я знаю, что этот вопрос уже решен, у меня есть другое решение. Я сделал мой Combiner подклассом редуктора. Затем в коде Редуктора я смог проверить, являюсь ли я подклассом Combiner или нет.
Основным преимуществом для этого является то, что мне нужно было изменить мой ключ на этапе Редуктора, но я не хотел менять его на этапе Объединения (иначе я бы применил одно и то же преобразование дважды). Кроме того, 95% кода был идентичен.
Это некорректный вопрос. Всякий раз, когда вы обнаруживаете, необходимо различать, какая функция () вызывает задачу. Добавьте объединитель. Например, ты пишешь
public static class Combine extends MapReduceBase implements Reducer<Text, Text, Text, Text> {
public void reduce(Text key, Iterator<Text> message, OutputCollector<Text, Text> output, Reporter reporter) throws IOException {}
public static class Reduce extends MapReduceBase implements Reducer<Text, Text, Text, Text> {
public void reduce(Text key, Iterator<Text> message, OutputCollector<Text, Text> output, Reporter reporter) throws IOException {}
В основной (), вы пишете
conf.setReducerClass(Reduce.class);
conf.setCombinerClass(Combine.class);