RxJava + Vert.x + jOOλ で Microservice的な何かを
作ってみた
たけうち ひでゆき
たけうち ひでゆき@chimerast
株式会社ユーザベース チーフテクノロジスト / イノベーション担当執行役員
企業・業界分析の 情報プラットフォーム
全世界300万社超 / 550業界のデータ
世界最大級のM&Aデータ
経済情報に特化した ソーシャル経済ニュース サービス
経済ニュースを 解説コメント付きで まとめ読み
130万ユーザぐらい
本題
RxJava + Vert.x + jOOλ で作ったもの
チャート描画用のRESTfulなAPIサービス• D3.jsでチャートを描画するために会社情報やら統計情報やら必要なデータをいろんなところからかき集めてきてJSONで返す
これを生成したい
D3.jsで描画
サービス構成Companies API
Stats API
Media API
Chart API ブラウザ
Microservices的な何か
サービス登場人物Companies API 企業名称や財務・株価情報データ等を扱うサービス
Stats API 統計名称や統計情報データ等を扱うサービス
Media API チャートの素となる情報を扱うサービス チャートの素 = どの企業のどの勘定科目でチャートを作るかの指定
チャート用JSONが返るまでの流れ1. Media API からチャートの素を取得する
2. チャートの素で指定されたデータをCompanies API や Stats APIに複数回リクエストを投げて取得する
3. 結果をまとめてJSONにしてブラウザに返す
データの取得Companies API
Stats API
Media API
Chart API ブラウザ
①
②, ③, ④, ⑤, ⑥, …
AWSオンプレミス
データセンターの壁Companies API
Stats API
Media API
Chart API ブラウザ
今回の問題
1. データセンターの壁を越えて
2. 複数回独立なリクエストを投げてデータを取得
3. それをまとめ上げてJSONとして返す
今回の選択
Vert.x + Vert.x-Web• ノンブロッキングI/Oな高レベルフレームワーク
• シングルスレッドで大量のコネクションをさばける
• 全てがコールバックで結果が返る非同期API
• Node.js + Express 的な存在 (というかまんまそれ)
• マルチスレッドでも動く Verticleという概念、EventBusという概念
Vert.x-Webのコードの雰囲気
/helloをHTTP GETすると"Hello World!!!"という文字列が返る
router.get("/hello").handler(routingContext -> { routingContext.response().end("Hello world!!!"); });
なぜVert.xを選択したか?• 1リクエストで、バックエンドのAPIに複数回独立したリクエストを投げる必要があった
• ブロッキングI/Oでやると並列化してスレッド数が爆増か直列化してレスポンスが激遅くなるか
RxJava
• Reactive ExtensionsのJava実装
• 非同期の複数イベントうまく扱うための仕組みと関数群
• RxJava = Promise × Stream
Reactive Extensions
• http://www.slideshare.net/stefanmayer13/functional-reactive-programming-with-rxjs
なぜRxJavaを選択したか?• Vert.x 単体だとコールバック地獄に陥る
• SQLでトランザクション使いつつのSelect&Updateとかマジで地獄
• 一度に複数のHTTPリクエストを並列で投げそれをエレガントに管理したかった
• ぶっちゃけPromise的な何かが欲しかった
RxJavaを使わなかった場合(イメージ)client.getConnection(result1 -> { SQLConnection connection = result1.result(); connection.setAutoCommit(false, result2 -> { connection.queryWithParams("SELECT ...", params3, result3 -> { connection.updateWithParams("UPDATE ...", params4, result4 -> { connection.commit(result5 -> { connection.close(result6 -> { successCallback(); }); }); }); }); }); }); エラー処理を入れるともっとひどいことに。。。
RxJavaを使った場合(イメージ)
client.getConnectionObservable().flatMap(connection -> { return connection.setAutoCommitObservable(false) .flatMap(result -> connection.queryWithParamsObservable("SELECT ...", params1)) .flatMap(result -> connection.updateWithParamsObservable("UPDATE ...", params2))) .flatMap(result -> connection.commitObservable()) .flatMap(result -> connection.closeObservable()); });
エラー処理はsubscribeで一回書くだけ!
Vert.x + Vert.x-Web + RxJava
≒ Node.js + Express + Promise (bluebird)
今回の開発でよく使った関数
• observable.map() • observable.flatMap() • Observable.zip() • observable.onErrorReturn()
observable.map()
• あるリクエストの結果を別の形に変換する
observableA .map(responseA -> { .. responseAからresponseZを作る .. return responseZ; });
observable.flatMap()• あるリクエストの結果をもとに別のリクエストを投げる時に使う
observableA .flatMap(responseA -> { .. responseAからobervableBを構築する .. return observableB; });
Observable.zip()• 複数の独立したリクエストを並列で投げるときに使う
Observable.zip( observableA, observableB, (responseA, responseB) -> { .. responseAとresponseBでzipの返り値を作る .. });
observable.onErrorReturn()
• 404エラーだった場合にも後続の処理を続行したいときに使う
observable .map(Optional::of) .onErrorReturn(e -> Optional.empty());
実際の良くあったコード(雰囲気)observableA // Media APIからチャートの素を取得 .flatMap(responseA -> { .. responseAからobservableB, C, D (企業B, C, D)を生成 .. return Observable.zip( observableB.map(Optional::of).onErrorReturn(e -> Optional.empty()), observableC.map(Optional::of).onErrorReturn(e -> Optional.empty()), observableD.map(Optional::of).onErrorReturn(e -> Optional.empty()), (optionalResponseB, optionalResponseC, optionalResponseD) -> { .. responseAとoptionalResponseB, C, DからresponseZを生成 .. return responseZ; // チャートのモデル }); });
jOOλ• Java8 SDKでかゆいところに手が届いていない部分を補完してくれるちっちゃなライブラリ
• Observable.zip()のためにTupleが欲しかったから使った
• Streamが微妙に足りてないところを補完するSeqSeq.zipWithIndex()とかたまに使ったりする
Vert.x-Rx• Vert.xの各モジュールをRxJava化するモジュール
• たまに用意されていないメソッドがあるので困る
• ただのwrapperなので途中からVert.x-Rxを使える
JDBCClient.createShared( // JDBCClientはRxのもの new io.vertx.rxjava.core.Vertx(vertx), // vertxは非Rx config.getJsonObject("jdbc").getJsonObject(db), db);
Vert.x Rest client• https://github.com/hubrick/vertx-rest-client • バックエンドのRESTなAPIを叩くのに使った
• RxJavaにも対応していて使いやすい
• レスポンスをJacksonでデシリアライズしたTが、Ovservable<T>の形で取得できる
まとめ• Vert.xを使うとノンブロッキングI/OでバックエンドのAPIに並列にリクエストを投げられるよ
• RxJavaを使うとVert.xのコールバック地獄から抜け出せるよ
• jOOλはあるとちょこっと便利なライブラリだよ
感想• Vert.x + RxJava はとても相性がいい。無いと死ぬ
• 並列、並列、並列
• Java8に対応していてラムダ式使いまくりで気持ちいい
• バックエンドAPIをいじめている感が出て気持ちいい俺はまだまだいけるぞ的な
エンジニア募集
• 株式会社ユーザベース、株式会社ニューズピックスではMicroservices的な何かを作りたいエンジニアを 募集しています
• 興味がある方は是非お声がけを。Wantedlyからでも。
提 供