知識0からgRPCに入門してみた

# 初めに
一番下の参考記事を参照しながら、自分なりにまとめたような記事になっています。
書籍などでガッツリ学べている訳ではないため、間違いや過不足があるかもです。
その場合はご指摘ください。

# gRPCの概要
- Google で開発された多言語間の**Remote Procedure Call (RPC)** を実現するプロトコル
- **IDL** としては **Protocol Buffers** が使われている
- 通信には **HTTP/2** の枠組みが利用されている
- 3タイプの**ストリーミング通信**が用意されている

## RPCとは？
ネットワーク上の別のアドレス空間に存在するプロシージャを、ローカルのプロシージャと同じように呼び出す仕組み。RPC の利用者視点ではネットワークや他の言語の知識を必要とすることなく、ただの関数として利用できる。

ちなみに、異なる言語間で RPC として呼び出せる関数を定義する場合、当然特定の言語に依らない独立の定義方法が必要になるが、それをInterface Description Language (IDL)という（下で詳しく説明している）。

通信の流れは以下のようなステップを踏む。
- リモートのプロシージャを呼び出す
- クライアント側のスタブがこの呼び出しを受け取り、リクエストをシリアライゼーション（バイナリ形式などに変換）する
- リクエストの送信
- サーバー側のスタブがリクエストを受け取り、リクエストをデシリアライゼーション（サーバー側の言語に変換）する
- デシリアライゼーションされた情報を元に、サーバ上で対応するプロシージャを実行
- 結果を再びシリアライゼーション（バイナリ形式などに変換）した上でレスポンス
- クライアント側のスタブが結果を受け取り、デシリアライゼーション（クライアント側の言語に変換）する
- デシリアライゼーションした結果をプロシージャ呼び出しの戻り値としてクライアントに返す

## IDLとは？
RPCの関数やその引数を定義する言語、言い換えると、異なるシステムや言語間のインターフェースを定義するための言語のこと。
加えて、IDLにはコンパイラが付属し、IDLで定義したインターフェースに基づいて特定の言語向けのクライアントやサーバのコードを生成してくれる。例えば以下のようなものを生成する。
- 定義したメッセージやデータ型に基づいた各言語のデータ構造（クラスや構造体）
- シリアライゼーション・デシリアライゼーションのメソッド
- クライアントとサーバのスタブ
- etc…

## **Protocol Buffersとは？**

Googleが開発し、オープンソースとして公開しているIDLで、以下の特徴を持つ。
- スキーマ言語（データ構造やフォーマットを明示的に定義するための言語）の一種で簡潔明瞭に書ける
- バイナリベースのエンコーディングを使用するため、シリアライゼーション/デシリアライゼーションが速い、かつ生成されたデータが小さいため転送と保存が効率的
- 仕様が小さいので、実装間の意図せぬ非互換性で苦しめられることが少ない
- Protocol Buffersのversion 3からは、Jsonとの相互変換の規則が仕様として定められている

## **HTTP/2とは？**

World Wide Webのための第二世代のHTTPネットワークプロトコルであり、2015年にHTTP/1.1の後継として公式に発表された。

ウェブページの読み込み速度を向上させることを主な目的として設計されており、以下のような特徴を有する。

**多重化ストリーム**
1つのTCP接続を用いて複数のリクエスト/レスポンスのやり取りをできる（1つのTCP接続上にストリームと呼ばれる仮想的な接続を複数個作成する）ため、サーバーの負荷低減に繋がる

**バイナリプロトコル**
バイナリベースのプロトコルで、ヘッダの解析や生成が効率的になる

**ヘッダ圧縮**
ヘッダデータを圧縮でき、データ転送のオーバーヘッドを削減できる

**サーバプッシュ**
サーバは、クライアントの明示的なリクエストなしにリソースをクライアントにプッシュすることができ、ウェブページのレンダリングがさらに高速化する

**ストリームの優先順位付け**
クライアントは、特定のリソースの取得を他のリソースよりも優先することができるため、重要なリソースがより早くロードされ、ページの可視性が向上する

## ストリーミング通信とは？

持続的な接続の上で複数個のデータを継続的に送る通信方式。言い換えると、リクエストとレスポンスが1対1の関係ではなく、一方向あるいは双方向に複数のメッセージを連続的に送受信できるようにするもの。

gRPCのストリーミング通信には、以下の3つのタイプがある。詳しくは[この記事](https://zenn.dev/hsaki/books/golang-grpc-starting/viewer/stream)など参考にすると良いと思います。
- Server streaming RPC（単方向サーバーストリーミング）
- Client streaming RPC（単方向クライアントストリーミング）
- Bidirectional streaming RPC（双方向ストリーミング）

:::message
HTTP/2にも「ストリーム」という用語が存在するが、それは「一対のリクエスト-レスポンスのやり取りをするために使う仮想通信路」という意味であって、gRPCでのストリームとは意味合いが全く別なので注意が必要。
:::

# gRPCの利点
- 通信が高速（RESTの7〜10倍）
- ストリーミング通信が可能
- API設計/仕様の定義が楽
- コードの自動生成による実装コストの削減
- 特定の言語に依存しない
- 様々な要求（デッドライン/タイムアウト、キャンセル、エラー処理、認証、ロードバランシング、ログ、モニタリング等）に柔軟に対応できる

# gRPCの欠点
- デバッグしずらい（シリアライズされるため）
- ブラウザから直接gRPCサービスを呼び出すことはできない（gRPC-webという別のプロジェクトを使用しない限り）
- HTTP/2に依存している（grpc-gatewayでHTTP1.1対応可）
- Swagger EditorのようなIF仕様を分かりやすく表示するツールがない
- gRPC関連のパッケージを複数インストールする必要がある
- 受信側はデータをデシリアライズする必要がある

# gRPCの使い所

**マイクロサービス間の通信**

マイクロサービスが要求する以下の問題にgRPCは対応できるため。
- 高速な通信（APIリクエストが頻発しパフォーマンスが劣化しやすいため）
- 全てのシステム間でのインターフェースの統一
- 複数言語への対応

**モバイルユーザが利用するサービス**

- モバイルデバイスのような制約のある環境下（低帯域幅や不安定なネットワーク）にも強いため
- シリアライズやHTTP/2によってデータ量が減り、モバイル端末の通信量制限に貢献できるため

**リアルタイム通信やストリーミングが必要なシステム:**

gRPCはストリーミング通信をサポートしているため、リアルタイムでのデータのやり取りや、長期間の接続を維持しつつデータを交換するようなシナリオなどに適している。

etc...

# 参考資料

- [で、結局gRPCってなんやねん | MattyLogs](https://mattylogs.com/blog/9/)
- [いまさらだけどgRPCに入門したので分かりやすくまとめてみた - Qiita](https://qiita.com/gold-kou/items/a1cc2be6045723e242eb)
- [そろそろ知っておきたいHTTP/2の話 - Qiita](https://qiita.com/mogamin3/items/7698ee3336c70a482843)
- [gRPC と従来の REST API の比較](https://www.integrate.io/jp/blog/grpc-vs-rest-how-does-grpc-compare-with-traditional-rest-apis-ja/)
- [gRPCで実現できるストリーミング処理｜作ってわかる！ はじめてのgRPC](https://zenn.dev/hsaki/books/golang-grpc-starting/viewer/stream)