こんにちは。クルーズ株式会社CTOの鈴木です。
今回はFlutterアプリ⇔APIサーバ間の通信をgRPC対応した話についての事例紹介です。
gRPCってなに?
平たくいうとGoogleが開発したネットワーク越しに別のコンピュータの関数を呼び出す仕組み (Remote Procedure Call)です。
イメージとしてはHTTPみたいな感じでクライアントからサーバ上にある関数、APIを呼び出すものなのでものすごくシンプルに見ればHTTPみたいな通信プロトコルの一種でHTTPよりいろいろ優れているものだと思ってもらえればいいと思います。
gRPCってどんな特徴があるの?
以下、わかりやすくするためにかなり簡略化して文章を書いているため、厳密にいうと個々の解釈違くない??といった部分はおそらくありますのでご容赦ください。
HTTPプロトコルでRestAPIを実装した場合と比較した場合、gRPCでは主には以下のような特徴があります。
① 双方向通信ができる(HTTP2準拠である)
② バイナリシリアライズ
③ インターフェース定義をIDLという言語で記述しそれを再利用できる
① 双方向通信ができる(HTTP2準拠である)
gRPCではクライアントとサーバ間で以下の4方式の通信方式があります
・Unary RPC (1リクエスト1レスポンス)
・Server streaming RPC (1リクエスト複数レスポンス)
・Client streaming RPC (複数リクエスト1レスポンス)
・Bidirectional streaming RPC (複数リクエスト複数レスポンス)
一方、HTTPのREST APIの場合は原則として1リクエスト1レスポンスです。
② バイナリシリアライズ
HTTPのREST APIの場合、原則レスポンスはテキストで、JSON、XML、YAMLなどでシリアライズされているのに対し、gRPCの場合レスポンスはバイナリとなります。
これはテキストと比べると容量が小さくできることが期待できます。
③ インターフェース定義をIDLという言語で記述しそれを再利用できる
gRPCではAPIの定義を.protoファイルというインターフェース定義言語で記載します。
そしてこの.protoはクライアントやAPI Docと共通で使用でき、バリデーション実装やAPI設計にかかるコストの短縮が期待できます。
gRPCの実装ってどんな言語があるの?
以下公式Documentの記載によると、2022年7月時点では以下の言語に対応しているようです
実はgRPCはPHPに対応していない
公式DocによるとPHPに対応しているように見えるのですが、正確にいうと
PHP をClient としてgRPC通信が可能、ただしServerにはなれないということがわかりました。
ただ、以下文献にもあるように現時点ではできるようです。
但し導入の意思決定を行った当時はPHP でのサーバ実装は実現できていなかったため、以下のような構成によって実現をしました。
アプリとPHPサーバ間をgRPCさせる方法
まずどのようにgRPC通信させるかよりもgRPC通信によって得たいことを整理を行いました。
マストで実現したいこと
・通信量の圧縮
・通信量の圧縮の結果としてのレスポンスタイムの向上
・Unary RPC (1リクエスト1レスポンス)通信の実現
マストじゃなくてもよいもの
・Unary RPC (1リクエスト1レスポンス)以外の通信の実現
※現時点で明確にメリットのあるユースケースがあまりないため。
したがって、最も簡単にできるProxy をアプリとAPIサーバ(PHP実装)の間に挟む形での設計としました。
通信要件を整理すると
①アプリはGRPC Proxyサーバに対してgRPCで通信を行う。
②gRPCプロキシサーバがアプリからの通信をプライベートネットワーク内の
Webサーバ(PHP)にHTTPプロトコル:80で転送する。
というものです。
これだけ見ると、WebサーバがmsgPackなどのバイナリシリアライズでアプリに返せばいいじゃんと思うかもしれません。
この指摘に対してはそのとおりです。今回この構成にした意図としては
・将来的にServer streaming RPC (1リクエスト複数レスポンス)を実現するための検証
・APIサーバとその他のAPIサーバ間通信をgRPC対応させるための検証
を兼ねているためです。
gRPCサーバ実装にC#を採用した理由
gRPCサーバの実装にさまざまな言語がある中で当社がC#を採用した理由は以下でした
・サーバサイド、コンテナの技術スタックとして既にあったものがC#とnode.js
・開発当時reflection機能やgzip圧縮機能がnode.jsでは未実装だった
上記のためC#実装にて行い、Kestrel上でASP.NET CoreアプリケーションとしてgRPC プロキシを実装しそれをAWS Fargateで運用する形としました
実際のアプリtoサーバ間通信
gRPCサーバに万が一の不具合があっても従来のHTTPのREST API の通信に戻せるように、まずはアプリ起動時にHTTPのREST APIでサーバの現状のプロトコルをを取得するAPIを叩きに行き、レスポンスがHTTP通信を示すものなら従来のHTTPのREST APIでサーバに通信、gRPCを示すものならgRPC Proxyに通信する方式としています。
導入してみて
APIレスポンス容量のみで比較すると約1/10に圧縮がされ、サーバレスポンスとしては平均で約3~4割向上しました。
APIレスポンス容量についてはバイナリシリアライズに加え、gzip圧縮の効果もかなり大きいのではないかと考えています。
理由としてはECサイトのAPIの場合、商品サムネ画像や関連商品画像など、APIレスポンス中に前方一致するURLが多いため、その共通部分が圧縮化されたことにより、レスポンスサイズが下がったことが主要因だと考えています。
今後について
今個人的に注目しているのがRoad Runner というGoLangで実装されたPHPサーバです。
このアーキテクチャはHTTPやgRPCをGoLangで受けてPHPをWorkerとして実行するものです。
実現性などまだ議論できるほど検討できてはいないですが、時間をとり検証していこうと思っている技術の一つです。