Если вы когда-нибудь задумывались, каково это — укротить дикого зверя по имени Apache Hadoop, создавая собственные плагины, вас ждёт настоящее приключение. Представьте Hadoop как надёжного, но иногда своенравного друга, который способен справиться с огромными объёмами работы, но для этого ему нужны очень конкретные инструкции. Сегодня мы погрузимся в искусство разработки плагинов для Hadoop на Java, и поверьте, это увлекательнее, чем смотреть, как краска сохнет на стойке сервера.
Подготовка: понимание архитектуры плагинов Hadoop
Прежде чем мы начнём орудовать клавиатурами Java как цифровыми самурайскими мечами, давайте разберёмся, с чем имеем дело. Архитектура плагинов Hadoop построена вокруг концепции расширяемости — это как иметь швейцарский армейский нож, который позволяет вам добавлять собственные инструменты.
Прелесть Hadoop заключается в его фреймворке MapReduce, который разбивает сложные задачи обработки данных на управляемые части. Когда вы разрабатываете плагин, вы, по сути, создаёте специализированный инструмент, который органично вписывается в этот распределённый конвейер обработки.
Подготовка среды разработки: основа совершенства
Давайте приступим к настройке. Вам понадобится несколько основных инструментов в наборе разработчика, и нет, кофеин в официальном списке не указан (хотя он должен быть).
Основные инструменты и зависимости
Первое, что нужно сделать, — настроить Eclipse IDE для разработки Hadoop. Процесс несложный, но требует внимания к деталям:
- Скачать и установить Eclipse IDE.
- Переместить папку eclipse в домашний каталог.
- Скачать необходимые JAR-файлы:
- hadoop-core-1.2.1.jar
- commons-cli-1.2.jar
Конфигурация Maven: супергерой вашей системы сборки
Maven станет вашим лучшим другом в этом путешествии. Это как иметь личного помощника, который никогда не забывает включить нужные зависимости и всегда идеально упаковывает всё.
Создайте файл pom.xml, который будет выглядеть примерно так:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>hadoop-custom-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<hadoop.version>2.7.1</hadoop.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${hadoop.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifest>
<mainClass>com.example.CustomHadoopPlugin</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
Конфигурация maven-jar-plugin имеет решающее значение, потому что она сообщает Maven, какой класс содержит ваш основной метод — думайте об этом как о координатах GPS для начальной точки вашего приложения.
Создание первого плагина Hadoop: пример инвертированного индекса
Теперь самое интересное — создадим настоящий работающий плагин Hadoop. Мы создадим генератор инвертированного индекса, который похож на создание исчерпывающего оглавления для огромных наборов данных. Это невероятно полезно для поисковых систем и приложений для анализа текста.
Основной класс драйвера
Каждому плагину Hadoop нужен класс драйвера — это дирижёр вашего распределённого оркестра:
package com.example;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class CustomHadoopPlugin {
public static void main(String[] args) throws IOException,
ClassNotFoundException,
InterruptedException {
if (args.length != 2) {
System.err.println("Использование: CustomHadoopPlugin <входной путь> <выходной путь>");
System.exit(-1);
}
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "custom inverted index");
// Установка jar-файла, содержащего драйвер, маппер и редукер
job.setJarByClass(CustomHadoopPlugin.class);
// Установка классов маппера и редуктора
job.setMapperClass(InvertedIndexMapper.class);
job.setCombinerClass(InvertedIndexReducer.class);
job.setReducerClass(InvertedIndexReducer.class);
// Установка типов выходных ключей и значений
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
// Установка входных и выходных путей
FileInputFormat.addInputPath(job, new Path(args));
FileOutputFormat.setOutputPath(job, new Path(args));
// Ожидание завершения задания
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
Маппер: где начинается магия
Маппер — это место, где начинается преобразование данных. Это как команда очень сосредоточенных библиотекарей, которые берут на себя раздел книг и составляют подробные каталоги:
package com.example;
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
public class InvertedIndexMapper extends Mapper<LongWritable, Text, Text, Text> {
private Text word = new Text();
private Text documentId = new Text();
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
// Получение имени файла из входного раздела
String filename = ((FileSplit) context.getInputSplit()).getPath().getName();
documentId.set(filename);
// Преобразование строки в нижний регистр и разделение на токены
String line = value.toString().toLowerCase();
StringTokenizer tokenizer = new StringTokenizer(line, ".,;!?\\t\\n\\r\\f ()[]{}");
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken().trim();
// Пропуск пустых токенов и очень коротких слов
if (token.length() > 2) {
word.set(token);
context.write(word, documentId);
}
}
}
}
Обратите внимание, как мы используем StringTokenizer с комплексным набором разделителей. Этот подход гарантирует, что мы учтём все крайние случаи — потому что никому не нравится, когда обработка текста пропускает пунктуацию и создаёт «слова» вроде «hello,world».
Редуктор: объединение всего воедино
Редуктор — ваш эксперт по консолидации данных. Он собирает всю разрозненную информацию от мапперов и создаёт окончательный, организованный результат:
package com.example;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
public class InvertedIndexReducer extends Reducer<Text, Text, Text, Text> {
private Text result = new Text();
@Override
protected void reduce(Text key, Iterable