Apache Crunchガイド
1. 前書き
このチュートリアルでは、データ処理アプリケーションの例を使用してApache Crunchを示します。 このアプリケーションは、MapReduceフレームワークを使用して実行します。
まず、ApacheCrunchの概念について簡単に説明します。 次に、サンプルアプリにジャンプします。 このアプリでは、テキスト処理を行います。
-
まず、テキストファイルから行を読み取ります
-
後で、それらを単語に分割し、いくつかの一般的な単語を削除します
-
次に、残りの単語をグループ化して、一意の単語とその数のリストを取得します
-
最後に、このリストをテキストファイルに書き込みます
2. Crunchとは何ですか?
MapReduceは、サーバーのクラスターで大量のデータを処理するための分散並列プログラミングフレームワークです。 HadoopやSparkなどのソフトウェアフレームワークはMapReduceを実装しています。
Crunch provides a framework for writing, testing and running MapReduce pipelines in Java. ここでは、MapReduceジョブを直接記述していません。 むしろ、データパイプライン(つまり、 Crunch APIを使用して、入力、処理、および出力ステップを実行する操作)。 Crunch Plannerは、それらをMapReduceジョブにマップし、必要に応じて実行します。
Therefore, every Crunch data pipeline is coordinated by an instance of the Pipeline interface.このインターフェイスは、Sourceインスタンスを介してパイプラインにデータを読み取り、パイプラインからTargetインスタンスにデータを書き込むためのメソッドも定義します。
データを表すための3つのインターフェイスがあります。
-
PCollection –要素の不変の分散コレクション
-
PTable<K、V> –キーと値の不変の分散された順序付けられていないマルチマップ
-
PGroupedTable<K、V> –タイプKのキーのIterable Vへの分散されたソート済みマップ。これは1回だけ繰り返すことができます。
DoFn is the base class for all data processing functions。 これは、MapReduceのMapper、Reducer、およびCombinerクラスに対応します。 開発時間のほとんどは、それを使用した論理計算の記述とテストに費やされます.
Crunchに慣れてきたので、それを使用してサンプルアプリケーションを作成しましょう。
3. Crunchプロジェクトのセットアップ
まず、Mavenでクランチプロジェクトを設定しましょう。 これには2つの方法があります。
-
既存のプロジェクトのpom.xmlファイルに必要な依存関係を追加します
-
アーキタイプを使用してスタータープロジェクトを生成する
両方のアプローチを簡単に見てみましょう。
3.1. Mavenの依存関係
Crunchを既存のプロジェクトに追加するために、必要な依存関係をpom.xmlファイルに追加しましょう。
まず、crunch-coreライブラリを追加しましょう。
org.apache.crunch
crunch-core
0.15.0
次に、Hadoopと通信するためのhadoop-clientライブラリを追加しましょう。 Hadoopインストールに一致するバージョンを使用します。
org.apache.hadoop
hadoop-client
2.2.0
provided
Maven Centralでcrunch-coreおよびhadoop-clientライブラリの最新バージョンを確認できます。
3.2. メイヴンの原型
Another approach is to quickly generate a starter project using the Maven archetype provided by Crunch:
mvn archetype:generate -Dfilter=org.apache.crunch:crunch-archetype
上記のコマンドでプロンプトが表示されたら、Crunchバージョンとプロジェクト成果物の詳細を提供します。
4. クランチパイプラインのセットアップ
プロジェクトを設定した後、Pipelineオブジェクトを作成する必要があります。 Crunch has 3 Pipeline implementations:
-
MRPipeline – HadoopMapReduce内で実行
-
SparkPipeline –一連のSparkパイプラインとして実行されます
-
MemPipeline –クライアントでメモリ内で実行され、単体テストに役立ちます
通常、MemPipelineのインスタンスを使用して開発およびテストします。 後で、実際の実行にMRPipelineまたはSparkPipelineのインスタンスを使用します。
インメモリパイプラインが必要な場合は、静的メソッドgetInstanceを使用してMemPipelineインスタンスを取得できます。
Pipeline pipeline = MemPipeline.getInstance();
ただし、ここでは、MRPipelineのインスタンスを作成して、Hadoop:でアプリケーションを実行しましょう。
Pipeline pipeline = new MRPipeline(WordCount.class, getConf());
5. 入力データを読む
パイプラインオブジェクトを作成したら、入力データを読み取ります。 The Pipeline interface provides a convenience method to read input from a text file、readTextFile(pathName).
このメソッドを呼び出して、入力テキストファイルを読み取ります。
PCollection lines = pipeline.readTextFile(inputPath);
上記のコードは、テキストファイルをStringのコレクションとして読み取ります。
次のステップとして、入力を読み取るためのテストケースを作成しましょう。
@Test
public void givenPipeLine_whenTextFileRead_thenExpectedNumberOfRecordsRead() {
Pipeline pipeline = MemPipeline.getInstance();
PCollection lines = pipeline.readTextFile(INPUT_FILE_PATH);
assertEquals(21, lines.asCollection()
.getValue()
.size());
}
このテストでは、テキストファイルを読み取るときに予想される行数を取得することを確認します。
6. データ処理手順
入力データを読み取った後、それを処理する必要があります。 Crunch API contains a number of subclasses of DoFn to handle common data processing scenarios:
-
FilterFn –ブール条件に基づいてコレクションのメンバーをフィルタリングします
-
MapFn –各入力レコードを正確に1つの出力レコードにマップします
-
CombineFn –複数の値を1つの値に結合します
-
JoinFn –内部結合、左外部結合、右外部結合、完全外部結合などの結合を実行します
これらのクラスを使用して、次のデータ処理ロジックを実装しましょう。
-
入力ファイルの各行を単語に分割します
-
ストップワードを削除する
-
ユニークな単語を数える
6.1. テキストの行を単語に分割する
まず、行を単語に分割するTokenizerクラスを作成しましょう。
DoFnクラスを拡張します。 このクラスには、processという抽象メソッドがあります。 このメソッドは、PCollectionからの入力レコードを処理し、出力をEmitter. に送信します
このメソッドに分割ロジックを実装する必要があります。
public class Tokenizer extends DoFn {
private static final Splitter SPLITTER = Splitter
.onPattern("\\s+")
.omitEmptyStrings();
@Override
public void process(String line, Emitter emitter) {
for (String word : SPLITTER.split(line)) {
emitter.emit(word);
}
}
}
上記の実装では、GuavaライブラリのSplitterクラスを使用して、行から単語を抽出しました。
次に、Tokenizerクラスの単体テストを作成しましょう。
@RunWith(MockitoJUnitRunner.class)
public class TokenizerUnitTest {
@Mock
private Emitter emitter;
@Test
public void givenTokenizer_whenLineProcessed_thenOnlyExpectedWordsEmitted() {
Tokenizer splitter = new Tokenizer();
splitter.process(" hello world ", emitter);
verify(emitter).emit("hello");
verify(emitter).emit("world");
verifyNoMoreInteractions(emitter);
}
}
上記のテストは、正しい単語が返されることを確認します。
最後に、このクラスを使用して、入力テキストファイルから読み取った行を分割しましょう。
PCollectionインターフェイスのparallelDoメソッドは、指定されたDoFnをすべての要素に適用し、新しいPCollectionを返します。
行コレクションでこのメソッドを呼び出して、Tokenizerのインスタンスを渡します。
PCollection words = lines.parallelDo(new Tokenizer(), Writables.strings());
その結果、入力テキストファイル内の単語のリストを取得します。 次のステップでストップワードを削除します。
6.2. ストップワードを削除
前の手順と同様に、ストップワードを除外するためにStopWordFilterクラスを作成しましょう。
ただし、DoFnではなくFilterFnを拡張します。 FilterFnには、acceptと呼ばれる抽象メソッドがあります。 このメソッドにフィルタリングロジックを実装する必要があります。
public class StopWordFilter extends FilterFn {
// English stop words, borrowed from Lucene.
private static final Set STOP_WORDS = ImmutableSet
.copyOf(new String[] { "a", "and", "are", "as", "at", "be", "but", "by",
"for", "if", "in", "into", "is", "it", "no", "not", "of", "on",
"or", "s", "such", "t", "that", "the", "their", "then", "there",
"these", "they", "this", "to", "was", "will", "with" });
@Override
public boolean accept(String word) {
return !STOP_WORDS.contains(word);
}
}
次に、StopWordFilterクラスの単体テストを記述しましょう。
public class StopWordFilterUnitTest {
@Test
public void givenFilter_whenStopWordPassed_thenFalseReturned() {
FilterFn filter = new StopWordFilter();
assertFalse(filter.accept("the"));
assertFalse(filter.accept("a"));
}
@Test
public void givenFilter_whenNonStopWordPassed_thenTrueReturned() {
FilterFn filter = new StopWordFilter();
assertTrue(filter.accept("Hello"));
assertTrue(filter.accept("World"));
}
@Test
public void givenWordCollection_whenFiltered_thenStopWordsRemoved() {
PCollection words = MemPipeline
.collectionOf("This", "is", "a", "test", "sentence");
PCollection noStopWords = words.filter(new StopWordFilter());
assertEquals(ImmutableList.of("This", "test", "sentence"),
Lists.newArrayList(noStopWords.materialize()));
}
}
このテストは、フィルタリングロジックが正しく実行されることを確認します。
最後に、StopWordFilterを使用して、前の手順で生成された単語のリストをフィルタリングしましょう。 The filter method of PCollection interface applies the given FilterFn to all the elements and returns a new PCollection.
単語コレクションでこのメソッドを呼び出し、StopWordFilterのインスタンスを渡します。
PCollection noStopWords = words.filter(new StopWordFilter());
その結果、フィルタリングされた単語のコレクションを取得します。
6.3. ユニークな単語を数える
フィルタリングされた単語のコレクションを取得した後、各単語の出現頻度をカウントします。 PCollection interface has a number of methods to perform common aggregations:
-
min –コレクションの最小要素を返します
-
max –コレクションの最大要素を返します
-
length –コレクション内の要素の数を返します
-
count –コレクションの一意の各要素の数を含むPTableを返します
countメソッドを使用して、一意の単語とその数を取得しましょう。
// The count method applies a series of Crunch primitives and returns
// a map of the unique words in the input PCollection to their counts.
PTable counts = noStopWords.count();
7. 出力を指定する
前の手順の結果、単語とそのカウントの表ができました。 この結果をテキストファイルに書き込みます。 The Pipeline interface provides convenience methods to write output:
void write(PCollection collection, Target target);
void write(PCollection collection, Target target,
Target.WriteMode writeMode);
void writeTextFile(PCollection collection, String pathName);
したがって、writeTextFileメソッドを呼び出しましょう。
pipeline.writeTextFile(counts, outputPath);
8. パイプライン実行の管理
これまでのすべての手順で、データパイプラインが定義されました。 入力の読み取りまたは処理は行われていません。 これは、Crunch uses lazy execution model.が
パイプラインインターフェースでジョブの計画と実行を制御するメソッドが呼び出されるまで、MapReduceジョブは実行されません。
-
run –必要な出力を作成するための実行プランを準備し、それを同期的に実行します
-
done –出力の生成に必要な残りのジョブを実行し、作成された中間データファイルをクリーンアップします
-
runAsync – runメソッドに似ていますが、非ブロッキング方式で実行されます
したがって、doneメソッドを呼び出して、パイプラインをMapReduceジョブとして実行しましょう。
PipelineResult result = pipeline.done();
上記のステートメントは、MapReduceジョブを実行して入力を読み取り、処理し、結果を出力ディレクトリに書き込みます。
9. パイプラインをまとめる
これまで、入力データを読み取り、処理し、出力ファイルに書き込むためのロジックを開発および単体テストしました。
次に、それらを組み合わせて、データパイプライン全体を構築しましょう。
public int run(String[] args) throws Exception {
String inputPath = args[0];
String outputPath = args[1];
// Create an object to coordinate pipeline creation and execution.
Pipeline pipeline = new MRPipeline(WordCount.class, getConf());
// Reference a given text file as a collection of Strings.
PCollection lines = pipeline.readTextFile(inputPath);
// Define a function that splits each line in a PCollection of Strings into
// a PCollection made up of the individual words in the file.
// The second argument sets the serialization format.
PCollection words = lines.parallelDo(new Tokenizer(), Writables.strings());
// Take the collection of words and remove known stop words.
PCollection noStopWords = words.filter(new StopWordFilter());
// The count method applies a series of Crunch primitives and returns
// a map of the unique words in the input PCollection to their counts.
PTable counts = noStopWords.count();
// Instruct the pipeline to write the resulting counts to a text file.
pipeline.writeTextFile(counts, outputPath);
// Execute the pipeline as a MapReduce.
PipelineResult result = pipeline.done();
return result.succeeded() ? 0 : 1;
}
10. Hadoop起動設定
これで、データパイプラインの準備ができました。
ただし、それを起動するにはコードが必要です。 したがって、アプリケーションを起動するためのmainメソッドを記述しましょう。
public class WordCount extends Configured implements Tool {
public static void main(String[] args) throws Exception {
ToolRunner.run(new Configuration(), new WordCount(), args);
}
ToolRunner.runは、コマンドラインからHadoop構成を解析し、MapReduceジョブを実行します。
11. アプリケーションを実行
これで、完全なアプリケーションの準備ができました。 次のコマンドを実行してビルドしましょう。
mvn package
上記のコマンドの結果、パッケージ化されたアプリケーションと特別なジョブjarをターゲットディレクトリに取得します。
このジョブjarを使用して、Hadoopでアプリケーションを実行してみましょう。
hadoop jar target/crunch-1.0-SNAPSHOT-job.jar
アプリケーションは入力ファイルを読み取り、結果を出力ファイルに書き込みます。 出力ファイルには、次のようなカウントとともに一意の単語が含まれています。
[Add,1]
[Added,1]
[Admiration,1]
[Admitting,1]
[Allowance,1]
Hadoopに加えて、IDE内でアプリケーションをスタンドアロンアプリケーションまたは単体テストとして実行できます。
12. 結論
このチュートリアルでは、MapReduceで実行するデータ処理アプリケーションを作成しました。 Apache Crunchを使用すると、JavaでMapReduceパイプラインを簡単に記述、テスト、実行できます。
いつものように、完全なソースコードはover on Githubにあります。