![Page 1: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/1.jpg)
ビズリーチの新サービスを Scalaで作ってみた
Naoki Takezoe BizReach, Inc
![Page 2: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/2.jpg)
About BizReach
![Page 3: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/3.jpg)
今日は新しいサービスの 話をします
![Page 4: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/4.jpg)
求人検索エンジン スタンバイ
Available for PC Web, Mobile Web, iOS and Android
![Page 5: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/5.jpg)
今日話すこと• マイクロサービスアーキテクチャ
• Apache Spark
• Scalaで開発する上での課題
![Page 6: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/6.jpg)
マイクロサービス アーキテクチャ
![Page 7: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/7.jpg)
マイクロサービスとは?• サービスによるコンポーネント化:ライブラリではなく別プロセスで動作するサービスによってアプリケーションのコンポーネント化を実現している。
• ビジネスケイパビリティに基づく組織化:役割ごとにチームが構成されるのではなく、複数の役割が混在したチームがひとつのサービスを構築する。(コンウェイの法則!)
• プロジェクトではなくプロダクト:コンポーネントは期限のあるプロジェクトとして開発されるではなく、継続的なプロダクトとして提供される。
• スマートエンドポイント、ダムパイプ:サービス間のメッセージは、HTTP経由でAPI呼び出しされるか、RabbitMQやZeroMQといった軽量メッセージングシステムによる通信で交換される。
• 分散ガバナンス:サービスごとに言語やデータベースなどは統一されず、個別に適切なものが選択される。
• 分散データ管理:サービスごとにデータを持ち、統合されていない。 • インフラストラクチャ自動化:継続的デリバリが実現され、自動テスト、自動デプロイなどが採用されている。
• 障害設計:構成されるサービスの障害に耐性を持つように設計されている。 • 進化的設計:各サービスごとに変更が行なわれ、漸進的に設計がされる。
![Page 8: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/8.jpg)
疎結合な小さなサービスの 集合体としてシステムを構築する
マイクロサービス
マイクロサービス
マイクロサービス
HTTP
Messaging
HTTP
![Page 9: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/9.jpg)
• 独立して開発・メンテナンスが可能
• 耐障害性の高いシステムを構築可能
なにがうれしいのか?
![Page 10: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/10.jpg)
• 並列処理を記述するのが容易
• ノンブロッキングI/Oベースのミドルウェア、フレームワーク、ライブラリが豊富
• 巨大なシステムにおける静的型付けの安全性
なぜScalaなのか?
![Page 11: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/11.jpg)
アーキテクチャ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 ???
![Page 12: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/12.jpg)
アーキテクチャ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アプリもフロントエンドと サーバサイドでレイヤリング
![Page 13: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/13.jpg)
Webアプリもレイヤリング
play2-stub play2-handlebars
フロントサーバ(Play2)
APIサーバ(Play2)
JSON over HTTP
開発時は固定の JSONを返す
JSONをテンプレートに 渡してHTMLをレンダリング
request response
![Page 14: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/14.jpg)
ねらい•サーバサイドとフロントエンドのライフサイクルは異なるので別々にデプロイできるようにしたい
•フロントエンドの修正はフロントエンジニアだけで完結したい
•サーバサイドはScalaで固く、フロントエンドはJavaScriptやテンプレートエンジンで柔らかく作ることで両者のメリットを活かせる
![Page 15: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/15.jpg)
play2-stub
•リクエストを受け取り、routingやstubbingを行うフロントコントローラ •フロントエンドとサーバサイドを分離して開発するためのもの •リクエストやレスポンスを加工するフィルタを挟んだり、複雑な処理は別のコントローラにデリゲートしたりすることもできる
play2stub {! routes: [! {! "GET /author/~authorName/books" {! template = "author-biology"! data = "authors/:authorName.json"! }! }! ]!}
https://github.com/bizreach/play2-stub
リモートAPIを呼び出す代わりに 静的なJSONでレンダリング
![Page 16: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/16.jpg)
play2-handlebars
• Play2でHandlebarsを使うためのプラグイン
•コンパイルなしでテンプレートを修正可能
•デザイナにも扱いやすいテンプレート記法
• Play2のTwirlテンプレートはコンパイルが遅いし記法も複雑で、カッチリ作るにはいいが、デザインを迅速に反映・修正するのには向いてない
https://github.com/bizreach/play2-stubobject Application extends Controller {!! def simple = Action {! Ok(HBS("simple", "who" -> "World"))! }!!}
![Page 17: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/17.jpg)
現実は厳しい•Handlebarsの自由度が低すぎるので大量にヘルパーを作らなくてはいけない(ヘルパーの作成がボトルネックになることも)
•結局画面の仕様に対応したAPIが必要になるケースが多く、再利用可能なAPIにならない
•性能面・工数面などの理由で直接DBに接続せざるを得ない、というような部分もあり、密結合になってしまっている
![Page 18: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/18.jpg)
複数のAPIの呼び出しWeb API
Web API
Web API
Web API
Controller
1つのコントローラから複数のWeb APIを呼び出す必要がある シリアルに呼び出していてはAPIが増えるほど遅くなってしまう
![Page 19: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/19.jpg)
Futureimport scala.concurrent._!import scala.concurrent.ExecutionContext.Implicits.global!!val fileNames = Seq("1.jpg", "2.jpg", "3.jpg", …)!!fileNames.foreach { fileName =>! Future {! resizeAndStore(fileName)! }!}
•FutureはScalaにおける非同期処理の基本的なインターフェース
•Play標準のWS APIはFutureを返す
•Dispatchなど他の通信系ライブラリもFutureを返すものが多い
![Page 20: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/20.jpg)
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)! }!}
![Page 21: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/21.jpg)
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をシリアルに呼び出す
![Page 22: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/22.jpg)
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から値を 取り出すことができるがブロックしてしまう
![Page 23: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/23.jpg)
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をパラレルに呼び出す
![Page 24: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/24.jpg)
やってみて思ったこと• 開発時のオーバーヘッドやサービス毎の冗長化によるコスト増など、短期的にはデメリットの方が大きい
• デバッグが難しくなる、並列プログラミングのスキルが必要になる
• 最初はモノリシックに作り、大きくなってきたタイミングでマイクロサービスにするのがよい
• 大きくなるタイミングはサービスとしては攻め時なので判断としては難しい
![Page 25: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/25.jpg)
Apache Spark
![Page 26: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/26.jpg)
クローリング・インデキシング
データを加工 Elasticsearchクロール JSONHTML
DispatchでHTMLを取得する アクターをスケジュール実行する ことでサーバのリソースを活用
HTMLを解析したり、別のデータと 結合して検索用のインデックスを 生成する処理をSparkで実行
![Page 27: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/27.jpg)
クローリング・インデキシング
データを加工 Elasticsearchクロール JSONHTML
DispatchでHTMLを取得する アクターをスケジュール実行する ことでサーバのリソースを活用
HTMLを解析したり、別のデータと 結合して検索用のインデックスを 生成する処理をSparkで実行
![Page 28: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/28.jpg)
Apache Sparkとは?• Scala製の高速バッチ処理フレームワーク
• 簡単なプログラムで分散処理を記述できる
• 機械学習のMLlib、SQLインターフェースを提供するSpark-SQL、ストリーミング処理用のSpark-Streamingなど様々なサブプロジェクトが存在する
• elastichadoop-sparkというアダプタを使うことでElasticsearchへの入出力が可能
![Page 29: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/29.jpg)
並列分散処理を手軽に記述できる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プログラムと 同じ感覚でコードを書ける
![Page 30: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/30.jpg)
複数のインデックスを結合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インデックスを外部結合
![Page 31: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/31.jpg)
複数のインデックスを更新// 加工したデータをキャッシュしておく!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"!)) 加工したデータをキャッシュしておくことで複数の
インデックスを高速に更新することができる
![Page 32: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/32.jpg)
注意点• I/Oが多いとCPUを使い切れずSparkの良さが出せない
• 細かいジョブを大量に投げると失敗することがある
• 処理の並列度がElasticsearchのシャード数に依存する(elastichadoop-sparkの場合)
• 記述の仕方を少し変えただけで猛烈にパフォーマンスが変わるので使いこなすのが難しい
![Page 33: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/33.jpg)
Scalaを使う上での 一番の課題
![Page 34: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/34.jpg)
一番の課題• Scalaプログラマの教育・採用
• JavaよりもLLからのほうが入りやすい様子
• 長期的にはScalaをもっと普及させる
• 短期的には社内で育成していくしかない
![Page 35: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/35.jpg)
長期的な取り組み
• 書籍・雑誌記事等の執筆
• イベントでの登壇・支援(スポンサー)
• 他社さんとの合同での勉強会
• OSS活動(足りない物を作る)
![Page 36: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/36.jpg)
短期的な取り組み
• Daily Scala(毎朝30分Scalaの勉強会)
• GitHubでのプルリクレビュー
• ハンズオンコンテンツの作成
![Page 38: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/38.jpg)
まとめ• マイクロサービスは良いことばかりではない
• Future大事
• Apache Sparkは便利だけど使いこなすのが難しい
• Scalaプログラマの教育・採用が一番の課題です
![Page 39: ビズリーチの新サービスをScalaで作ってみた 〜マイクロサービスの裏側 #jissenscala](https://reader036.vdocuments.pub/reader036/viewer/2022081421/55a6143a1a28abf4328b457f/html5/thumbnails/39.jpg)
お知らせ• 弊社オフィスのイベントスペースを使用して渋谷java、Swiftもくもく会などいろんな勉強会をやってます
• 会場提供も可能です
お問い合わせはhttp://dcube.ioまたは@takezoenまで!