it-swarm.com.de

Verketten mehrerer MapReduce-Jobs in Hadoop

In vielen Situationen, in denen Sie MapReduce anwenden, sind die letzten Algorithmen mehrere MapReduce-Schritte.

also Map1, Reduce1, Map2, Reduce2 und so weiter.

Sie haben also die Ausgabe der letzten Reduzierung, die als Eingabe für die nächste Karte benötigt wird. 

Die Zwischendaten sind etwas, das Sie (im Allgemeinen) nicht behalten möchten, wenn die Pipeline erfolgreich abgeschlossen wurde. Da diese Zwischendaten im Allgemeinen eine Datenstruktur sind (z. B. eine "Karte" oder eine "Menge"), möchten Sie sich beim Schreiben und Lesen dieser Schlüssel-Wert-Paare nicht allzu viel Mühe geben.

Was ist der empfohlene Weg, dies in Hadoop zu tun?

Gibt es ein (einfaches) Beispiel, das zeigt, wie diese Zwischendaten korrekt behandelt werden, einschließlich der anschließenden Bereinigung?

117
Niels Basjes

Ich denke, dieses Tutorial im Yahoo-Entwicklernetzwerk wird Ihnen dabei helfen: Verkettung von Jobs

Sie verwenden die JobClient.runJob(). Der Ausgabepfad der Daten vom ersten Job wird zum Eingabepfad zu Ihrem zweiten Job. Diese müssen als Argumente mit einem geeigneten Code an Ihre Jobs übergeben werden, um sie zu parsen und die Parameter für den Job festzulegen.

Ich denke, dass die obige Methode möglicherweise so ist, wie es die jetzt ältere Mapred-API getan hat, aber es sollte trotzdem funktionieren. Es wird eine ähnliche Methode in der neuen Mapreduce-API geben, aber ich bin nicht sicher, was es ist.

Wenn Sie nach Abschluss eines Jobs Zwischendaten entfernen, können Sie dies in Ihrem Code tun. Die Art und Weise, wie ich es vorher gemacht habe, ist etwas wie:

FileSystem.delete(Path f, boolean recursive);

Wo ist der Pfad der Ort der Daten auf HDFS. Sie müssen sicherstellen, dass Sie diese Daten nur löschen, wenn dies für keinen anderen Job erforderlich ist.

53
Binary Nerd

Es gibt viele Möglichkeiten, dies zu tun.

(1) Kaskadierung von Jobs

Legen Sie das JobConf-Objekt "job1" für den ersten Job an und setzen Sie alle Parameter mit "input" als Eingabeverzeichnis und "temp" als Ausgabeverzeichnis. Führen Sie diesen Job aus:

JobClient.run(job1).

Direkt darunter legen Sie das JobConf-Objekt "job2" für den zweiten Job an und setzen alle Parameter mit "temp" als Eingabeverzeichnis und "output" als Ausgabeverzeichnis. Führen Sie diesen Job aus: 

JobClient.run(job2).

(2) Erstellen Sie zwei JobConf-Objekte, und legen Sie alle Parameter wie(1)fest, außer dass Sie JobClient.run nicht verwenden.

Dann erstellen Sie zwei Job-Objekte mit jobconfs als Parametern:

Job job1=new Job(jobconf1); 
Job job2=new Job(jobconf2);

Mit dem jobControl-Objekt geben Sie die Jobabhängigkeiten an und führen Sie die Jobs aus:

JobControl jbcntrl=new JobControl("jbcntrl");
jbcntrl.addJob(job1);
jbcntrl.addJob(job2);
job2.addDependingJob(job1);
jbcntrl.run();

(3) Wenn Sie eine Struktur wie Map + | benötigen Reduzieren | Map * können Sie die ChainMapper- und ChainReducer-Klassen verwenden, die mit Hadoop Version 0.19 und höher geliefert werden.

Prost

17
user381928

Dafür gibt es mehrere Möglichkeiten. Ich werde mich auf zwei konzentrieren.

Eine ist über Riffle ( http://github.com/cwensel/riffle ) eine Anmerkungsbibliothek, um abhängige Dinge zu identifizieren und in abhängiger (topologischer) Reihenfolge "auszuführen".

Oder Sie können Cascade (und MapReduceFlow) in Cascading ( http://www.cascading.org/ ) verwenden. Eine zukünftige Version wird Riffle-Anmerkungen unterstützen, funktioniert aber jetzt hervorragend mit MR-JobConf-Jobs.

Eine Variante dazu besteht darin, MR-Jobs überhaupt nicht manuell zu verwalten, sondern Ihre Anwendung mithilfe der Cascading-API zu entwickeln. Die JobConf- und Job-Verkettung wird dann intern über die Cascading Planer- und Flow-Klassen abgewickelt.

Auf diese Weise verbringen Sie Ihre Zeit damit, sich auf Ihr Problem zu konzentrieren, nicht auf die Mechanismen der Verwaltung von Hadoop-Jobs usw. Sie können sogar verschiedene Sprachen (wie clojure oder jruby) darüber legen, um Ihre Entwicklung und Anwendungen noch weiter zu vereinfachen. http://www.cascading.org/modules.html

7
cwensel

Ich habe eine Job-Verkettung mit JobConf-Objekten nacheinander durchgeführt. Ich habe WordCount als Beispiel genommen, um die Jobs zu verketten. Ein Job ermittelt, wie oft ein Wort in der angegebenen Ausgabe wiederholt wird. Der zweite Job nimmt die erste Jobausgabe als Eingabe und ermittelt die Gesamtwörter in der gegebenen Eingabe. Unten ist der Code, der in die Driver-Klasse eingefügt werden muss.

    //First Job - Counts, how many times a Word encountered in a given file 
    JobConf job1 = new JobConf(WordCount.class);
    job1.setJobName("WordCount");

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

    job1.setMapperClass(WordCountMapper.class);
    job1.setCombinerClass(WordCountReducer.class);
    job1.setReducerClass(WordCountReducer.class);

    job1.setInputFormat(TextInputFormat.class);
    job1.setOutputFormat(TextOutputFormat.class);

    //Ensure that a folder with the "input_data" exists on HDFS and contains the input files
    FileInputFormat.setInputPaths(job1, new Path("input_data"));

    //"first_job_output" contains data that how many times a Word occurred in the given file
    //This will be the input to the second job. For second job, input data name should be
    //"first_job_output". 
    FileOutputFormat.setOutputPath(job1, new Path("first_job_output"));

    JobClient.runJob(job1);


    //Second Job - Counts total number of words in a given file

    JobConf job2 = new JobConf(TotalWords.class);
    job2.setJobName("TotalWords");

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

    job2.setMapperClass(TotalWordsMapper.class);
    job2.setCombinerClass(TotalWordsReducer.class);
    job2.setReducerClass(TotalWordsReducer.class);

    job2.setInputFormat(TextInputFormat.class);
    job2.setOutputFormat(TextOutputFormat.class);

    //Path name for this job should match first job's output path name
    FileInputFormat.setInputPaths(job2, new Path("first_job_output"));

    //This will contain the final output. If you want to send this jobs output
    //as input to third job, then third jobs input path name should be "second_job_output"
    //In this way, jobs can be chained, sending output one to other as input and get the
    //final output
    FileOutputFormat.setOutputPath(job2, new Path("second_job_output"));

    JobClient.runJob(job2);

Der Befehl zum Ausführen dieser Jobs lautet:

bin/hadoop Dose TotalWords.

Wir müssen den endgültigen Jobnamen für den Befehl angeben. Im obigen Fall handelt es sich um TotalWords.

6
psrklr

Sie können die MR-Kette auf die im Code angegebene Weise ausführen.

BITTE BEACHTEN : Nur der Treibercode wurde bereitgestellt

public class WordCountSorting {
// here the Word keys shall be sorted
      //let us write the wordcount logic first

      public static void main(String[] args)throws IOException,InterruptedException,ClassNotFoundException {
            //THE DRIVER CODE FOR MR CHAIN
            Configuration conf1=new Configuration();
            Job j1=Job.getInstance(conf1);
            j1.setJarByClass(WordCountSorting.class);
            j1.setMapperClass(MyMapper.class);
            j1.setReducerClass(MyReducer.class);

            j1.setMapOutputKeyClass(Text.class);
            j1.setMapOutputValueClass(IntWritable.class);
            j1.setOutputKeyClass(LongWritable.class);
            j1.setOutputValueClass(Text.class);
            Path outputPath=new Path("FirstMapper");
            FileInputFormat.addInputPath(j1,new Path(args[0]));
                  FileOutputFormat.setOutputPath(j1,outputPath);
                  outputPath.getFileSystem(conf1).delete(outputPath);
            j1.waitForCompletion(true);
                  Configuration conf2=new Configuration();
                  Job j2=Job.getInstance(conf2);
                  j2.setJarByClass(WordCountSorting.class);
                  j2.setMapperClass(MyMapper2.class);
                  j2.setNumReduceTasks(0);
                  j2.setOutputKeyClass(Text.class);
                  j2.setOutputValueClass(IntWritable.class);
                  Path outputPath1=new Path(args[1]);
                  FileInputFormat.addInputPath(j2, outputPath);
                  FileOutputFormat.setOutputPath(j2, outputPath1);
                  outputPath1.getFileSystem(conf2).delete(outputPath1, true);
                  System.exit(j2.waitForCompletion(true)?0:1);
      }

}

DIE SEQUENZ IST

( JOB1 ) MAP-> REDUCE-> ( JOB2 ) MAP
Dies wurde getan, um die Schlüssel zu sortieren. Es gibt jedoch noch mehr Möglichkeiten, beispielsweise eine Baumkarte zu verwenden
Dennoch möchte ich Ihre Aufmerksamkeit darauf richten, wie die Jobs verkettet wurden !!
Vielen Dank

5
Aniruddha Sinha

Sie können oozie für die Barchverarbeitung Ihrer MapReduce-Jobs verwenden. http://issues.Apache.org/jira/browse/HADOOP-5303

4
user300313

Wir können die waitForCompletion(true)-Methode des Jobs verwenden, um die Abhängigkeit zwischen dem Job zu definieren.

In meinem Szenario hatte ich 3 Jobs, die voneinander abhängig waren. In der Treiberklasse habe ich den folgenden Code verwendet und es funktioniert wie erwartet.

public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub

        CCJobExecution ccJobExecution = new CCJobExecution();

        Job distanceTimeFraudJob = ccJobExecution.configureDistanceTimeFraud(new Configuration(),args[0], args[1]);
        Job spendingFraudJob = ccJobExecution.configureSpendingFraud(new Configuration(),args[0], args[1]);
        Job locationFraudJob = ccJobExecution.configureLocationFraud(new Configuration(),args[0], args[1]);

        System.out.println("****************Started Executing distanceTimeFraudJob ================");
        distanceTimeFraudJob.submit();
        if(distanceTimeFraudJob.waitForCompletion(true))
        {
            System.out.println("=================Completed DistanceTimeFraudJob================= ");
            System.out.println("=================Started Executing spendingFraudJob ================");
            spendingFraudJob.submit();
            if(spendingFraudJob.waitForCompletion(true))
            {
                System.out.println("=================Completed spendingFraudJob================= ");
                System.out.println("=================Started locationFraudJob================= ");
                locationFraudJob.submit();
                if(locationFraudJob.waitForCompletion(true))
                {
                    System.out.println("=================Completed locationFraudJob=================");
                }
            }
        }
    }
3
Shivaprasad

Im Apache Mahout-Projekt gibt es Beispiele, die mehrere MapReduce-Jobs zusammenfassen. Eines der Beispiele finden Sie unter:

RecommenderJob.Java

http://search-lucene.com/c/Mahout:/core/src/main/Java/org/Apache/mahout/cf/taste/hadoop/item/RecommenderJob.Java%7C%7CRecommenderJob

3

Die neue Klasse org.Apache.hadoop.mapreduce.lib.chain.ChainMapper hilft dieses Szenario

2
Xavi

Ich denke, dass Oozie den Folgeaufträgen dabei hilft, die Eingaben direkt vom vorherigen Auftrag zu erhalten. Dies vermeidet den E/A-Vorgang, der mit der Jobsteuerung ausgeführt wird. 

1
stholy

Wenn Sie Ihre Jobs programmatisch verketten möchten, müssen Sie JobControl verwenden. Die Verwendung ist recht einfach:

    JobControl jobControl = new JobControl(name);

Danach fügen Sie ControlledJob-Instanzen hinzu. ControlledJob definiert einen Job mit seinen Abhängigkeiten, sodass Ein- und Ausgänge automatisch an eine "Kette" von Jobs angepasst werden.

    jobControl.add(new ControlledJob(job, Arrays.asList(controlledjob1, controlledjob2));

    jobControl.run();

beginnt die Kette. Sie möchten das in einen eigenen Thread einfügen. Damit können Sie den Status Ihrer Kette überprüfen, während sie läuft:

    while (!jobControl.allFinished()) {
        System.out.println("Jobs in waiting state: " + jobControl.getWaitingJobList().size());
        System.out.println("Jobs in ready state: " + jobControl.getReadyJobsList().size());
        System.out.println("Jobs in running state: " + jobControl.getRunningJobList().size());
        List<ControlledJob> successfulJobList = jobControl.getSuccessfulJobList();
        System.out.println("Jobs in success state: " + successfulJobList.size());
        List<ControlledJob> failedJobList = jobControl.getFailedJobList();
        System.out.println("Jobs in failed state: " + failedJobList.size());
    }
1

Obwohl es komplexe Server-basierte Hadoop-Workflow-Engines gibt, z. B. Oozie, habe ich eine einfache Java-Bibliothek, die die Ausführung mehrerer Hadoop-Jobs als Workflow ermöglicht. Die Jobkonfiguration und der Workflow, die die Abhängigkeit zwischen Jobs definieren, werden in einer JSON-Datei konfiguriert. Alles ist extern konfigurierbar und erfordert keine Änderung der vorhandenen Map, um die Implementierung als Teil eines Workflows zu reduzieren.

Details finden Sie hier. Quellcode und jar sind in github verfügbar.

http://pkghosh.wordpress.com/2011/05/22/hadoop-orchestration/

Pranab

1
Pranab

Wie Sie in Ihrer Anforderung erwähnt haben, dass Sie möchten, dass O/P von MRJob1 der I/P von MRJob2 ist, usw., können Sie den Oozie-Workflow für diese Verwendung verwenden. Sie können auch in Erwägung ziehen, Ihre Zwischendaten in HDFS zu schreiben, da diese vom nächsten MRJob verwendet werden. Nachdem der Job abgeschlossen ist, können Sie Ihre Zwischendaten bereinigen. 

<start to="mr-action1"/>
<action name="mr-action1">
   <!-- action for MRJob1-->
   <!-- set output path = /tmp/intermediate/mr1-->
    <ok to="end"/>
    <error to="end"/>
</action>

<action name="mr-action2">
   <!-- action for MRJob2-->
   <!-- set input path = /tmp/intermediate/mr1-->
    <ok to="end"/>
    <error to="end"/>
</action>

<action name="success">
        <!-- action for success-->
    <ok to="end"/>
    <error to="end"/>
</action>

<action name="fail">
        <!-- action for fail-->
    <ok to="end"/>
    <error to="end"/>
</action>

<end name="end"/>

0
Neha Kumari