MapReduceを適用する多くの実際の状況では、最終的なアルゴリズムはいくつかのMapReduceステップになります。
すなわち、Map1、Reduce1、Map2、Reduce2など。
したがって、次のマップの入力として必要な最後のリデュースからの出力があります。
パイプラインが正常に完了すると、中間データは(一般的に)保持したくないものです。また、この中間データは一般に何らかのデータ構造(「マップ」や「セット」など)であるため、これらのキーと値のペアの書き込みと読み取りにあまり労力をかけたくありません。
Hadoopで推奨される方法は何ですか?
後のクリーンアップを含め、この中間データを正しい方法で処理する方法を示す(単純な)例はありますか?
Yahooの開発者ネットワークに関するこのチュートリアルは、これに役立つと思います。 Chaining Jobs
JobClient.runJob()
を使用します。最初のジョブからのデータの出力パスは、2番目のジョブへの入力パスになります。これらは、適切なコードを使用してジョブに引数として渡して、それらを解析し、ジョブのパラメーターを設定する必要があります。
しかし、上記の方法は、現在の古いmapred APIが行った方法かもしれないと思いますが、それでも動作するはずです。新しいmapreduce APIにも同様のメソッドがありますが、それが何であるかはわかりません。
ジョブの終了後に中間データを削除する限り、コードでこれを実行できます。私が以前にやった方法は、次のようなものを使用しています:
FileSystem.delete(Path f, boolean recursive);
パスは、データのHDFS上の場所です。他のジョブで必要とされない場合にのみ、このデータを削除するようにしてください。
できる方法はたくさんあります。
(1)カスケードジョブ
最初のジョブのJobConfオブジェクト「job1」を作成し、「input」を入力ディレクトリ、「temp」を出力ディレクトリとしてすべてのパラメータを設定します。このジョブを実行します:
JobClient.run(job1).
そのすぐ下で、2番目のジョブのJobConfオブジェクト「job2」を作成し、「temp」を入力ディレクトリ、「output」を出力ディレクトリとしてすべてのパラメータを設定します。このジョブを実行します:
JobClient.run(job2).
(2) 2つのJobConfオブジェクトを作成し、それらのすべてのパラメーターを(1)のように設定します= JobClient.runを使用しないことを除きます。
次に、パラメーターとしてjobconfsを使用して2つのJobオブジェクトを作成します。
Job job1=new Job(jobconf1);
Job job2=new Job(jobconf2);
JobControlオブジェクトを使用して、ジョブの依存関係を指定してからジョブを実行します。
JobControl jbcntrl=new JobControl("jbcntrl");
jbcntrl.addJob(job1);
jbcntrl.addJob(job2);
job2.addDependingJob(job1);
jbcntrl.run();
(3) Map +のような構造が必要な場合|削減| Map *では、Hadoopバージョン0.19以降に付属のChainMapperクラスとChainReducerクラスを使用できます。
乾杯
実際にこれを行うには多くの方法があります。 2つに焦点を当てます。
1つは、Riffle( http://github.com/cwensel/riffle )を介して、依存物を識別し、それらを依存(トポロジー)順に「実行」するための注釈ライブラリです。
または、カスケードでカスケード(およびMapReduceFlow)を使用できます( http://www.cascading.org/ )。将来のバージョンでは、Riffle注釈がサポートされる予定ですが、未加工のMR JobConfジョブで現在はうまく機能します。
これのバリエーションは、MRジョブを手動で管理するのではなく、Cascading APIを使用してアプリケーションを開発することです。次に、JobConfとジョブチェーンは、Cascading plannerおよびFlowクラスを介して内部的に処理されます。
このようにして、Hadoopジョブなどの管理のメカニズムではなく、問題に集中して時間を費やします。開発やアプリケーションをさらに簡素化するために、異なる言語(clojureやjrubyなど)を重ねることもできます。 http://www.cascading.org/modules.html
JobConfオブジェクトを使用して、次々にジョブチェーンを実行しました。ジョブを連鎖させるためにWordCountの例を取り上げました。 1つのジョブは、指定された出力でWordが何回繰り返されたかを把握します。 2番目のジョブは、最初のジョブ出力を入力として受け取り、指定された入力の合計単語数を計算します。以下は、Driverクラスに配置する必要があるコードです。
//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);
これらのジョブを実行するコマンドは次のとおりです。
bin/hadoop jar TotalWords。
コマンドの最終的なジョブ名を指定する必要があります。上記の場合、TotalWordsです。
コードで指定された方法でMRチェーンを実行できます。
注意してください:ドライバーコードのみが提供されています
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);
}
}
シーケンスは
(JOB1)MAP-> REDUCE->(-JOB2)MAP
これはキーをソートするために行われましたが、ツリーマップを使用するなどの方法があります
それでも、ジョブの連鎖方法に注意を向けたいと思います!!
ありがとうございました
MapReduceジョブのバーチ処理にoozieを使用できます。 http://issues.Apache.org/jira/browse/HADOOP-53
JobのwaitForCompletion(true)
メソッドを使用して、ジョブ間の依存関係を定義できます。
私のシナリオでは、互いに依存する3つのジョブがありました。ドライバークラスでは、以下のコードを使用しましたが、期待どおりに機能します。
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=================");
}
}
}
}
Apache Mahoutプロジェクトには、複数のMapReduceジョブを連結する例があります。例の1つは、次の場所にあります。
RecommendederJob.Java
新しいクラスorg.Apache.hadoop.mapreduce.lib.chain.ChainMapperはこのシナリオを支援します
Oozieは、後続のジョブが前のジョブから直接入力を受け取るのに役立つと思います。これにより、ジョブ制御で実行されるI/O操作が回避されます。
プログラムでジョブをチェーン化する場合は、JobControlを使用します。使い方はとても簡単です:
JobControl jobControl = new JobControl(name);
その後、ControlledJobインスタンスを追加します。 ControlledJobは、依存関係を持つジョブを定義するため、入力と出力を自動的に接続して、ジョブの「チェーン」に合わせます。
jobControl.add(new ControlledJob(job, Arrays.asList(controlledjob1, controlledjob2));
jobControl.run();
チェーンを開始します。それを特定のスレッドに入れたいと思うでしょう。これにより、実行中にチェーンのステータスを確認できます。
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());
}
複雑なサーバーベースのHadoopワークフローエンジン(例:oozie)がありますが、ワークフローとして複数のHadoopジョブの実行を可能にするシンプルなJavaライブラリがあります。 JSONファイル内:すべてが外部で構成可能であり、既存のマップの変更を必要としないため、ワークフローの一部として実装を削減できます。
詳細はこちらをご覧ください。ソースコードとjarはgithubで入手できます。
http://pkghosh.wordpress.com/2011/05/22/hadoop-orchestration/
プラナブ
要件で、MRJob1のo/pをMRJob2のi/pなどにすることを述べたように、このユースケースでoozieワークフローを使用することを検討できます。また、次のMRJobで使用されるため、中間データをHDFSに書き込むことを検討することもできます。また、ジョブの完了後、中間データをクリーンアップできます。
<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"/>