Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Scala on Hadoop

7,684 views

Published on

Published in: Technology
  • Be the first to comment

Scala on Hadoop

  1. 1. Hadoop Conference Scala on Hadoop はてな 田中 慎司 stanaka @ hatena.ne.jp http://d.hatena.ne.jp/stanaka/ http://twitter.com/stanaka/
  2. 2. アジェンダ <ul><li>自己紹介 </li></ul><ul><li>はてなでの Hadoop </li></ul><ul><ul><li>Scala までの道 </li></ul></ul><ul><li>Scala on Hadoop </li></ul><ul><li>Scala on Hadoop の応用 </li></ul>
  3. 3. 自己紹介 <ul><li>( 株 ) はてな 執行役員 </li></ul><ul><li>担当領域 </li></ul><ul><ul><li>システムアーキテクチャ </li></ul></ul><ul><ul><ul><li>スケーラビリティ </li></ul></ul></ul><ul><ul><li>サーバ・ネットワーク </li></ul></ul><ul><ul><li>サポート </li></ul></ul>
  4. 4. はてなでの Hadoop #1 <ul><li>自作サーバ 10 台 </li></ul><ul><ul><li>CPU: Core2 Quad x 1 </li></ul></ul><ul><ul><li>Mem: 8GB </li></ul></ul><ul><ul><li>HDD: 3TB </li></ul></ul><ul><li>DC ではなくオフィスに置いて、電源代節約 </li></ul>
  5. 5. はてなでの Hadoop #2 <ul><li>蓄積されるデータ ( 主にログ ) </li></ul><ul><ul><li>ダイアリー 7G/day </li></ul></ul><ul><ul><li>ブックマーク 5G/day </li></ul></ul><ul><ul><li>うごメモ 3G/day </li></ul></ul><ul><li>ジョブ </li></ul><ul><ul><li>300 jobs/day </li></ul></ul>
  6. 6. はてなでの Hadoop システム ( 現状 ) Hadoop MapReduce HDFS Reverse Proxy ジョブの 投入 Hatena Fotolife Hatena Graph ログを 時間毎に蓄積 /logs/$service/$year/$month/$date/$host_access-$hour.log
  7. 7. Hadoop <ul><li>2008/5 頃 調査 </li></ul><ul><ul><li>Hadoop Streaming </li></ul></ul><ul><li>2008/8 頃 稼働 </li></ul><ul><ul><li>Perl による Mapper, Reducer </li></ul></ul><ul><ul><li>YAML でジョブを定義 </li></ul></ul><ul><li>2009/4 WebUI を作成 </li></ul><ul><li>2009/11 Scala 化 ← イマココ </li></ul>
  8. 8. Hadoop Streaming <ul><li>Java 以外の言語で MapReduce を可能に ! </li></ul><ul><ul><li>Map, Reduce の入出力を標準入力 / 標準出力として扱う -> map.pl, reduce.pl を用意するだけ </li></ul></ul><ul><li>通常は入力出力共に HDFS 上に置かれる </li></ul>
  9. 9. map.pl #!/usr/bin/env perl use strict; use warnings; while (<>) { chomp; my @segments = split /s+/; printf &quot;%s %s &quot;, $segments[8], 1; }
  10. 10. reduce.pl #!/usr/bin/env perl use strict; use warnings; my %count; while (<>) { chomp; my ($key, $value) = split / /; $count{$key}++; } while (my ($key, $value) = each %count) { printf &quot;%s %s &quot;, $key, $value; }
  11. 11. 実行 % hadoop jar $HADOOP_DIR/contrib/hadoop-*-streaming.jar -input httpd_logs -output analog_out -mapper /home/user/work/analog/map.pl -reducer /home/user/work/analog/reduce.pl
  12. 12. ジョブの定義 <ul><li>YAML で定義 </li></ul>- name: latency mapper: class: LogAnalyzer::Mapper options: filters: isbot: 0 conditions: - key: Top filters: uri: '^/$' value: $response reducer: class: Reducer::Distribution input: class: LogAnalyzer::Input options: service: ugomemo period: 1 output: class: Output::Gnuplot options: title: &quot;Ugomemo Latency $date&quot; xlabel: &quot;Response time (msec)&quot; ylabel: &quot;Rates of requests (%)&quot; fotolife_folder: ugomemo
  13. 13. WebUI
  14. 14. Hadoop Streaming の限界 <ul><li>遅い ← Perl の問題も .. </li></ul><ul><li>ジョブを KILL しても、プロセスが残ることがある </li></ul><ul><li>HDFS 操作が遅い </li></ul><ul><li>Combiner が定義できない </li></ul>
  15. 15. Scala <ul><li>2003 年登場 </li></ul><ul><li>関数型の特徴を備えた言語 </li></ul><ul><li>普通のオブジェクト指向っぽくも書ける </li></ul><ul><li>JavaVM 上で動作する </li></ul>object HelloWorld { def main(args: Array[String]) { println(&quot;Hello, world!&quot;) } }
  16. 16. Scala による Quick sort def qsort[T <% Ordered[T]](list: List[T]): List[T] = list match { case Nil => Nil case pivot::tail => qsort(tail.filter(_ < pivot)) ::: pivot :: qsort(tail.filter(_ >= pivot)) } scala> qsort(List(2,1,3)) res1: List[Int] = List(1, 2, 3)
  17. 17. WordCount by Java public class WordCount { public static class Map extends MapReduceBase implements Mapper<LongWritable, IntWritable> { private final static IntWritable one = new IntWritable(1); private Text word = new Text(); public void map(LongWritable key, Text value, OutputCollector<Text, IntWritable> output, Reporter reporter) throws IOException { String line = value.toString(); StringTokenizer tokenizer = new StringTokenizer(line); while (tokenizer.hasMoreTokens()) { word.set(tokenizer.nextToken()); output.collect(word, one); } } } public static class Reduce extends MapReduceBase implements Reducer<Text, IntWritable> { …
  18. 18. WordCount by Scala object WordCount { class MyMap extends Mapper[LongWritable, Text, Text, IntWritable] { val one = 1 override def map(ky: LongWritable, value: Text, output: Mapper[LongWritable, Text, Text, IntWritable]#Context) = { (value split &quot; &quot;) foreach (output write (_, one)) } } class MyReduce extends Reducer[Text, IntWritable, Text, IntWritable] { override def reduce(key: Text, values: java.lang.Iterable[IntWritable], output: Reducer[Text, IntWritable, Text, IntWritable]#Context) = { val iter: Iterator[IntWritable] = values.iterator() val sum = iter reduceLeft ((a: Int, b: Int) => a + b) output write (key, sum) } } def main(args: Array[String]) = { …
  19. 19. Java vs Scala <ul><li>Java </li></ul><ul><li>Scala </li></ul>public void map(LongWritable key, Text value, OutputCollector<Text, IntWritable> output, Reporter reporter) throws IOException { String line = value.toString(); StringTokenizer tokenizer = new StringTokenizer(line); while (tokenizer.hasMoreTokens()) { word.set(tokenizer.nextToken()); output.collect(word, one); } } override def map(ky: LongWritable, value: Text, output: Mapper[LongWritable, Text, Text, IntWritable]#Context) = { (value split &quot; &quot;) foreach (output write (_, one)) }
  20. 20. Scala on Hadoop <ul><li>Java と Scala を接続するライブラリが必要 </li></ul><ul><li>SHadoop </li></ul><ul><ul><li>http://code.google.com/p/jweslley/source/browse/#svn/trunk/scala/shadoop </li></ul></ul><ul><ul><li>型変換を行うシンプルなライブラリ </li></ul></ul>
  21. 21. mapper class MyMap extends Mapper[LongWritable, Text, Text, IntWritable] { val one = 1 override def map(ky: LongWritable, value: Text, output: Mapper[LongWritable, Text, Text, IntWritable]#Context) = { (value split &quot; &quot;) foreach (output write (_, one)) } }
  22. 22. reducer class MyReduce extends Reducer[Text, IntWritable, Text, IntWritable] { override def reduce(key: Text, values: java.lang.Iterable[IntWritable], output: Reducer[Text, IntWritable, Text, IntWritable]#Context) = { val iter: Iterator[IntWritable] = values.iterator() val sum = iter reduceLeft ((a: Int, b: Int) => a + b) output write (key, sum) } }
  23. 23. main def main(args: Array[String]) = { val conf = new Configuration() val otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs() val job = new Job(conf, &quot;word count&quot;) job setJarByClass(WordCount getClass()) job setMapperClass(classOf[WordCount.MyMap]) job setCombinerClass(classOf[WordCount.MyReduce]) job setReducerClass(classOf[WordCount.MyReduce]) job setMapOutputKeyClass(classOf[Text]) job setMapOutputValueClass(classOf[IntWritable]) job setOutputKeyClass(classOf[Text]) job setOutputValueClass(classOf[IntWritable]) FileInputFormat addInputPath(job, new Path(otherArgs(0))) FileOutputFormat setOutputPath(job, new Path(otherArgs(1))) System exit(job waitForCompletion(true) match { case true => 0 case false => 1}) }
  24. 24. HDFS 操作 import java.net.URI import org.apache.hadoop.fs._ import org.apache.hadoop.hdfs._ import org.apache.hadoop.conf.Configuration object Hdfs { def main(args: Array[String]) = { val conf = new Configuration() val uri = new URI(&quot;hdfs://hadoop01:9000/&quot;) val fs = new DistributedFileSystem fs.initialize(uri, conf) var status = fs.getFileStatus(new Path(args(0))) println(status.getModificationTime) } }
  25. 25. ビルド手法 <ul><li>Maven </li></ul><ul><ul><li>http://maven.apache.org/ </li></ul></ul><ul><ul><li>Java 系のプロジェクト管理ツール </li></ul></ul><ul><li>プロジェクト作成 </li></ul>mvn org.apache.maven.plugins:maven-archetype-plugin:2.0-alpha-4:create -DarchetypeGroupId=org.scala-tools.archetypes -DarchetypeArtifactId=scala-archetype-simple -DarchetypeVersion=1.2 -DremoteRepositories=http://scala-tools.org/repo-releases -DgroupId=com.hatena.hadoop -DartifactId=hadoop
  26. 26. 依存関係の記述 <ul><li>Hadoop 関連 jar の登録 </li></ul><ul><li>依存関係の記述 </li></ul><dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.0.4</version> <scope>provided</scope> </dependency> <dependency> <groupId>commons-cli</groupId> <artifactId>commons-cli</artifactId> <version>1.0</version> <scope>provided</scope> </dependency> mvn install:install-file -DgroupId=org.apache.hadoop -DartifactId=hadoop-core -Dversion=0.20.1 -Dpackaging=jar -Dfile=/opt/hadoop/hadoop-0.20.1-core.jar
  27. 27. ビルド・パッケージ作成と実行 <ul><li>ビルド・パッケージ作成 </li></ul><ul><li>実行 </li></ul>$HADOOP_HOME/bin/hadoop jar ../maven/hadoop/target/hadoop-1.0-SNAPSHOT.jar com.hatena.hadoop.Hadoop -D mapred.job.tracker=local -D fs.default.name=file:/// input output mvn scala:compile mvn package mvn clean
  28. 28. レスポンス時間の計測 #1 <ul><li>計測方法 </li></ul><ul><ul><li>特定の URL を叩いて、その時間を計測 </li></ul></ul><ul><ul><li>生アクセスログから収集 </li></ul></ul><ul><li>生アクセスログを分析 </li></ul><ul><ul><li>Hadoop クラスタ </li></ul></ul><ul><ul><ul><li>Core2Quad サーバ 10 台 </li></ul></ul></ul><ul><ul><ul><li>はてなダイアリーのログ 7GB -> 10 分程度で処理 </li></ul></ul></ul><ul><ul><li>分布をグラフ化 </li></ul></ul>
  29. 29. レスポンス時間の計測 #2 Mapper URL などの条件でフィルタ レスポンス時間を記録 Reducer レスポンス時間の分布を計算 後処理 グラフ化 (gnuplot) Fotolife にアップロード (AtomAPI)
  30. 30. レスポンス時間の分布グラフ
  31. 31. 良好なレスポンスの例
  32. 32. キャッシュによる影響
  33. 33. まとめ <ul><li>はてなでの Hadoop </li></ul><ul><li>Scala on Hadoop </li></ul><ul><li>色々、触って楽しみましょう ! </li></ul>
  34. 34. <ul><li>Q & A </li></ul><ul><li>[email_address] </li></ul>

×