Hadoop MapReduce-eksempel: Først Java Program med Code

⚡ Smart opsummering

Hadoop MapReduce-programmer er skrevet som tre Java klasser, en mapper, en reducer og en driver, der kompileres, pakkes i en jar og sendes til klyngen for at tælle salg pr. land.

  • 🔘 datasæt: SalesJan2009.csv indeholder én transaktion pr. linje, med landet i den ottende kommaseparerede kolonne.
  • ☑️ Kortlægger: SalesMapper opdeler hver linje og udsender landet parret med den konstante værdi et.
  • ✅ Reducer: SalesCountryReducer summerer antallet af ankommende varer for hvert land til et enkelt frekvensantal.
  • 🧪 Chauffør: SalesCountryDriver navngiver jobbet, deklarerer nøgle- og værdityper og forbinder begge klasser sammen.
  • 🛠️ Build: Kompiler med javac, tilføj en Main-Class manifestpost, og pak derefter alt med jar cfm.
  • ⚠️ Løb: Kopier CSV-filen til HDFS, send jar-filen, og læs part-00000 fra outputmappen.

Hadoop MapReduce eksempel på oprettelse af en første Java program

I denne tutorial lærer du at bruge Hadoop med MapReduce-eksempler. De anvendte inputdata er SalgJan2009.csvDen indeholder salgsrelaterede oplysninger såsom produktnavn, pris, betalingsmetode, by og land for kunden. Målet er at finde ud af antallet af solgte produkter i hvert land.

Første Hadoop MapReduce-program

Nu i dette MapReduce tutorial, vil vi skabe vores første Java MapReduce-program:

Skærmbilledet nedenfor viser de rå SalesJan2009-data, hvor hver linje er én transaktion, og landet er placeret i den ottende kommaseparerede kolonne.

Salgsdata for SalesJan2009 åbnet i et regneark, der viser transaktionskolonner

Sørg for, at du har Hadoop installeret. Før du starter med selve processen, skal du ændre brugeren til 'hduser' (id'et, der blev brugt under konfigurationen af ​​Hadoop — du kan skifte til det bruger-id, der blev brugt under din egen Hadoop-konfiguration).

su - hduser_

Prompten ændres til hduser-kontoen, som vist nedenfor.

Terminalprompt efter skift til hduser-kontoen

Trin 1) Opret projektmappen og kildefilerne

Opret en ny mappe med navnet MapReduceTutorial som vist i MapReduce-eksemplet nedenfor.

sudo mkdir MapReduceTutorial

mkdir-kommandoen opretter MapReduceTutorial-mappen

Giv tilladelser

sudo chmod -R 777 MapReduceTutorial

chmod-kommando der giver fulde tilladelser på MapReduceTutorial

Skab de tre Java kildefilerne nedenfor i MapReduceTutorial. Bemærk, at alle tre bruger den ældre org.apache.hadoop.mapred API, som stadig leveres med 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();
        }
    }
}

Download filer her

Arkivet udvides til de samme tre kildefiler, som vist her.

Extracted arkivliste SalesMapper, SalesCountryReducer og SalesCountryDriver

Kontroller filtilladelserne for alle disse filer

Lang liste, der viser filtilladelser på de tre Java kildefiler

Hvis der mangler 'læse'-tilladelser, skal du give dem:

chmod-kommandoen tilføjer læsetilladelse til Java kildefiler

Trin 2) Eksporter Hadoop-klassestien

Eksporter klassestien som vist i Hadoop-eksemplet nedenfor.

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/*"

Den eksporterede klassesti afspilles igen ved prompten.

Shell-prompt efter eksport af Hadoop CLASSPATH-variablen

Trin 3) Kompilér Java filer

Kompilér Java filer (disse filer findes i mappen Final-MapReduceHandsOn). Deres klassefiler vil blive placeret i pakkemappen.

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

javac-output viser en advarsel om udfasning under kompilering

Denne advarsel kan trygt ignoreres – den rapporterer kun, at mapred API'en er udfaset.

Denne kompilering vil oprette en mappe i den aktuelle mappe med navnet på den pakkenavn, der er angivet i Java kildefilen (dvs. SalesCountry i vores tilfælde) og læg alle kompilerede klassefiler i den.

SalesCountry-pakkemappen, der indeholder de tre kompilerede klassefiler

Trin 4) Opret manifestfilen

Opret en ny fil Manifest.txt

sudo gedit Manifest.txt

Tilføj følgende linje til den:

Main-Class: SalesCountry.SalesCountryDriver

gedit vindue, der viser Main-Class-posten i Manifest.txt

SalesCountry.SalesCountryDriver er navnet på hovedklassen. Bemærk venligst, at du skal trykke på Enter-tasten i slutningen af ​​denne linje.

Trin 5) Pak klasserne i en krukke

Opret en Jar-fil

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

jar-kommandoopbygning af ProductSalePerCountry.jar fra manifestet

Tjek, at jar-filen er oprettet

Katalogfortegnelse, der bekræfter, at ProductSalePerCountry.jar blev oprettet

Trin 6) Start Hadoop

Start Hadoop

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

Trin 7) Kopier inputfilen til HDFS

Kopier filen SalesJan2009.csv til ~/inputMapReduce

Brug nu kommandoen nedenfor til at kopiere ~/inputMapReduce til HDFS.

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

copyFromLocal-kommandooutput med en advarsel fra native-biblioteket

Vi kan roligt ignorere denne advarsel.

Kontroller, om en fil faktisk er kopieret eller ej.

$HADOOP_HOME/bin/hdfs dfs -ls /inputMapReduce

hdfs dfs -ls viser SalesJan2009.csv i inputMapReduce-mappen

Trin 8) Kør MapReduce-jobbet

Kør MapReduce-job

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

Konsoloutput, mens SalePerCountry-jobbet kører på klyngen

Dette vil oprette en output-mappe med navnet mapreduce_output_sales on HDFS. Indholdet af denne mappe vil være en fil, der indeholder produktsalg pr. land.

Trin 9) Læs resultaterne

Resultatet kan ses via kommandogrænsefladen som,

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

Land- og salgstalspar udskrevet af kommandoen hdfs dfs -cat

Resultater kan også ses via en webgrænseflade som-

Åbne http://localhost:50070/ i en webbrowser. På Hadoop 3.x blev NameNode-webgrænsefladen flyttet til port 9870, så brug http://localhost:9870/ der i stedet.

NameNode webgrænseflade hjemmeside i en browser

Vælg nu 'Gennemse filsystemet' og naviger til /mapreduce_output_sales

HDFS-filbrowser, der viser mappen mapreduce_output_sales

Åben del-r-00000

part-r-00000 resultatfil åbnet i HDFS-browseren

Forklaring af SalesMapper Class

Med arbejdet kørende fra ende til anden, gennemgår de næste tre afsnit, hvad hver klasse rent faktisk laver.

I dette afsnit vil vi forstå implementeringen af ​​SalesMapper-klassen.

1. Vi starter med at angive et pakkenavn til vores klasse. SalesCountry er navnet på vores pakke. Bemærk venligst, at outputtet fra kompileringen, SalesMapper.class, vil blive placeret i en mappe med dette pakkenavn: SalesCountry.

Efterfulgt af dette importerer vi bibliotekspakker.

Nedenstående snapshot viser en implementering af SalesMapper-klassen-

Redigeringsvisning af den komplette implementering af SalesMapper-klassen

Prøve Code Forklaring:

1. SalesMapper Klasse Definition-

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

Hver mapper-klasse skal udvides fra MapReduceBase-klassen, og den skal implementere Mapper-grænsefladen.

2. Definition af 'kort' funktion-

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

Hoveddelen af ​​Mapper-klassen er en 'map()'-metode, der accepterer fire argumenter.

Ved hvert kald til metoden 'map()' sendes et nøgle-værdi-par ('key' og 'value' i denne kode).

Metoden 'map()' starter med at opdele inputteksten, der modtages som et argument. Den opdeler hver linje i felter.

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

Her bruges ',' som afgrænser.

Derefter dannes et par ved hjælp af en post ved det 7. indeks i arrayet 'SingleCountryData' og værdien '1'.

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

Vi vælger posten ved 7. indeks, fordi vi har brug for landedata, og den er placeret ved 7. indeks i arrayet 'SingleCountryData'.

Bemærk venligst, at vores inputdata er i nedenstående format (hvor Land er på 7. indeks, med 0 som startindeks) -

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

Outputtet fra mapper er igen et nøgle-værdi-par, der udsendes ved hjælp af 'collect()'-metoden i 'OutputCollector'.

Forklaring af SalesCountryReducer Class

I dette afsnit vil vi forstå implementeringen af ​​SalesCountryReducer-klassen.

1. Vi starter med at angive et navn på pakken til vores klasse. SalesCountry er navnet på vores pakke. Bemærk venligst, at outputtet fra kompileringen, SalesCountryReducer.class, vil blive placeret i en mappe med dette pakkenavn: SalesCountry.

Efterfulgt af dette importerer vi bibliotekspakker.

Nedenstående snapshot viser en implementering af SalesCountryReducer-klassen-

Redigeringsvisning af den komplette implementering af SalesCountryReducer-klassen

Code Forklaring:

1. SalesCountryReducer Klassedefinition-

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

Her er de to første datatyper, 'Text' og 'IntWritable', datatypen for inputnøgleværdien til reduceringsfunktionen.

Outputtet fra mapperen er i form af , Dette output fra mapper bliver input til reduceren. Så for at justere med dens datatype bruges Text og IntWritable som datatyper her.

De sidste to datatyper, 'Tekst' og 'IntWritable', er datatypen for output genereret af reduceren i form af et nøgle-værdi-par.

Hver reducer-klasse skal udvides fra MapReduceBase-klassen, og den skal implementere Reducer-grænsefladen.

2. Definition af 'reducer' funktion-

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

Input til reduce()-metoden er en nøgle med en liste over flere værdier.

For eksempel vil det i vores tilfælde være-

, , , , , .

Dette gives til reducer som

Så for at acceptere argumenter af denne form bruges de første to datatyper, nemlig tekst og iterator. Tekst er en datatype for nøgle og iterator er en datatype for en liste over værdier for den pågældende nøgle.

Det næste argument er af typen OutputCollector som indsamler outputtet fra reducerfasen.

Metoden reduce() begynder med at kopiere nøgleværdien og initialisere frekvensantallet til 0.

Text key = t_key;
int frequencyForCountry = 0;

Derefter bruger vi 'while'-løkken til at iterere gennem listen over værdier, der er knyttet til nøglen, og beregne den endelige frekvens ved at summere alle værdierne.

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

Nu sender vi resultatet til output-samleren i form af nøgle og opnået frekvensantal.

Nedenstående kode gør dette-

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

Forklaring af SalesCountryDriver Class

I dette afsnit vil vi forstå implementeringen af ​​SalesCountryDriver-klassen.

1. Vi starter med at angive et pakkenavn til vores klasse. SalesCountry er navnet på vores pakke. Bemærk venligst, at outputtet fra kompileringen, SalesCountryDriver.class, vil blive placeret i en mappe med dette pakkenavn: SalesCountry.

Her er en linje, der specificerer pakkenavn efterfulgt af kode for at importere bibliotekspakker.

Pakkedeklaration og importerklæringer for SalesCountryDriver

2. Definer en driverklasse, som vil oprette et nyt klientjob, konfigurationsobjekt og annoncere Mapper- og Reducer-klasser.

Chaufførklassen er ansvarlig for at indstille vores MapReduce-job til at køre ind HadoopI denne klasse angiver vi jobnavn, datatype for input/output og navne på mapper- og reducer-klasser.

Driverkodeindstilling af jobnavn, nøgle- og værdiklasser og formater

3. I nedenstående kodestykke indstiller vi input- og outputmapper, som bruges til henholdsvis at forbruge inputdatasæt og producere output.

arg[0] og arg[1] er kommandolinjeargumenterne, der sendes med en kommando givet i MapReduce hands-on, dvs.

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

Driverkode, der sender input- og outputstierne fra kommandolinjen

4. Udløs vores job

Koden nedenfor starter udførelsen af ​​MapReduce-jobbet-

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

Ofte Stillede Spørgsmål

Alle tre klasser importerer org.apache.hadoop.mapred, den originale MapReduce API. Hadoop 3.x leverer og kører den stadig, men nyt arbejde skrives normalt mod org.apache.hadoop.mapreduce, som erstatter JobConf og JobClient med Configuration og Job.

Modeller, der er trænet på tidligere jobhistorik, forudsiger kørselstid, foreslår opdelingsstørrelser og reduktionsantal og finder skævheder, før en kørsel afsluttes. De markerer også job, hvis tællere for spildte poster eller mislykkede opgaver afviger uden for det sædvanlige område.

Copilot fuldfører de gentagne dele godt: klassesignaturer, generiske programmer, import og driverkonfigurationskald. Forretningslogikken, såsom hvilken kolonne der indeholder landet, skal stadig kontrolleres i forhold til det faktiske skema af en udvikler.

Hadoop nægter at skrive ind i en eksisterende outputmappe, så færdige resultater aldrig overskrives. Slet mapreduce_output_sales rekursivt med hdfs dfs -rm -r, eller send en anden outputsti ved næste kørsel.

Jar-værktøjet ignorerer en endelig manifestlinje, der ikke har nogen linjeafslutning, så Main-Class slettes lydløst. Jar-værktøjet bygger derefter uden fejl, men fejler under kørsel, fordi der ikke er registreret nogen main-klasse.

Ja. Eksemplet fastgør 2.2.0 i hvert jar-navn. I en anden udgivelse skal du erstatte den version, eller blot bruge outputtet fra hadoop-classpath, som udskriver alle jar-filer, som den installerede distribution har brug for.

Nej. En pseudodistribueret installation med én node kører den uændret, fordi kommandoerne kun antager, at HDFS og YARN er startet. Den samme jar-fil sendes til en rigtig klynge med flere noder uden nogen kodeændring.

Skift array-indekset i mapperen fra 7 til 5, fordi City er den sjette kolonne i SalesJan2009.csv. Kompiler igen, genopbyg jar-filen, og kør jobbet mod en ny outputmappe.

Opsummer dette indlæg med: