More Related Content Similar to ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala (20) ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala11. アーキテクチャ
PC Web Mobile Web iOS App Android App
Front API
Backend APIs Backend APIs Backend APIs
Elasticsearch MySQL Memcached
JSON over HTTP
JSON over HTTP
elasticsearch4s Slick ???
12. アーキテクチャ
PC Web Mobile Web iOS App Android App
Front API
Backend APIs Backend APIs Backend APIs
Elasticsearch MySQL Memcached
JSON over HTTP
JSON over HTTP
elasticsearch4s Slick ???
Webアプリもフロントエンドと
サーバサイドでレイヤリング
20. PlayのWS API
// 戻り値としてFutureを返す!
val f: Future[WSResponse] = WS.url(requestUrl).get()!
!
// アクションの実行結果をFutureで返す!
def wsAction = Action.async {!
WS.url(requestUrl).get().map { res =>!
Ok(res.body)!
}!
}
21. Futureによる並列処理
// 1つ目のAPIを呼び出す!
val res1: WSResponse !
= Await.result(WS.url(url1).get(), Duration.Inf)!
// レスポンスを取得!
val body1: String = res1.body!
!
// 2つ目のAPIを呼び出す!
val res2: WSResponse !
= Await.result(WS.url(url2).get(), Duration.Inf)!
// レスポンスを取得!
val body2: String = res2.body
複数のWeb APIをシリアルに呼び出す
22. Futureによる並列処理
// 1つ目のAPIを呼び出す!
val res1: WSResponse !
= Await.result(WS.url(url1).get(), Duration.Inf)!
// レスポンスを取得!
val body1: String = res1.body!
!
// 2つ目のAPIを呼び出す!
val res2: WSResponse !
= Await.result(WS.url(url2).get(), Duration.Inf)!
// レスポンスを取得!
val body2: String = res2.body
複数のWeb APIをシリアルに呼び出す
Await.resultを使うとFutureから値を
取り出すことができるがブロックしてしまう
23. Futureによる並列処理
val f1 = WS.url(url1).get()!
val f2 = WS.url(url2).get()!
!
val f: Future[(String, String)] = for {!
res1 <- f1!
res2 <- f2!
} yield {!
(res1.body, res2.body)!
}
複数のWeb APIをパラレルに呼び出す
28. Apache Sparkとは?
• Scala製の高速バッチ処理フレームワーク
• 簡単なプログラムで分散処理を記述できる
• 機械学習のMLlib、SQLインターフェースを提供するSpark-SQL、
ストリーミング処理用のSpark-Streamingなど様々なサブプロ
ジェクトが存在する
• elastichadoop-sparkというアダプタを使うことでElasticsearch
への入出力が可能
29. 並列分散処理を手軽に記述できる
def main(args: Array[String]) {!
val sc = new SparkContext("local", "Log Query", !
System.getenv("SPARK_HOME"), !
SparkContext.jarOfClass(this.getClass)) // ログファイルをロード!
!
val dataSet = sc.textFile("hdfs://...")!
// ERRORで始まるデータを抽出しキャッシュ!
val cached = dataSet.filter(_ startsWith "ERROR").cache()!
!
val counts1 = cached!
.flatMap(_ split " ") // スペースで分割しフラット化!
.map(_ -> 1) // 文字列とカウントのタプルに変換!
.reduceByKey(_ + _) // 集計!
!
// キャッシュしたデータを使って別の条件で集計する!
val counts2 = cached.filter・・・!
}
毎回ストレージにアクセス
しないようキャッシュ
通常のScalaプログラムと
同じ感覚でコードを書ける
30. 複数のインデックスを結合
val conf = new SparkConf().setAll(Seq(!
ES_NODES -> "localhost",!
ES_PORT -> “9200",!
ES_RESOURCE -> "job",!
ES_QUERY -> "?q=*:*"!
))!
!
val spark = new SparkContext(conf)!
!
val rdd = spark.esRDD.leftOuterJoin(spark.esRDD(Map(!
ES_RESOURCE -> "geo",!
ES_QUERY -> "?q=*:*"!
))).flatMap { case (_id, (_source, geo)) =>!
geo.map { x =>!
_source ++ Map("location" -> x(“location"))!
}!
}
jobインデックスに
geoインデックスを外部結合
31. 複数のインデックスを更新
// 加工したデータをキャッシュしておく!
val rdd = spark.esRDD.map { x=>!
…!
}.cache!
!
// 1台目のElasticsearchにデータを登録!
rdd.saveToEs(Map(!
ES_NODES -> "localhost",!
ES_PORT -> "9200"!
))!
!
// 2台目のElasticsearchにデータを登録!
rdd.saveToEs(Map(!
ES_NODES -> "localhost",!
ES_PORT -> "9201"!
)) 加工したデータをキャッシュしておくことで複数の
インデックスを高速に更新することができる