MapReduce-példaprogramok

Befejeződött

Egyszerű MapReduce-példaként írhatunk olyan feladatot/programot, amely megszámolja, hogy az egyes szavak hányszor fordulnak elő egy fájlban vagy fájlkészletben. Ha például feltételezzük, hogy két fájl (A és B) egyaránt tartalmazza a „This is a cloud computing course” és a „This is learning path 4 in the cloud computing course” kifejezéseket, akkor az alábbi táblázatban látható kimenetet várhatjuk. Az ilyen programokat általában WordCountnak nevezzük. A WordCount-program a Hadoop-terjesztés része, továbbá futtatáshoz és teszteléshez is elérhető. A szövegfeldolgozási algoritmusok széles spektrumának felel meg a big data típusú adatokat használó alkalmazásokban. Éppen ezért sokan a Hadoop hatékonyságának értékelésére használható, korszerű teljesítménytesztnek tartják. Az eredeti MapReduce-cikk1 a WordCountot használta teljesítménytesztként.

Az alábbi táblázatban két, a „This is a cloud computing course” és a „This is learning path 4 in the cloud computing course” kifejezéseket egyaránt tartalmazó fájlban az egyes szavak előfordulási gyakorisága látható, ahogy azt a WordCount teljesítményteszt-program feltételezhetően előállította:

Word Gróf    Word Gróf    Word Gróf
This 2   computing 2   4 1
is 2   course 2   in 1
a 1   tanulás 1   The 1
cloud 2   ösvény 1      

A MapReduce-programokat Java programozási nyelvben lehet megírni. A WordCount-programok (illetve általában MapReduce-programok) írását általában a leképezési és a csökkentési függvények bemeneti és kimeneti formátumainak definiálásával kezdjük. Mivel kulcs-érték párokkal foglalkozunk, csak azokat a tényleges kulcsokat és értékeket kell megadnunk, amelyeket a bemeneti és kimeneti fájlok tartalmazni fognak, valamint ezek típusait (például sztring vagy numerikus). A feladat bemenetének (a bemeneti adathalmaznak) egy vagy egymillió fájl esetében is mindig azonos formátumúnak kell lennie. Hasonlóképpen, a feladat kimeneti fájljainak is mindig azonos formátumúnak kell lenniük, amely eltérhet a bemeneti formátumtól.

A bemeneti és kimeneti formátumok tetszőleges soralapú naplók, képek, videók, többsoros rekordok vagy egészen mások is lehetnek. A Hadoop bármilyen fájlformátumot fel tud dolgozni, a sima szöveges formátumtól kezdve, a binárison keresztül, a strukturált adatbázisokig. Ennek rendezéséhez a felhasználók felülbírálhatják a Hadoop InputFormat (alapértelmezés szerint TextInputFormat) és OutputFormat (alapértelmezés szerint TextOutputFormat) osztályait. Az előbbi azt határozza meg, hogy a bemeneti fájlok hogyan lesznek beolvasva kulcs-érték párokként, és létrehozza a leképezési feladatoknak megfelelő bemeneti felosztási egységeket. A Hadoop-motor az így létrehozott bemeneti felosztási egységenként egy-egy leképezési feladatot állít elő. Az OutputFormat osztály hasonlóképpen azt határozza meg, hogyan írják a csökkentési feladatok a kulcs-érték párokat kimeneti fájlokként a HDFS-re. A következő videó részletesen bemutatja, hogy a Hadoop MapReduce hogyan használja az InputFormat és az OutputFormat osztályokat.

Az alapértelmezett I/O-alosztályok alkalmasak a szövegfeldolgozásra. A TextInputFormat lehetővé teszi a szövegfájlok olvasását, ahol a sorok bájtban megadott eltolása a kulcs, a sor tényleges tartalma pedig az érték. A TextOutputFormat lehetővé teszi a fájlok kulcs-érték3 szövegpárokként való írását. A Hadoop tartalmaz további formázási osztályokat (például SequenceFileInputFormat és SequenceFileOutputFormat) is, amelyek lehetővé teszik a bináris fájlok olvasását és írását. Emellett a Hadoop-felhasználók implementálhatnak a bemeneti adathalmazaikra szabott egyéni bemeneti és kimeneti formázási osztályokat is. A teendőkről további információt White "Hadoop: The Definitive Guide" (Hadoop: The Definitive Guide) című témakörében talál.3

Az itt látható WordCount-példa azt feltételezi, hogy a bemenetek szöveges fájlok. Így közvetlenül használhatjuk a TextInputFormat osztályt, ahol a kulcs a sorok bájtban megadott eltolása, az érték pedig maga a sor tartalma. Továbbá közvetlenül használhatjuk a TextOutputFormat osztályt is, ahol a kulcs a bemeneti adathalmazban előforduló szó, az érték pedig a szó gyakorisága. A kulcs típusa beállítható Java Long formátumra (LongWritable a Hadoopban), az érték típusa szintén beállítható Java String formátumra (Text a Hadoopban). A csökkentési függvénynek a leképezési feladatokból származó szavakat kell megkapnia kulcsként, illetve szavanként az 1 számot értékekként,4 így a kulcs típusa a szavak típusa (Text), az érték típusa pedig az egységet jelölő szám típusa (Java Integer, IntWritable a Hadoopban) lesz. Ezek után már csak a leképezési és a csökkentési függvények logikáját kell megérteni. A leképezési függvény esetében a bemeneti felosztási egységeket elemezni kell, amelynek kimenetében minden szóhoz 1-es darabszám tartozik. A csökkentési függvény esetében minden egyes fogadott szó egyszerűen, változatlan formában jeleníthető meg a kimenetben, az adott szám esetében kapott 1-esek összegzésével számított gyakorisággal együtt.5

Az alábbi kódrészlet bemutatja a Hadoop 0.20.0 új Java MapReduce API-jának teljes WordCount-példakódját.

import java.io.IOException;
import java.util.*;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.conf.*;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;

public class WordCount {

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

		public void map(LongWritable key, Text value, Context context) throws
		IOException, InterruptedException {
			String line = value.toString();
			StringTokenizer tokenizer = new StringTokenizer(line);

			while (tokenizer.hasMoreTokens()) {
				word.set(tokenizer.nextToken());
				context.write(word, one);
			}
		}
	}

	public static class WCReduce extends Reducer<Text, IntWritable, Text,
	IntWritable> {

		public void reduce(Text key, Iterable<IntWritable> values, Context
		context)
				throws IOException, InterruptedException {
			int sum = 0;
			for (IntWritable val : values) {
				sum += val.get();
			}
			context.write(key, new IntWritable(sum));
		}
	}

	public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();

		Job job = new Job(conf, "wordcount");

		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);

		job.setMapperClass(WCMap.class);
		job.setReducerClass(WCReduce.class);

		job.setInputFormatClass(TextInputFormat.class);
		job.setOutputFormatClass(TextOutputFormat.class);

		FileInputFormat.addInputPath(job, new Path(args[1]));
		FileOutputFormat.setOutputPath(job, new Path(args[2]));

		job.waitForCompletion(true);
	}

}

A kódrészlet alapján látható, hogy a programozóknak csupán két szekvenciális függvényt kell létrehozniuk (a leképezési és a csökkentési függvényt), amelyek két (belső) osztályban találhatók: ez esetben a WCMap és a WCReduce belső osztályokban. A WCMap belső osztály a Mapper osztály kiterjesztése, és felülbírálja annak map() függvényét. A Mapper osztály adott bemeneti kulcs-érték pártípusokat (LongWritable és Text) képez le a kimeneti kulcs-érték pártípusokra (Text és IntWritable). A Mapper osztályban meghatározott kimeneti kulcs-érték pártípusoknak mindig meg kell egyezniük a Reducer osztály bemeneti kulcs-érték pártípusaival. A WCReduce belső osztály a Reducer osztály kiterjesztése, és felülbírálja annak reduce() függvényét. A bemeneti kulcs-érték pártípusok meghatározásán kívül a Reducer osztály meghatározza a kimeneti kulcs-érték pártípusokat (Text és IntWritable) is, amelyet a csökkentési feladatok a végeredmény előállításához használnak.

A WCMap és a WCReduce belső osztályok map() és reduce() függvényei tartalmazzák a WordCount-program tényleges logikáját. A Context paraméter mindkét függvényben I/O-írásokat végez a helyi lemezekre és a HDFS-re. A main() függvény egy olyan feladatot állít be, amely végrehajtja a WordCount-programot egy bemeneti fájlkészleten az addInputPath() függvény használatával. Azt is megadja, hogy hová kerüljenek a kimeneti fájlok a HDFS-en a setOutputPath() függvény használatával. A main() függvényben setOutputKeyClass() és setOutputValueClass() adja meg azokat a kulcs-érték pártípusokat, amelyeket a csökkentési feladatok kimenetében megtalálhatók, és alapértelmezés szerint feltételezi, hogy ezek a típusok megegyeznek a leképezési feladat kimeneti kulcs-érték típusaival. Ellenkező esetben a main() függvénynek a setMapOutputKeyClass() és a setMapOutputValueClass() függvényeket is meg kell hívnia a leképezési feladat kimeneti kulcs-érték típusainak megadásához. A bemeneti és kimeneti formátumok beállításához a rendszer a setInputFormatClass() és a setOutputFormatClass() függvényeket hívja meg. Végül a setMapperClass() és setReducerClass() függvények használatával állíthatja be a feladat belső leképezési és csökkentési osztályait (WCMap és WCReduce). Az alábbi videó a Sortot ismerteti, amely egy másik klasszikus példa a MapReduce-ra.

A következő videó a Sobelt mutatja be, amely a képfeldolgozásra és az élészlelésre szolgál példaként.



3 Vegye figyelembe, hogy a kulcs és az érték között tabulátorhely (nem egyszerű szóköz) van.

4 A W szóhoz tartozó 1-es szám, mint kimenet azt jelzi, hogy W megjelenik egy bemeneti felosztási egységben. Ez lehetővé teszi a W szót fogadó csökkentési feladat számára, hogy egyszerűen növelje a W szóhoz tartozó számláló értékét.

5 Minden egyes csökkentési feladat több szó fogadására képes több leképezési feladatból, de az egyes szavak csak egy-egy csökkentési feladatnál jelennek meg.


Hivatkozások

  1. J. Dean and S. Ghemawat (Dec. 2004). MapReduce: Egyszerűsített adatfeldolgozás nagy fürtökön OSDI
  2. M. Zaharia, A. Konwinski, A. Joseph, R. Katz, and I. Stoica (2008). A MapReduce teljesítményének javítása heterogén környezetekben OSDI
  3. T. White (2011). Hadoop: The Definitive Guide 2nd Edition O'Reilly

Tesztelje tudását

1.

Melyik egyesítő használható a WordCount esetében, hogy megfelelő kimenetet kapjunk?