Kotlin Coroutines と Ktor HTTP Client で作るスケールするタスク実行
by
Hideyuki Takeuchi
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
Kotlin Coroutines と Ktor HTTP Client で作る スケールするタスク実⾏
Slide 2
Slide 2 text
⾃⼰紹介 たけうち ひでゆき @chimerast 株式会社イエソド エンジニア
Slide 3
Slide 3 text
イエソド社 提供サービス SaaS 統制 プラットフォーム 「 YESOD 」 ⼈・組織・情報と SaaS をシームレスに 管理するためのSaaS
Slide 4
Slide 4 text
No content
Slide 5
Slide 5 text
No content
Slide 6
Slide 6 text
No content
Slide 7
Slide 7 text
No content
Slide 8
Slide 8 text
本題
Slide 9
Slide 9 text
本⽇扱うコルーチンの対象範囲 ⼤量のタスクの並⾏実⾏
Slide 10
Slide 10 text
サービスの要件の⼀つ 毎朝午前 4 時に、顧客が利⽤している、 全ての SaaS のアカウント状態を取り込みたい
Slide 11
Slide 11 text
No content
Slide 12
Slide 12 text
SaaS 連携サーバの課題 SaaS 連携サーバからのHTTP レスポンスが、 2 秒〜20 分 SaaS 連携サーバは各SaaS からアカウントの状態を取り込む SaaS 連携サーバは無限に⽔平スケールする想定(GKE) もちろんDDoS っぽくならないようにスロットリングはする
Slide 13
Slide 13 text
そこから派⽣する課題 「全てのお客さんが使っている、全てのSaaS のアカウント」 100 企業 × 100 SaaS = 10,000 リクエスト 1,000 企業 × 100 SaaS = 100,000 リクエスト Kotlin/Java でHTTP のブロッキングAPI を利⽤して作ると、 スレッド数の限界を超える
Slide 14
Slide 14 text
スレッドを利⽤したダメな例 fun main() { val pool = Executors.newFixedThreadPool(4) repeat(10000) { // 10,000 タスク pool.submit { // 10 秒レスポンスを待つだけのタスク Thread.sleep(10000L) println("DONE: " + Thread.currentThread().name) } } pool.shutdown() } → 実質4 並列でしか動かず、10 秒に4 つタスクが終わる。 全て終わるまで約7 時間。
Slide 15
Slide 15 text
並⾏タスク実⾏における スレッド & コルーチンおさらい
Slide 16
Slide 16 text
並⾏プログラミング観点から プロセス プリエンプティブマルチタスク メモリ空間: 独⽴ タスク切り替え: OS レイヤー スレッド プリエンプティブマルチタスク メモリ空間: 共有 タスク切り替え: OS レイヤー コルーチン ノンプリエンプティブマルチタスク メモリ空間: 共有 タスク切り替え: アプリレイヤー
Slide 17
Slide 17 text
同期・⾮同期プログラミング観点から スレッド ブロッキングモデル タスク実⾏中に、待ちが発⽣した際に、他のタスクにスレッドを 明け渡せない コルーチン ノンブロッキングモデル タスク実⾏中に、待ちが発⽣した際に、スレッドを明け渡せる 変な待ち⽅をすると他の全てのタスクが⽌まる
Slide 18
Slide 18 text
スレッド vs コルーチン スレッド コルーチン タスク辺りのメモリ使⽤量 多くなる 少ない コンテキストスイッチのオーバーヘッド ある 少ない プログラムのしやすさ 簡単 難しい (?) プログラムのしやすさは諸説ある コルーチンは、プログラマが他のタスクに、 いつCPU を明け渡すか制御しなければならない。
Slide 19
Slide 19 text
スレッド と コルーチン の関係性 スレッド と コルーチン は排他ではない。 複数スレッドの上で複数コルーチンを動かすことができる Node.js 1 スレッド x 複数コルーチン (Promise) Kotlin 複数スレッド x 複数コルーチン (suspending function)
Slide 20
Slide 20 text
Coroutines | Kotlin https://kotlinlang.org/docs/coroutines-overview.html コルーチンをいい感じに実装できるAPI 群
Slide 21
Slide 21 text
Coroutines を複数スレッドで⾛らせる正しい例 fun main() { val pool = Executors.newFixedThreadPool(4) runBlocking(pool.asCoroutineDispatcher()) { repeat(10000) { // 10,000 タスク async { // 10 秒レスポンスを待つだけのタスク delay(10000L) println("DONE: " + Thread.currentThread().name) } } } pool.shutdown() } → 10 秒(10000L) で10000 タスク全部終わる
Slide 22
Slide 22 text
Coroutines を使ってるけどダメな例 fun main() { val pool = Executors.newFixedThreadPool(4) runBlocking(pool.asCoroutineDispatcher()) { repeat(10000) { // 10,000 タスク async { // 10 秒レスポンスを待つだけのタスク Thread.sleep(10000L) // delay(10000L) println("DONE: " + Thread.currentThread().name) } } } pool.shutdown() } → 10 秒(10000L) に⼀回4 つタスクが流れるだけ
Slide 23
Slide 23 text
Coroutines を使っているのに、なにが違うか? delay(10000L) suspending function → ノンブロッキング 他のコルーチンが動ける Thread.sleep(10000L) 普通のfunction → ブロッキング 他のコルーチンが動けない
Slide 24
Slide 24 text
Ktor HTTP Client https://ktor.io/docs/client.html JetBrains 謹製のフレームワーク「Ktor 」の HTTP Client API 群 Kotlin coroutines を利⽤した、awesome な HTTP Client ( 意訳) もちろんすべてノンブロッキングAPI
Slide 25
Slide 25 text
Ktor HTTP Client で HTTP リクエストを投げる fun main() { val client = HttpClient() val pool = Executors.newFixedThreadPool(4) runBlocking(pool.asCoroutineDispatcher()) { repeat(10000) { async { val response = client.get("http://localhost:4000/healthcheck") println("DONE [${Thread.currentThread().name}]: $response") } } } pool.shutdown() } → HTTP サーバからレスポンスが待たされたとしても、 他のリクエストが並⾏で実⾏される
Slide 26
Slide 26 text
並⾏ / 並列処理プログラミング TIPS ローカルでの開発時には、 すべてのスレッドプールの数を1 にする DB のコネクションプールの数も1 にする
Slide 27
Slide 27 text
Ktor TIPS デフォルトのHTTP 待ち受けスレッドプールの数が Runtime.getRuntime().availableProcessors() (CPU 論理コア数) に 設定される。 クラスタのノードがしょぼいインスタンスだとこれが1 になって、 本番環境だとなんか動かないと焦る時がある ktor { deployment { connectionGroupSize = < 適切な値> workerGroupSize = < 適切な値> callGroupSize = < 適切な値> } }
Slide 28
Slide 28 text
まとめ 並⾏・並列処理と コルーチンはおもしろいよ 並⾏と並列の⾔葉遣い間違ってたら すみません 右の画像は、15 年前の本だけどガチ でお勧めな本
Slide 29
Slide 29 text
Kotlin Ktor Exposed TypeScript Vue.js Vuex Kubenetes (GKE) Istio Grafana Prometheus Argo CD