Приклад Hadoop MapReduce: Перший Java Програма с Code

⚡ Розумний підсумок

Програми Hadoop MapReduce пишуться як три Java класи, маппер, редуктор та драйвер, які компілюються, упаковуються в JAR-файл та надсилаються до кластера для підрахунку продажів у кожній країні.

  • 🔘 Набір даних: Файл SalesJan2009.csv містить одну транзакцію на рядок, а країна вказана у восьмому стовпці, розділеному комами.
  • ☑️ Картограф: SalesMapper розділяє кожен рядок і видає країну в парі з константним значенням один.
  • ✅ Редуктор: SalesCountryReducer підсумовує ті, що надходять для кожної країни, в єдиний підрахунок частоти.
  • 🧪 водій: SalesCountryDriver надає назву завданню, оголошує типи ключів і значень і з'єднує обидва класи разом.
  • 🛠️ Буду: Скомпілюйте за допомогою javac, додайте запис маніфесту Main-Class, а потім упакуйте все за допомогою jar cfm.
  • ⚠️ Пробіг: Скопіюйте CSV-файл у HDFS, надішліть JAR-файл та зчитайте частину 00000 з вихідного каталогу.

Приклад створення першого Hadoop MapReduce Java більшість квитків вже розпродано!

У цьому посібнику ви навчитеся використовувати Hadoop із прикладами MapReduce. Використані вхідні дані SalesJan2009.csvВін містить інформацію, пов’язану з продажами, таку як назва товару, ціна, спосіб оплати, місто та країна клієнта. Мета полягає в тому, щоб з’ясувати кількість товарів, проданих у кожній країні.

Перша програма Hadoop MapReduce

Тепер у цьому Підручник MapReduce, ми створимо свій перший Java Програма MapReduce:

На знімку екрана нижче показано необроблені дані SalesJan2009, де кожен рядок відповідає одній транзакції, а країна знаходиться у восьмому стовпці, розділеному комами.

Дані про продажі за січень 2009 року відкрито в електронній таблиці зі стовпцями транзакцій

Переконайтеся, що у вас встановлено Hadoop. Перш ніж розпочати сам процес, змініть ім'я користувача на «hduser» (ідентифікатор, який використовувався під час налаштування Hadoop — ви можете переключитися на ідентифікатор користувача, який використовувався під час власного налаштування Hadoop).

su - hduser_

Запит змінюється на обліковий запис hduser, як показано нижче.

Запит термінала після перемикання на обліковий запис hduser

Крок 1) Створення каталогу проекту та вихідних файлів

Створіть новий каталог з назвою MapReduceTutorial, як показано в наведеному нижче прикладі MapReduce.

sudo mkdir MapReduceTutorial

Команда mkdir для створення каталогу MapReduceTutorial

Дайте дозволи

sudo chmod -R 777 MapReduceTutorial

Команда chmod надає повні дозволи на MapReduceTutorial

Створіть три Java вихідні файли нижче в MapReduceTutorial. Зверніть увагу, що всі три використовують старішу версію org.apache.hadoop.mapred API, який досі постачається з Hadoop 3.x.

SalesMapper.java

package SalesCountry;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.*;

public class SalesMapper extends MapReduceBase implements Mapper <LongWritable, Text, Text, IntWritable> {
	private final static IntWritable one = new IntWritable(1);

	public void map(LongWritable key, Text value, OutputCollector <Text, IntWritable> output, Reporter reporter) throws IOException {

		String valueString = value.toString();
		String[] SingleCountryData = valueString.split(",");
		output.collect(new Text(SingleCountryData[7]), one);
	}
}

SalesCountryReducer.java

package SalesCountry;

import java.io.IOException;
import java.util.*;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.*;

public class SalesCountryReducer extends MapReduceBase implements Reducer<Text, IntWritable, Text, IntWritable> {

	public void reduce(Text t_key, Iterator<IntWritable> values, OutputCollector<Text,IntWritable> output, Reporter reporter) throws IOException {
		Text key = t_key;
		int frequencyForCountry = 0;
		while (values.hasNext()) {
			// replace type of value with the actual type of our value
			IntWritable value = (IntWritable) values.next();
			frequencyForCountry += value.get();
			
		}
		output.collect(key, new IntWritable(frequencyForCountry));
	}
}

SalesCountryDriver.java

package SalesCountry;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapred.*;

public class SalesCountryDriver {
    public static void main(String[] args) {
        JobClient my_client = new JobClient();
        // Create a configuration object for the job
        JobConf job_conf = new JobConf(SalesCountryDriver.class);

        // Set a name of the Job
        job_conf.setJobName("SalePerCountry");

        // Specify data type of output key and value
        job_conf.setOutputKeyClass(Text.class);
        job_conf.setOutputValueClass(IntWritable.class);

        // Specify names of Mapper and Reducer Class
        job_conf.setMapperClass(SalesCountry.SalesMapper.class);
        job_conf.setReducerClass(SalesCountry.SalesCountryReducer.class);

        // Specify formats of the data type of Input and output
        job_conf.setInputFormat(TextInputFormat.class);
        job_conf.setOutputFormat(TextOutputFormat.class);

        // Set input and output directories using command line arguments, 
        //arg[0] = name of input directory on HDFS, and arg[1] =  name of output directory to be created to store the output file.

        FileInputFormat.setInputPaths(job_conf, new Path(args[0]));
        FileOutputFormat.setOutputPath(job_conf, new Path(args[1]));

        my_client.setConf(job_conf);
        try {
            // Run the job 
            JobClient.runJob(job_conf);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Завантажте файли тут

Архів розгортається до тих самих трьох вихідних файлів, як показано тут.

ExtracСписок архівів Ted SalesMapper, SalesCountryReducer та SalesCountryDriver

Перевірте права доступу для всіх цих файлів

Довгий список, що показує дозволи на файли для трьох Java вихідні файли

Якщо дозволи на «читання» відсутні, надайте їх:

Команда chmod додає дозвіл на читання до Java вихідні файли

Крок 2) Експортуйте шлях до класів Hadoop

Експортуйте шлях до класів, як показано в прикладі Hadoop нижче.

export CLASSPATH="$HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-client-core-2.2.0.jar:$HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-client-common-2.2.0.jar:$HADOOP_HOME/share/hadoop/common/hadoop-common-2.2.0.jar:~/MapReduceTutorial/SalesCountry/*:$HADOOP_HOME/lib/*"

Експортований шлях до класів відображається назад у командному рядку.

Запит оболонки після експорту змінної Hadoop CLASSPATH

Крок 3) Скомпілюйте Java файли

Складіть Java файли (ці файли знаходяться в каталозі Final-MapReduceHandsOn). Їхні файли класів будуть поміщені в каталог пакета.

javac -d . SalesMapper.java SalesCountryReducer.java SalesCountryDriver.java

Вивід javac, що показує попередження про застарівання під час компіляції

Це попередження можна безпечно ігнорувати — воно лише повідомляє про те, що API mapred застарів.

Ця компіляція створить каталог у поточному каталозі з назвою пакета, вказаною в Java вихідний файл (тобто SalesCountry у нашому випадку) та помістити туди всі скомпільовані файли класів.

Каталог пакета SalesCountry, що містить три скомпільовані файли класів

Крок 4) Створення файлу маніфесту

Створіть новий файл Manifest.txt

sudo gedit Manifest.txt

Додайте до нього наступний рядок:

Main-Class: SalesCountry.SalesCountryDriver

gedit вікно, що показує запис Main-Class у файлі Manifest.txt

SalesCountry.SalesCountryDriver – це назва основного класу. Зверніть увагу, що в кінці цього рядка потрібно натиснути клавішу Enter.

Крок 5) Упакуйте класи в банку

Створіть файл Jar

jar cfm ProductSalePerCountry.jar Manifest.txt SalesCountry/*.class

Команда jar створює ProductSalePerCountry.jar з маніфесту

Перевірте, чи створено файл jar

Список каталогу, що підтверджує створення ProductSalePerCountry.jar

Крок 6) Запустіть Hadoop

Запустіть Hadoop

$HADOOP_HOME/sbin/start-dfs.sh
$HADOOP_HOME/sbin/start-yarn.sh

Крок 7) Скопіюйте вхідний файл у HDFS

Скопіюйте файл SalesJan2009.csv у ~/inputMapReduce

Тепер скористайтеся командою нижче, щоб скопіювати ~/inputMapReduce до HDFS.

$HADOOP_HOME/bin/hdfs dfs -copyFromLocal ~/inputMapReduce /

Вивід команди copyFromLocal з попередженням щодо native-library

Ми можемо сміливо ігнорувати це попередження.

Перевірте, чи дійсно файл скопійовано чи ні.

$HADOOP_HOME/bin/hdfs dfs -ls /inputMapReduce

hdfs dfs -ls виводить файл SalesJan2009.csv у директорії inputMapReduce

Крок 8) Запустіть завдання MapReduce

Запустіть завдання MapReduce

$HADOOP_HOME/bin/hadoop jar ProductSalePerCountry.jar /inputMapReduce /mapreduce_output_sales

Вивід консолі під час виконання завдання SalePerCountry у кластері

Це створить вихідний каталог під назвою mapreduce_output_sales on HDFS. Вміст цього каталогу буде файлом, що містить дані про продажі продукції по країнах.

Крок 9) Ознайомтеся з результатами

Результат можна побачити через інтерфейс команди так:

$HADOOP_HOME/bin/hdfs dfs -cat /mapreduce_output_sales/part-00000

Пари країн та кількості продажів, що друкуються командою hdfs dfs -cat

Результати також можна побачити через веб-інтерфейс як-

відкритий http://localhost:50070/ у веббраузері. У Hadoop 3.x веб-інтерфейс NameNode перемістився в порт 9870, тому використовуйте http://localhost:9870/ там натомість.

Головна сторінка веб-інтерфейсу NameNode у браузері

Тепер виберіть «Переглянути файлову систему» ​​та перейдіть до /mapreduce_output_sales

Оглядач файлів HDFS, що відображає каталог mapreduce_output_sales

Відкрита частина-r-00000

Файл результатів part-r-00000 відкрито у браузері HDFS

Пояснення класу SalesMapper

Оскільки завдання виконується від початку до кінця, у наступних трьох розділах розглядається, що насправді робить кожен клас.

У цьому розділі ми розглянемо реалізацію класу SalesMapper.

1. Почнемо з визначення назви пакета для нашого класу. SalesCountry – це назва нашого пакета. Зверніть увагу, що результат компіляції, SalesMapper.class, буде поміщений у каталог з назвою цього пакета: SalesCountry.

Після цього ми імпортуємо бібліотечні пакети.

На знімку нижче показано реалізацію класу SalesMapper.

Вигляд редактора повної реалізації класу SalesMapper

Зразок Code Пояснення:

1. Визначення класу SalesMapper-

public class SalesMapper extends MapReduceBase implements Mapper<LongWritable, Text, Text, IntWritable> {

Кожен клас маппера має бути розширеним з класу MapReduceBase та має реалізовувати інтерфейс Mapper.

2. Визначення функції 'map'-

public void map(LongWritable key,
         Text value,
OutputCollector<Text, IntWritable> output,
Reporter reporter) throws IOException

Основною частиною класу Mapper є метод 'map()', який приймає чотири аргументи.

Під час кожного виклику методу 'map()' передається пара ключ-значення ('key' та 'value' у цьому коді).

Метод 'map()' починає з розділення вхідного тексту, отриманого як аргумент. Він розбиває кожен рядок на поля.

String valueString = value.toString();
String[] SingleCountryData = valueString.split(",");

Тут як роздільник використовується ','.

Після цього пара формується з використанням запису на 7-му індексі масиву 'SingleCountryData' та значення '1'.

output.collect(new Text(SingleCountryData[7]), one);

Ми вибираємо запис за 7-м індексом, оскільки нам потрібні дані про країну, і він розташований за 7-м індексом у масиві 'SingleCountryData'.

Зверніть увагу, що наші вхідні дані мають формат, наведений нижче (де країна знаходиться на 7-му місці в індексі, а 0 – як початковий індекс):

Transaction_date,Product,Price,Payment_Type,Name,City,State,Country,Account_Created,Last_Login,Latitude,Longitude

Вихідним результатом mapper знову є пара ключ-значення, яка видається за допомогою методу 'collect()' класу 'OutputCollector'.

Пояснення класу SalesCountryReducer

У цьому розділі ми розглянемо реалізацію класу SalesCountryReducer.

1. Почнемо з визначення назви пакета для нашого класу. SalesCountry – це назва нашого пакета. Зверніть увагу, що результат компіляції, SalesCountryReducer.class, буде поміщений у каталог з назвою цього пакета: SalesCountry.

Після цього ми імпортуємо бібліотечні пакети.

На знімку нижче показано реалізацію класу SalesCountryReducer.

Перегляд редактора повної реалізації класу SalesCountryReducer

Code Пояснення:

1. Визначення класу SalesCountryReducer-

public class SalesCountryReducer extends MapReduceBase implements Reducer<Text, IntWritable, Text, IntWritable> {

Тут перші два типи даних, «Text» та «IntWritable», є типом даних вхідного ключ-значення для редуктора.

Вихідні дані мапера мають вигляд , Цей вивід маппера стає вхідним для редуктора. Отже, для узгодження з його типом даних тут використовуються типи даних Text та IntWritable.

Останні два типи даних, «Text» та «IntWritable», – це типи даних, що генеруються редуктором у вигляді пари ключ-значення.

Кожен клас редуктора має бути розширеним з класу MapReduceBase та має реалізовувати інтерфейс Reducer.

2. Визначення функції «зменшення» -

public void reduce( Text t_key,
             Iterator<IntWritable> values,                           
             OutputCollector<Text,IntWritable> output,
             Reporter reporter) throws IOException {

Вхідними даними для методу reduce() є ключ зі списком кількох значень.

Наприклад, у нашому випадку це буде-

, , , , , .

Це надається редуктору як

Отже, для прийняття аргументів такої форми використовуються перші два типи даних, а саме: текст та ітератор. Текст – це тип даних ключа та ітератора. – це тип даних для списку значень для цього ключа.

Наступний аргумент має тип OutputCollector який збирає вихідний сигнал фази редуктора.

Метод reduce() починається з копіювання значення ключа та ініціалізації frequency count рівним 0.

Text key = t_key;
int frequencyForCountry = 0;

Потім, використовуючи цикл 'while', ми перебираємо список значень, пов'язаних з ключем, і обчислюємо кінцеву частоту шляхом підсумовування всіх значень.

 while (values.hasNext()) {
            // replace type of value with the actual type of our value
            IntWritable value = (IntWritable) values.next();
            frequencyForCountry += value.get();
            
        }

Тепер ми передаємо результат на вихідний колектор у вигляді ключа та отриманого підрахунку частот.

Код нижче робить це -

output.collect(key, new IntWritable(frequencyForCountry));

Пояснення класу SalesCountryDriver

У цьому розділі ми розглянемо реалізацію класу SalesCountryDriver.

1. Почнемо з визначення назви пакета для нашого класу. SalesCountry – це назва нашого пакета. Зверніть увагу, що результат компіляції, SalesCountryDriver.class, буде поміщений у каталог з назвою цього пакета: SalesCountry.

Ось рядок із назвою пакета, а потім кодом для імпорту пакетів бібліотеки.

Оголошення пакета та оператори імпорту SalesCountryDriver

2. Визначте клас драйвера, який створить нове завдання клієнта, об’єкт конфігурації та оголосить класи Mapper і Reducer.

Клас драйвера відповідає за налаштування нашого завдання MapReduce для запуску HadoopУ цьому класі ми вказуємо назву завдання, тип даних вводу/виводу та назви класів маппера та редуктора.

Код драйвера, що встановлює назву завдання, класи ключів і значень, а також формати

3. У наведеному нижче фрагменті коду ми встановлюємо вхідні та вихідні каталоги, які використовуються для використання вхідного набору даних і створення вихідних даних відповідно.

arg[0] та arg[1] – це аргументи командного рядка, що передаються з командою, заданою в практичному вивченні MapReduce, тобто,

$HADOOP_HOME/bin/hadoop jar ProductSalePerCountry.jar /inputMapReduce /mapreduce_output_sales

Код драйвера, що передає вхідні та вихідні шляхи з командного рядка

4. Запустіть нашу роботу

Наведений нижче код запускає виконання завдання MapReduce:

try {
    // Run the job 
    JobClient.runJob(job_conf);
} catch (Exception e) {
    e.printStackTrace();
}

Поширені запитання

Усі три класи імпортують org.apache.hadoop.mapred, оригінальний API MapReduce. Hadoop 3.x все ще постачається та запускає його, але нова робота зазвичай пишеться на основі org.apache.hadoop.mapreduce, який замінює JobConf та JobClient на Configuration та Job.

Моделі, навчені на історії минулих завдань, прогнозують час виконання, пропонують розміри розділень та кількість редюсерів, а також виявляють перекіс до завершення виконання. Вони також позначають завдання, лічильники розлитих записів або невдалих завдань яких виходять за межі звичайного діапазону.

Copilot добре виконує повторювані частини: сигнатури класів, генерики, імпорт та виклики конфігурації драйвера. Бізнес-логіку, таку як стовпець, який містить країну, все ще має перевірити розробник на відповідність реальній схемі.

Hadoop відмовляється записувати в існуючий вихідний каталог, щоб готові результати ніколи не перезаписувалися. Рекурсивно видаліть mapreduce_output_sales за допомогою hdfs dfs -rm -r або передайте інший вихідний шлях під час наступного запуску.

Інструмент jar ігнорує останній рядок маніфесту, який не має символу завершення рядка, тому Main-Class непомітно видаляється. Потім jar збирається без помилок, але завершується аварійно під час виконання, оскільки не записаний головний клас.

Так. У прикладі в кожній назві jar-файлу вказано версію 2.2.0. В іншому випуску замініть її цією версією або просто скористайтеся виводом hadoop classpath, який виводить усі jar-файли, необхідні встановленому дистрибутиву.

Ні. Псевдорозподілена інсталяція з одним вузлом запускається без змін, оскільки команди припускають лише запуск HDFS та YARN. Той самий JAR-файл надсилається до реального багатовузлового кластера без будь-яких змін коду.

Змініть індекс масиву в маппері з 7 на 5, оскільки City є шостим стовпцем SalesJan2009.csv. Перекомпілюйте, перебудуйте JAR-файл і запустіть завдання з новим вихідним каталогом.

Підсумуйте цей пост за допомогою: