В первом посте этой серии мы рассмотрели, как MultipleOutputFormat класс можно использовать в задаче для записи в несколько выходных файлов. У этого подхода было несколько недостатков, которые заключались в том, что его нельзя было использовать на стороне задания, в котором использовались редукторы, и он работал только со старым mapred API.
В этом посте мы рассмотрим MultipleOutputs класс, который предлагает альтернативу, MultipleOutputFormat а также устраняет ее недостатки.
MultipleOutputs
Использование MultipleOutputs класса — это более современный способ записи Hadoop на несколько выходов. Он имеет mapredи mapreduce реализацию API, и позволяет работать с несколькими классами OutputFormat в вашей работе. Его подход отличается от MultipleOutputFormat — вместо того, чтобы определять свой собственный, OutputFormat он просто предоставляет некоторые вспомогательные методы, которые должны вызываться в вашем коде драйвера, а также в вашем преобразователе / преобразователе.
Два MultipleOutputs класса mapred и mapreduce по функциональности близки, главное отличие заключается в поддержке выходов с несколькими именами, которые мы рассмотрим позже в этом посте.
Давайте посмотрим, как мы могли бы достичь того же результата, что и мы MultipleOutputFormat. Если вы помните из предыдущего поста в этой серии, мы работали с некоторыми образцами данных с фруктового рынка, где точками данных были местоположение каждого рынка и проданный фрукт:
cupertino apple sunnyvale banana cupertino pear
Наша цель состоит в том, чтобы разбить выходные данные по городам, чтобы были файлы, специфичные для города. Прежде всего, это наш код драйвера, где нам нужно сообщить MultipleOutputs именованные выходные данные и связанные с ними OutputFormat классы. Для простоты мы выбрали TextOutputFormat оба варианта, но вы можете использовать разные OutputFormats для каждого именованного вывода.
MultipleOutputs.addNamedOutput(jobConf, "cupertino", TextOutputFormat.class, Text.class, Text.class); MultipleOutputs.addNamedOutput(jobConf, "sunnyvale", TextOutputFormat.class, Text.class, Text.class);
Именованные выходы «cupertino» и «sunnyvale» используются для двух целей: во- MultipleOutputs первых, в качестве логических ключей, которые вы используете в своем преобразователе и редукторе для поиска связанных с ними OutputCollector классов. И, во-вторых, они используются в качестве выходных файлов в HDFS.
Мы не можем использовать редуктор идентификации в этом примере, так как мы должны использовать MultipleOutputs класс для перенаправления нашего вывода в соответствующий файл, поэтому давайте продолжим и посмотрим, как будет выглядеть редуктор.
class Reduce extends MapReduceBase
implements Reducer<Text, Text, Text, Text> {
private MultipleOutputs output;
@Override
public void configure(final JobConf job) {
super.configure(job);
output = new MultipleOutputs(job);
}
@Override
public void reduce(final Text key, final Iterator<Text> values,
final OutputCollector<Text, Text> collector, final Reporter reporter)
throws IOException {
while (values.hasNext()) {
output.getCollector(key.toString(), reporter).collect(key, values.next());
}
}
}
Как вы можете, вы не используете OutputCollector предоставленный нам в reduce методе. Вместо этого вы создаете MultipleOutputs экземпляр в configure методе, который используется в методе Reduce. Для каждой входной записи редуктора мы используем ключ для поиска OutputCollector и затем посылаем каждую пару ключ / значение в этот коллектор. Помните, что при вызове getCollector вы должны использовать один из именованных выходов, которые вы определили в драйвере задания. В нашем случае нашими клавишами ввода являются «cupertino» или «sunnyvale», и они отображаются непосредственно на именованные выходы, которые мы определили в нашем драйвере, поэтому мы в хорошей форме.
Давайте проверим содержимое каталога вывода задания после запуска задания.
$ hadooop -lsr /output /output/cupertino-r-00000 /output/sunnyvale-r-00000 /output/part-00000 /output/part-00001
Этот вывод подчеркивает одно из ключевых различий между MultipleOutputs и MultipleOutputFormat. При использовании MultipleOutputs вы можете выводить на обычный редуктор OutputCollector, или OutputCollector на именованный выход, или на оба, поэтому вы видите part-nnnnn файлы.
Но ждать! Одна из проблем MultipleOutputs заключается в том, что вам необходимо заранее определить разделы «купертино» и «солнечный сезон» в нашем драйвере. Что если мы не знали разделов раньше времени?
Динамические файлы с классом MultipleOutputs
До сих MultipleOutputs пор к нам относились хорошо — он поддерживал как старый, так и новый API MapReduce, а также мог поддерживать несколько классов OutputFormat в одном и том же редукторе. Но, как мы видели, нам, по сути, пришлось предварительно определить выходные файлы в нашем коде драйвера. Итак, как нам обращаться со случаями, когда мы хотим, чтобы это динамически выполнялось в редукторе?
К счастью, у них MultipleOutputs есть понятие «многоименованный» вывод. В методе драйвера вместо перечисления всех выходных файлов, которые мы хотим, мы просто добавим одно логическое имя с именем «fruit», используя addMultiNamedOutput вместо addNamedOutput:
MultipleOutputs.addMultiNamedOutput(jobConf, "fruit", TextOutputFormat.class, Text.class, Text.class);
В нашем редукторе мы всегда указываем «фрукты» в качестве имени, но мы используем другой getCollector метод, который принимает дополнительное поле, которое используется для определения имени файла, которое используется для вывода:
output.getCollector("fruit", key.toString(), reporter).collect(key, values.next());
Давайте сделаем еще один листинг HDFS:
$ hadooop -lsr /output /output/fruit_cupertino-r-00000 /output/fruit_sunnyvale-r-00000 /output/part-00000 /output/part-00001
Ура! Теперь у нас есть несколько выходных файлов, которые динамически создаются на основе выходного ключа редуктора, как мы это делали с MultipleOutputFormat.
К сожалению, вывод с несколькими именами поддерживается только старым mapred API, тогда как с новым mapreduce API вы вынуждены определять свои разделы в драйвере задания.
Вывод
Есть много вещей, которые могут понравиться MultipleOutputs, а именно: поддержка «старых» и «новых» API MapReduce, а также поддержка нескольких OutputFormat классов. Единственный его недостаток в том, что многоименованные выходные данные поддерживаются только в старом mapred API, поэтому те, кто ищет динамические разделы в новом mapreduce API, не поддерживаются ни одним из них MultipleOutputs или не MultipleOutputFormat описаны в части 1 .