Wie ich bereits in meinem Blog Post "Apache Hadoop für Windows Azure – MapReduce mit C#" gezeigt hatte, lässt sich mit dem .Net Framework und dem Hadoop Streaming Feature einen MapReduce-Algorithmus mit Microsoft Technologien umsetzen.
Einen weitaus eleganteren Lösungsansatz, möchte ich in diesem Blog Post vorstellen …
Das .Net Hadoop MapReduce Job Submission Framework
Wenn man sich noch einmal den Quellcode meines WordCount-Beispiels aus dem "Apache Hadoop für Windows Azure – MapReduce mit C#" Blog Post ansieht, fällt auf, dass man sich immer wieder aufs Neue um grundlegende Dinge kümmern muss.
Das wären zum Beispiel das Lesen und Schreiben der Daten aus dem Input- bzw. in den Output-Stream, das Serialisieren bzw. Deserialisieren von Objekten, sowie Konvertieren von Datentypen.
Konsolenanwendung für den Mapper
using System; using System.Text.RegularExpressions; namespace WordCountMapper { class Program { static void Main(string[] args) { string line; var regex = new Regex("[a-zA-Z]+"); // Einlesen des Hadoop Datenstroms while ((line = Console.ReadLine()) != null) { foreach (Match match in regex.Matches(line)) { // Schreiben in den Hadoop Datenstrom Console.WriteLine("{0}t1", match.Value.ToLower()); } } } } }
Konsolenanwendung für den Reducer
using System; namespace WordCountReducer { class Program { static void Main(string[] args) { string line; string prevWord = null; int count = 0; // Einlesen des Hadoop Datenstroms while ((line = Console.ReadLine()) != null) { if (!line.Contains("t")) continue; var word = line.Split('t')[0]; var cnt = Convert.ToInt32(line.Split('t')[1]); if (prevWord != word) { if (prevWord != null) Console.WriteLine("{0}t{1}", prevWord, count); prevWord = word; count = cnt; } else count += cnt; } Console.WriteLine("{0}t{1}", prevWord, count); } } }
Dieser Problemstellung hat sich Carl Nolan vor einigen Monaten angenommen und ein Framework für die Erstellung und Übertragung von .Net basierten Hadoop MapReduce Jobs erstellt (Link zum Framework).
Das Framework liefert neben den Basisklassen für Mapper und Reducer auch eine Konsolen- und eine WinForms-Anwendung für die Job-Übertragung an das Hadoop Cluster.
Framework für .Net basierte Hadoop MapReduce Jobs
Um den WordCount-Algorithmus mit Carl Nolan’s Framework abzubilden, benötigt man eine einfache Klassenbibliothek, sowie eine Map- und eine Reduce-Klasse.
Die Map-Klasse
Für den Map-Teil des WordCount-Algorithmus, leite ich von der Basisklasse MapperBaseText<> ab und überschreibe die Map Methode.
public class WordCountMapper : MapperBaseText<int> { private static readonly Regex Regex = new Regex("[a-zA-Z]+"); public override IEnumerable<Tuple<string, int>> Map(string value) { return from Match match in Regex.Matches(value) select new Tuple<string, int>(match.Value.ToLower(), 1); } }
Hierbei kann man die Vorteile des Frameworks schon gut erkennen:
- Es gibt verschiedene Mapper-Basisklassen, die die Art des Input-Streams definieren, wie z.B. MapperText, MapperBinary oder MapperXml
- Der Datentyp des "Values" der Key-/Value-Paare, wird bereits durch die generische Basisklasse festgelegt
- Es gibt 3 überschreibbare Methoden (Setup, Map und Cleanup) um den Map-Teil des Algorithmus zu implementieren
Die Reduce-Klasse
Der Reduce-Teil des WordCount-Algorithmus, wird auf die gleiche Weise wie der Map-Teil implementiert:
public class WordCountReducer : ReducerBase<int, int> { public override IEnumerable<Tuple<string, int>> Reduce(string key, IEnumerable<int> value) { yield return new Tuple<string, int>(key, value.Sum()); } }
Besonders hervorheben, möchte ich die typsichere Verwendung der Input- und Output-Streams durch die generische Reducer-Basisklasse.
Ausführen des MapReduce-Algorithmus
Um den in .Net entwickelten MapReduce-Algorithmus, noch einfacher als gehabt, ausführen zu können, wurde mit dem Framework ein Kommandozeilentool, sowie eine WinForms-Anwendung, für die Job-Übertragung an das Hadoop Cluster mitgeliefert.
Das Kommandozeilentool benötigt mindestens die Angabe des Input- und Output-Streams, die Klassen für Mapper und Reducer, sowie Pfad zur Klassenbibliothek.
Weitere Optionale Parameter, wie z.B. das gewünschte Ausgabeformat, werden außerdem unterstützt:
MSDN.Hadoop.Submission.Console.exe
Command Arguments: -help (Required=false) : Display Help Text -input (Required=true) : Input Directory or Files -output (Required=true) : Output Directory -mapper (Required=true) : Mapper Class -reducer (Required=false) : Reducer Class -combiner (Required=false) : Combiner Class (Optional) -format (Required=false) : Input Format |Text(Default)|Binary|Xml| -numberReducers (Required=false) : Number of Reduce Tasks (Optional) -numberKeys (Required=false) : Number of MapReduce Keys (Optional) -outputFormat (Required=false) : MapReduce Output Format |Text(Json)|Binary| (Optional) -file (Required=true) : Processing Files (Must include Map and Reduce Class files) -nodename (Required=false) : XML Processing Nodename (Optional) -cmdenv (Required=false) : List of Environment Variables for Job (Optional) -jobconf (Required=false) : List of Job Configuration Parameters (Optional) -partitionerOption (Required=false) : Specify Partitioner specification (Optional) -comparatorOption (Required=false) : Specify Comparator specification (Optional) -partitioner (Required=false) : Specify a Java class for the Partitioner (Optional) -debug (Required=false) : Turns on Debugging Options (Optional)
Bei meinem WordCount-Beispiel könnte der Aufruf wie folgt aussehen:
"C:NetJobSubmissionMSDN.Hadoop.Submission.Console.exe" -input "/user/Sascha/input/texte" -output "/user/Sascha/output/Woerter" -mapper "WordCount.WordCountMapper, WordCount" -reducer "WordCount.WordCountReducer, WordCount" -file "C:NetJobSubmissionWordCount.dll"
Das Ergebnis
Das Ergebnis kann, wie gewohnt, mit fs.read(…) in der interaktiven JavaScript-Konsole angezeigt werden:
js> fs.read("Woerter") ab 1 abendrot 1 aber 1 abgesponnen 1 ach 2 akkorden 1 alle 4 allein 1 allen 1 allenfalls 1 aller 1 allerliebste 1 allerschlimmste 1 alles 1 allgemeinen 1 als 2 alte 1 alten 1 alter 1 am 3 an 10 anblick 1 andern 1 angefochten 1 ...
Download der Beispielanwendung:
Weitere Informationen |