Microsoft HDInsight und ein Framework für .Net MapReduce-Algorithmen

Azure Hadoop LogoWie 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 Download der Beispielanwendung:

 

Weitere Informationen


Check Also

Time Machine Backups nach Microsoft Azure

Seit einigen Jahren verwende ich eine Apple Time Capsule, um meine Time Machine Backups an einem zentralen Ort speichern zu können. Bislang hatte das für mich auch vollkommen ausgereicht. Seitdem ich jedoch immer mehr unterwegs bin, habe ich nach einer Lösung gesucht, die ich auch von unterwegs nutzen kann. In diesem Blog Post zeige ich deshalb, wie man Time Machine Backups nach Microsoft Azure machen kann.