Hadoop公式ページの「Map/Reduce チュートリアル」のWordCountを単独環境で動かしてみる。
|
|
チュートリアルのページではjavacコマンドを使ってコンパイルしているが、やはりコーディングにはEclipseを使いたい。
Hadoopのjarファイルをビルドパスに追加するだけでよい。
jarファイル | 備考 | |
---|---|---|
Hadoop | C:\cygwin\usr\local\hadoop-0.20.1\hadoop-0.20.1-core.jar | ↓ソースの場所(Eclipseで添付可能) C:/cygwin/usr/local/hadoop-0.20.1/src |
チュートリアルのソースはHadoop0.20.1より前のバージョンのものらしく、0.20.1だとコンパイルが警告になる(警告になるだけで、実行は出来る)。
なので、(正しいかどうか分からないけど^^;)0.20.1用に直してみた。
// http://oss.infoscience.co.jp/hadoop/common/docs/current/mapred_tutorial.html 2010-02-21 package jp.hishidama.hadoop.tutorial; import java.io.IOException; import java.util.StringTokenizer; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.Reducer; 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 Map extends Mapper<LongWritable, Text, Text, IntWritable> { private final static IntWritable one = new IntWritable(1); private Text word = new Text(); @Override protected 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 Reduce extends Reducer<Text, IntWritable, Text, IntWritable> { @Override protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { int sum = 0; for (IntWritable value : values) { sum += value.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.setJarByClass(WordCount.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); job.setMapperClass(Map.class); job.setCombinerClass(Reduce.class); job.setReducerClass(Reduce.class); job.setInputFormatClass(TextInputFormat.class); job.setOutputFormatClass(TextOutputFormat.class); FileInputFormat .setInputPaths(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); if (true) { // 実行を開始して完了するまで待つ boolean success = job.waitForCompletion(true); System.out.println(success); } else { // 実行を開始して(完了を待たずに)すぐ戻ってくる job.submit(); // while (!job.isComplete()); // boolean success = job.isSuccessful(); // System.out.println(success); } } }
変更点は以下の通り。
変更箇所 | 変更内容 |
---|---|
Mapクラス | 元は「extends MapReduceBase implements
Mapper」だった。 しかしMapReduceBaseが非推奨になっていたので、 mapreduceパッケージのMapperクラスを継承するよう変更。 (mapredパッケージのMapperインターフェースとは別もの) |
map()メソッド | Mapクラスの継承元が変わったので、オーバーライドするメソッドも変わった。 特に第3引数以降がcontextに変更になっている。 したがって「output.collect(ket, value)」→「context.write(key, value)」になった。 |
元は「extends MapReduceBase implements Reducer」だった。 しかしMapReduceBaseが非推奨になっていたので、 mapreduceパッケージのReducerクラスを継承するよう変更。 (mapredパッケージのReducerインターフェースとは別もの) |
|
reduce()メソッド | Reduceクラスの継承元が変わったので、オーバーライドするメソッドも変わった。 第2引数がIteratorからIterableに変わった。 したがってループ方法はiteratorでなくfor each構文が使える。 また、第3引数以降がcontextになっている。 したがって「output.collect(ket, value)」→「context.write(key, value)」になった。 |
Configuration Job |
コンフィグは、元はJobConfだったが非推奨になっていた。 そこでConfigurationクラスとJobクラスに変更。 Configurationには設定ファイル(xmlファイル)をaddResouce()で指定できるようだ。 Jobのコンストラクト方法はJobConfとちょっと違う。 JobConfに値をセットしていたメソッドはJobでほぼそのまま使える。 ただ、setInputFormat()・setOutputFormat()は setInputFormatClass()・setOutputFormatClass()になっている模様。 |
FileInputFormat FileOutputFormat |
FileInputFormat.setInputPaths()・FileOutputFormat.setOutputPath()は クラス名・メソッド名は変わっていない。 が、第1引数がJobConfからJobになったので、そのままでは使えない。 mapredパッケージのFileInputFormat・FileOutputFormatでなく、 mapreduce.input.FileInputFormat・mapreduce.output.FileOutputFormatを使う。 |
実行方法 | JobClient.runJob()でなく、Jobを使った実行方法に変更。 |
Hadoop用のプログラムを実行するには(Cygwinから)hadoopコマンドで実行する必要があるので、Eclipseから直接実行することは出来ない。
Eclipseを使っていればコンパイルは自動的に行われるので問題ないが、
hadoopコマンドで実行するにはjarファイルでないといけないので、jarファイル化する必要がある。
何度も作り直すことを考えるならbuild.xml化した方が断然楽だが、Eclipseのエクスポート機能を使ってjarファイルを作ることも出来る。
とりあえずチュートリアルを実行する為に「C:\cygwin\home\hadoop\tutorial\」というディレクトリーを作り、
そこに「wordcount.jar」を置くことにする。
$ mkdir -p /home/hadoop/tutorial
(Cygwinのbashから)HADOOP_HOME/bin/hadoopコマンドを使って実行する。
cd /home/hadoop/tutorial mkdir input echo "Hello World Bye World" > input/file01 echo "Hello Hadoop Goodbye Hadoop" > input/file02
再実行する際には出力先のoutputディレクトリーが存在しているとエラーになるので、削除する。
(存在したまま実行すると、FileAlreadyExistsExceptionが発生する)
cd /home/hadoop/tutorial rm -r output
cd /home/hadoop/tutorial
/usr/local/hadoop/bin/hadoop jar wordcount.jar jp.hishidama.hadoop.tutorial.WordCount ./input ./output
この引数input・outputは、ディレクトリー名を相対パスで表している。input→args[0]、output→args[1]
Cygwinで実行すると以下のような警告が出る(ことがある)が、気にしなーい。
cygwin warning: MS-DOS style path detected: C:\cygwin\usr\local\hadoop-0.20.1\/build/native Preferred POSIX equivalent is: /usr/local/hadoop-0.20.1/build/native CYGWIN environment variable option "nodosfilewarning" turns off this warning. Consult the user's guide for more details about POSIX paths: http://cygwin.com/cygwin-ug-net/using.html#using-pathnames
$ cat output/* Bye 1 Goodbye 1 Hadoop 2 Hello 2 World 2
Eclipseでソースを直してjarファイルを作ってCygwinでoutputディレクトリーを消してから実行
って面倒なので、Antのbuild.xmlを書いてみた。
<?xml version="1.0" encoding="Windows-31J"?>
<project name="hadoop standalone" basedir="." default="mk_jar">
<property name="cygwin" location="C:\cygwin" />
<property name="hadoop.home" location="${cygwin}/usr/local/hadoop-0.20.1" />
<property name="target.home" location="${cygwin}/home/hadoop/tutorial" />
<property name="target.input" location="${target.home}/input" />
<property name="target.output" location="${target.home}/output" />
<property name="jar.file" location="${target.home}/wordcount.jar" />
<property name="main.class" value="jp.hishidama.hadoop.tutorial.WordCount" />
<target name="init" description="ディレクトリーや入力ファイルを準備する">
<mkdir dir="${target.input}" />
<echo file="${target.input}/file01">Hello World Bye World</echo>
<echo file="${target.input}/file02">Hello Hadoop Goodbye Hadoop</echo>
</target>
<target name="mk_jar" description="jarファイルを作成する">
<jar destfile="${jar.file}">
<fileset dir="../classes" />
</jar>
</target>
<target name="rm_output" description="outputディレクトリーを削除する">
<delete dir="${target.output}" />
</target>
<target name="execute_jar" description="hadoopを実行する">
<exec executable="${cygwin}/bin/bash.exe">
<arg value="--login" />
<arg value="/usr/local/hadoop/bin/hadoop" />
<arg value="jar" />
<arg value="${jar.file}" />
<arg value="${main.class}" />
<arg value="${target.input}" />
<arg value="${target.output}" />
</exec>
</target>
<target name="cat_output" description="outputの内容を表示する">
<exec executable="cmd" dir="${target.output}">
<arg value="/c" />
<arg value="type" />
<arg value="*." />
</exec>
</target>
<target name="execute_all" depends="rm_output,execute_jar,cat_output" description="実行して結果を表示する" />
<target name="all" depends="mk_jar,execute_all" description="jarファイルを作って実行する" />
</project>
このbuild.xmlは、Eclipseのプロジェクトにbinというディレクトリーを作り、そこに置く想定。
生成されるjarファイルやinput・outputは${target.home}で指定された場所に置かれる想定。
→EclipseからAntを実行する方法
execute_allターゲットでoutputの削除→hadoopの実行→結果の表示を行う。
allターゲットはjarファイルを作ってからexecute_allを行う。
hadoopコマンドはUNIX(Cygwin)上で動くからexecタスクの引数はUNIX形式のパス指定にしないといけないのかと思ったら、
逆にWindowsのパスでないと駄目なようだ。
(実行用のHADOOP_HOME/bin/hadoopシェル自体がCygwinに対応していて、クラスパス類はWindows用に変換してくれる模様)
HADOOP_HOME/bin/hadoopシェルの中を見てみると、結局はjavaコマンドでクラスを実行しているだけ。[2010-02-22]
ということは、パラメーターさえ用意してやれば、Eclipseから直接実行できる。
タブ | 項目 | 設定値 | 備考 | |
---|---|---|---|---|
メイン | メイン・クラス(M) | jp.hishidama.hadoop.tutorial.WordCount | 変更不要。 | |
引数 | プログラムの引数(A) | C:\cygwin\home\hadoop\tutorial\input C:\cygwin\home\hadoop\tutorial\output |
WordCountの引数。 | |
VM引数(G) | -Xmx1000m -Dhadoop.log.dir=C:\cygwin\usr\local\hadoop-0.20.1\logs -Dhadoop.log.file=hadoop.log -Dhadoop.home.dir=C:\cygwin\usr\local\hadoop-0.20.1\ -Dhadoop.id.str=host -Dhadoop.root.logger=INFO,console -Dhadoop.policy.file=hadoop-policy.xml |
|||
クラスパス | クラスパス(L) |
C:\cygwin\usr\local\hadoop-0.20.1\conf (これは無くても動くが、一番先頭に追加することで conf\log4j.propertiesの設定が読み込まれるようになる) |
ユーザー・エントリーに 「拡張(A)」→「外部フォルダー(E)」で 左記のものを追加。 |
|
C:\cygwin\usr\local\hadoop-0.20.1\hadoop-0.20.1-core.jar | コンパイルに使っているので デフォルトで入っているはず。 |
|||
C:\cygwin\usr\local\hadoop-0.20.1\lib の直下のjarファイル全て (場合によってはlib\jsp-2.1内のjarファイルも全て) |
ユーザー・エントリーに 「外部JARの追加(X)」で 左記のものを追加。 |
|||
環境 | 変数 | PATH | C:\cygwin\bin | 「新規(E)」で名前「PATH」を追加。 (これが無いとchmodが実行できない) |
この方法でデバッグ実行をすれば、ブレークポイントで止めてデバッグすることも出来る。