gRPC Streaming によるスケーラブルな常時接続型 API の構築

gRPC Streaming によるスケーラブルな常時接続型 API の構築

常時接続型 API を構築するとき、 Go + gRPC Streaming はパフォーマンスに優れる有力な選択肢となります。しかしながら常時接続ゆえ、通常通信時間が短時間で終了する Web API とは異なる注意点があります。そこで本セッションでは、gRPC Streaming の紹介にはじまり、注意点やハマりポイントをご紹介します。また、GKE 上でオートスケールするオートモーティブ移動体情報配信システムを構築した事例をご紹介します。

F8e7a1a5f90a13a1dd9fb57ce65cab77?s=128

avvmoto

July 13, 2019
Tweet

Transcript

  1. GoConference’19 gRPC Streaming による スケーラブルな常時接続型 API の構築 Yutaka Imoto DeNA

  2. GoConference’19 今日のお題: gRPC Streaming • サービス紹介 • 直面したスケーラビリティの課題 • 解決策

    • gRPC Streamingの紹介 • インフラ構成 2
  3. GoConference’19 弊社のタクシー配車サービス MOV 3

  4. GoConference’19 4

  5. GoConference’19 5 - スマホアプリでタクシーに配車依頼 - 電話をかけなくてOK - 全車輌ネット決済対応 - 事前のクレカ登録で、車内での支払い無し

    - 対応エリア現在拡大中 - 東京、神奈川、大阪、京都
  6. GoConference’19 アプリ画面 • ユーザーの現在地周辺の、配車可能な タクシーが表示される • 3秒に1度、タクシーの位置が更新される ◦ 動きはアニメーションで補完される 6

    注:開発中のダミー画面です 実際の車輌の位置情報は含みません
  7. GoConference’19 パフォーマンスの課題 7

  8. GoConference’19 8 アーキテクチャ

  9. GoConference’19 9 パフォーマンスに 課題 - DB に負荷のかかる処理 重い!!!

  10. GoConference’19 既存アーキテクチャの課題 - DB に負荷の高い処理 - ユーザーの近傍車輌の取得 - 営業圏ポリゴン上にあるかどうかの判定 -

    配車禁止エリアポリゴン上にあるかどうかの判定 - この処理がユーザー数に比例して発生 - ユーザーアプリは3秒に一度 API を叩く - DBはスケールしにくいので、システム全体のスケーラビリティに課題 - 突発的なアクセス増加に耐えられない - DBのインスタンス代が高い 10
  11. GoConference’19 解決策 11

  12. GoConference’19 MQTT 3秒に1度 publish 位置情報、タクシーの状態等 - 無駄な計算を抑制したい - 車輌の情報が更新されたタイミングで、ポリゴン上にあるかどうかの判定したい -

    ユーザー数に応じて、オートスケールしたい - 車輌台数に応じて、オートスケールしたい - 車輌台数も一定の規模ある。対応地域拡大で一気に増える - 車輌搭載のAndroid 端末から、タクシーの位置やステータス ( e.g. 空車かどうか )が、3秒に一度送 られてきている 新アーキテクチャでやりたいこと 12 車輌情報配信システム
  13. GoConference’19 新アーキテクチャ方針 - アプリ→API間 - アプリはAPI のポーリングはやめる - アプリはAPI と常時接続する

    Push 型 API とする - アプリは配車可能な車輌のみを API から Push され受けとる - 車輌情報配信システム→API間 - 必要最小限の回数のみ、ポリゴン判定したい - こちらも常時接続型API とし、車輌情報が送られてきたら、配車可能なもののみ配信する - ユーザ側と車輌側でスケールの観点が異なるので、APIのプロセスを分け、それぞ れ別にスケールさせたい 13
  14. GoConference’19 新アーキテクチャ 概要 - 新システムは、gRPC Streaming とRedis PubSubを活用する - ユーザーに、ユーザー近辺の車輌情報を配信する

    Subscribe Server - 車輌情報を受け取り、メッシュ判定等をし、配車可能なものを配信する Publish Server - 地図を1km四方に細分化し(3次メッシュ)、その領域中の車輌情報を配信する Redis PubSub の channelを用意する 14 車輌情報配信システム gRPC Streaming gRPC Streaming Publish Subscribe
  15. GoConference’19 Redis Pubsub - Redis は インメモリ KVS として有名だが、Pub/Sub 機能もある

    - メッセージのqueueing はしない。Publishする時点で、Subscribeしている クライアントにだけ伝達され る。 - 今回は Redis を Pub/Sub 機能のみ利用。データの永続化はしない。 15 source: https://aws.amazon.com/jp/pub-sub-messaging/
  16. GoConference’19 新アーキテクチャ 全体像 16 車 輌 情 報 配 信

    シ ス テ ム Cloud Load Balancing Publish Server Kubernetes Subscribe Server Kubernetes Cloud Load Balancing gRPC Publish Server Kubernetes Publish Server Kubernetes Subscribe Server Kubernetes Subscribe Server Kubernetes gRPC gRPC gRPC Publish Server 車輌台数に応じた オートスケール Redis Cluster シャード負荷に 応じてスケール Subscribe Server ユーザーに応じた オートスケール ユーザーアプリ
  17. GoConference’19 新アーキテクチャ 全体像 17 車 輌 情 報 配 信

    シ ス テ ム Cloud Load Balancing Publish Server Kubernetes Subscribe Server Kubernetes Cloud Load Balancing gRPC Publish Server Kubernetes Publish Server Kubernetes Subscribe Server Kubernetes Subscribe Server Kubernetes gRPC gRPC gRPC Publish Server 車輌台数に応じた オートスケール Redis Cluster シャード負荷に 応じてスケール Subscribe Server ユーザーに応じた オートスケール ユーザーアプリ - 必要な箇所が必要なだけスケール! - DB がボトルネックとなることを回避できた! - コスト削減!
  18. GoConference’19 gRPC Streaming について 18

  19. GoConference’19 gRPC とは - Google 製、多言語/多 Platform の RPC フレームワーク

    - クライアントは別のマシンのサーバーのメソッドをローカルオブジェクトのように直接 呼び出す - シリアライザにProtocol Buffers - コードジェネレーター - 通信は HTTP/2 経由で行う 19 19 source: https://grpc.io/docs/guides/
  20. GoConference’19 gRPC の通信方式 - 4分類 - Unary RPC - Server

    streaming RPC - Client streaming RPC - Bidirectional streaming RPC 20
  21. GoConference’19 gRPC の通信方式 - Unary RPC - Unary = 単項

    - Request : Response = (1, 1) 21 source: https://qiita.com/yuzo777/items/046910c95559cf0fff68
  22. GoConference’19 gRPC の通信方式 - Server streaming RPC - サーバーは1 Request

    に対し、複数の Response を一連の応答として返す - サーバーは、すべての応答を送り返した後、サーバー側のステータス等を返して完了 - クライアントは、すべてのサーバーの応答を受信したら完了。タイムアウトも設定可 22 source: https://qiita.com/yuzo777/items/046910c95559cf0fff68
  23. GoConference’19 gRPC の通信方式 - Client streaming RPC - 今度は、クライアントが単一のリクエストではなく、複数の一連のリクエストをサーバーに送信する -

    サーバーは単一の応答を返す - サーバーはすべてのリクエストを受け取る前に、レスポンスを返しても良い 23 source: https://qiita.com/yuzo777/items/046910c95559cf0fff68
  24. GoConference’19 gRPC の通信方式 - Bidirectional streaming RPC - リクエストとレスポンスがそれぞれ複数 -

    リクエストもレスポンスのタイミングは独立している。同期してもしなくてもよい 24 source: https://qiita.com/yuzo777/items/046910c95559cf0fff68
  25. GoConference’19 今回用いた gRPC の通信方式 25 車輌情報配信システム gRPC Streaming gRPC Streaming

    Publish Subscribe Client streaming gRPC Server streaming gRPC
  26. GoConference’19 gRPC Hello World 26

  27. GoConference’19 gRPC Hello World - Unary RPCのものをみてみましょう - proto ファイルの定義

    - proto ファイルから Go のコードを生成 - gRPC サーバー・クライアントの実装 - Server Streaming RPC の場合 27
  28. GoConference’19 proto の定義 28 source: https://grpc.io/docs/quickstart/go/

  29. GoConference’19 proto ファイルから Go のコードを生成 29 source: https://grpc.io/docs/quickstart/go/

  30. GoConference’19 source: https://grpc.io/docs/quickstart/go/ proto ファイルから Go のコードを生成 - request/response 用の

    struct 30
  31. GoConference’19 proto ファイルから Go のコードを生成 - server用の interface - client

    用のinterface 31 source: https://grpc.io/docs/quickstart/go/
  32. GoConference’19 Server Streaming RPC の場合 - Server interface - Client

    interface - Recv() が (nil, io.EOF) で サーバー側完了 32 source: https://grpc.io/docs/reference/go/generated-code/
  33. GoConference’19 今回のサーバーの実装について 33

  34. GoConference’19 Subscribe Server - ユーザーアプリが接続する API - あるエリアの配車可能な車輌情報を 3秒に一度返す -

    Server Streaming RPC として実装 - 今回はレスポンスに終わりはないので、サーバーに明示的に終了を告げる口を用意 している - 34
  35. GoConference’19 Publish Server - 車輌情報が打ち上げられるAPI - Client Streaming RPC として実装

    - 35
  36. GoConference’19 Go で良かった点 - 簡単にサーバーのCPU リソースを限界まで使うことが出来た - よしなにサーバーへの接続数を増やしても、 内部で goroutine

    が適切にスケジューリングされる - ワーカー数の調整など、特にチューニング作業することなく、限界まで CPUリソースを使うことができ た - これにより、簡単にサーバー 1台あたりのユーザー数を増やすことができた - 今回のように CPU ヘビーな API であり、 CPU 使用率でオートスケールする構成に はありがたい 36
  37. GoConference’19 インフラ構成 37

  38. GoConference’19 インフラ構成 - 方針 - なるべくマネージドなクラウドサービスを使う - 負荷に応じたオートスケール - APIサーバー

    - API サーバーのバックエンドには、 GCP の Kubernetes Engine, GKE を採用 - CPU 使用率に応じ、HPA でオートスケール - Redis サーバー - GKE 上に Redis Cluster を自前で構築・運用 - 本当は マネージド・サービスである Google Cloud Pub/Sub を用いたかったが、レイテンシーの要 件を満たせず断念 38
  39. GoConference’19 ロードバランサーについて - 課題 - オートスケールしても負荷の偏りが持続する問題 - gRPC Streaming に対応する

    クラウドの LB はどれだ問題 39
  40. GoConference’19 LB について / 負荷の偏りが持続する問題 40 Cloud Load Balancing Subscribe

    Server Kubernetes Subscribe Server Kubernetes オートスケールしても 既存の接続はそのまま持続し 新しい Pod に向き直したりしない Subscribe Server Kubernetes Subscribe Server Kubernetes Subscribe Server Kubernetes Subscribe Server Kubernetes Cloud Load Balancing オートスケール! サーバーとユーザー間の接続が持続している
  41. GoConference’19 - 課題 - オートスケールしても既存 Pod の負荷が下がらない - 接続が持続しているため、既存ユーザの接続は新しい Pod

    に向かない - 対策 - 長期間接続が発生する部分では、定期的に切断 &再接続処理を行う - 今回 - Subscribe Server → アプリの仕様として、長いコネクションは発生しない想定 - Publish Server → 定期的に切断&再接続 - Redis → 定期的に切断&再接続 LB について / 負荷の偏りが持続する問題 41
  42. GoConference’19 gRPC Streaming に対応する LB - GKE - Ingress -

    BETA - ReadinessProbe が httpGet でのみ動作する。gRPC 層でヘルスチェックできない 。 httpGet での実装・検証事例はある [1] - LoadBalancer Service - BETA ではない [2] - `protocol: "TCP"` で、L4LBとして動かす - Ingress とは違い、SSL 終端は自前で行う必要がある - 今回はこちらを採用 - ReadinessProbe で exec を利用し、gRPC 層でヘルスチェックできる 42 [1]GKE gRPC Ingress LoadBalancing https://medium.com/google-cloud/gke-grpc-ingress-loadbalancing-4b9cdbc09758 [2]https://cloud.google.com/kubernetes-engine/docs/concepts/service
  43. GoConference’19 まとめ 43

  44. GoConference’19 - gRPC Streaming + Go で スケーラブルな常時接続型 API を作った事例紹介を

    しました - 弊社のタクシー配車アプリにて、ポーリング型からPush型の常時接続APIに変更す ることで、スケール可能かつ低コスト化を達成した - gRPC Streaming をクラウドサービスで実現するためには、対応状況を見極める必 要がある - 特に LB 周り まとめ 44