目次

はじめに

対象環境
ソースコードについて
ご意見やご質問など
免責事項

第1章 gRPCとRESTの違い

1.1 gRPCとは?
1.2 RESTと比較した場合のgRPCの長所
1.3 RESTと比較した場合のgRPCの短所
1.4 スキーマファースト開発による生産性の高さが最大の長所

第2章 .protoファイルを書いてみよう

2.1 Potocol Buffers
2.2 バージョン
2.3 パッケージ定義
2.4 import
2.5 サービスとRPCメソッド定義
2.6 スカラー型
2.7 メッセージ型
2.8 repeated / リスト(配列)
2.9 列挙型
2.10 マップ(連想配列)
2.11 oneof
2.12 Well Known Types
2.13 コメント
2.14 各言語で実際にどのようなコードが生成されるか

第3章 Go言語でつくるgRPCサーバー

3.1 単項RPC
3.2 スキーマファースト
3.3 .protoファイルを作る
3.4 Go言語でのサーバー側の実装と解説
3.5 CLIツールで動作確認する

第4章 RailsアプリケーションからgRPCサーバーにアクセスする

4.1 クライアントの構成と環境構築
4.2 protoコンパイラでコードを自動生成する
4.3 自動生成されたコードを呼び出すモデルを追加する
4.4 gRPCのサービスに接続する
4.5 RPCメソッドを呼び出す
4.6 動作確認
4.7 まとめ

第5章 インターセプタでログや認証を追加してみよう

5.1 インターセプタとは?
5.2 自作のインターセプタを追加する
5.3 go-grpc-middlewareを使う
5.4 インターセプタでログを出力する
5.5 インターセプタで認証を追加する

第6章 単方向ストリーミングでつくる画像アップロードAPI

6.1 ストリーミングを使うgRPCアプリケーションの設計
6.2 Goでつくる画像アップロードサーバー
6.3 Rubyでつくる画像アップロードクライアント
6.4 まとめ

第7章 双方向ストリーミングでつくるリアルタイムリバーシ

7.1 リアルタイムリバーシの要件
7.2 アプリケーションの構成
7.3 リアルタイムリバーシの基本ロジック
7.4 .protoファイルでスキーマ定義する
7.5 buildパッケージ
7.6 Matchingサービスのサーバーとクライアント
7.7 Gameサービスのサーバーとクライアント
7.8 main関数の実装
7.9 リアルタイムリバーシで遊んでみよう
7.10 まとめ

付録A Google APIに学ぶprotoスタイルガイド

A.1 Googleの推奨規則
A.2 インタフェース名
A.3 メソッド名
A.4 メッセージ名
A.5 列挙型
A.6 フィールド名
A.7 Google以外の事例

付録B Protocol Buffersの自動コード生成仕様(Go編)

B.1 はじめに
B.2 コンパイラ呼び出し
B.3 パッケージ
B.4 メッセージ型
B.5 フィールド
B.6 列挙型
B.7 拡張機能 (proto2)
B.8 Service

付録C Protocol Buffersの自動コード生成仕様(Ruby編)

C.1 はじめに
C.2 コンパイラ呼び出し
C.3 パッケージ
C.4 メッセージ型
C.5 フィールド

あとがき

はじめに

 gRPCはGoogleが開発した高速なAPI通信とスキーマ駆動開発を実現するRPCフレームワークであり、マイクロサービス間の内部通信を実現する有力な選択肢として活用されはじめています。

 本書ではサーバー側(Go)/ クライアント側(Ruby)と異なる言語を用いて、いくつかのサンプルアプリケーションを実装しながら、gRPCとRESTの違い、Protocol Buffersにおけるスキーマの文法、単純なRPCから双方向ストリーミングRPCまでのgRPCにおける基本的な実装方法などを平易に説明します。

対象環境

 本書のプログラムは次の環境で作成・確認しています

 ・OS:macOS 10.14.6

 ・Go:1.13.4

 ・Ruby:2.6.2

 macOS以外でも動作することが期待されますが、動作確認などはしておりません。また環境構築を行う箇所でも、macOS以外での方法に言及する場合もありますが、基本的にはmacOSを中心に解説しています。あらかじめご了承ください。

ソースコードについて

 本書で使用したサンプルコードはGitHub上で公開されています。ぜひお手元で実際に動かして確認してみてください。

 ・2-4章 https://github.com/gami/grpc_example

 ・5章 https://github.com/gami/grpc_example/tree/chapter_5

 ・6章 https://github.com/gami/grpc_image_upload_example

 ・7章 https://github.com/gami/grpc_realtime_reversi

ご意見やご質問など

 誤植や不正確な表現などありましたら、著者のTwitterアカウントでサポートしますので、お問い合わせください。読んでいて不明な点などあった場合なども、遠慮なく質問いただければと思います。

 ・https://twitter.com/gami

免責事項

 本書に掲載された内容は、情報の提供のみを目的としています。したがって、本書を用いた開発、制作、運用は、必ずご自身の責任と判断によっておこなってください。掲載内容の瑕疵により何か悲しい出来事が起こった場合でも著者は責任を負うことができません。

第1章 gRPCとRESTの違い

1.1 gRPCとは?

 gRPCはRPCフレームワークのひとつです。RPCはRemote Procedure Callの略で、逐語的に訳すと「遠隔手続き呼び出し」となります。すなわち、「あるプログラムがネットワーク上の異なる場所に配置されたプログラムを呼び出して実行すること」を指します。

1.1.1 gRPCの歴史

 2004年ごろから、GoogleはStubbyという名前のRPCフレームワークを、自社データセンター内で活用してきました。StubbyはGoogleの各マイクロサービス間において、秒間100億リクエスト以上の処理をさばいていました1が、HTTP2などの標準規格の登場により、汎用的な規格にあわせてつくり直すことで、モバイルやIoT、クラウドなどのユースケースに対応していくべき2と判断されるようになりました。そのような流れの中で、2015年2月3にオープンソースのプロダクトとして公開されたものがgRPCです。

 gRPCとしての歴史は5年を超えたくらいなので、まだ歴史が浅いように見えるかもしれません。しかしStubby時代も含めると、15年以上に渡ってGoogleという世界最大の分散システムを支えてきています4。この点から考えると、gRPC自体の運用実績はすでに十分にある5といえるでしょう。

1.1.2 そもそもRPCとは?

 Remote Procedure Callを訳すと遠隔手続き呼び出しという意味になります。すなわち、あるサービスから別のサービスのアプリケーションの処理(サブルーチン/クラス/関数など)を呼び出す技術です。RPCを使うことで、違うアプリケーションのロジックを、あたかも自分のアプリケーションの中に実装されているかのように扱うことができます。RPCは1976年にサン・マイクロシステムズがNFS(分散ファイルシステム)の一部で使うために開発した、とても歴史のある技術です。RPCでよく使われている技術には、gRPC以外にもJSON-RPC6やSOAP7、Apache Thrift8などがあります。

1.1.3 RESTとgRPCの比較

 WebAPIによく使われるRESTも、離れたサーバーの機能を呼び出すという点で、RPCと同じ役割で使われるように思えます。ただし、RESTには「リソース志向を強く打ち出している」という思想的な違いがあります。リソース志向とはリソース(オブジェクト)を中心に考え、これに対してHTTPメソッドで操作していく考え方です。RPCではメソッドの呼び出しが基点となり、データはあくまでその副産物であるので、考え方としては逆になります。この志向性の違いから、RESTとRPCは対比的に捉えられています。

 RESTでは、一般的にはJSONがよく使われます。この点はRESTとRPCの比較になりうるでしょうか?

 RPCにも前に述べたようにJSON-RPCがあるので、レスポンスにJSONを使うかどうかはRESTとRPCの違いにはなりません9。つまり少なくともフォーマットという点では、RPCとそれ以外のものを区別する特徴にはならないと考えられます。

 RESTは規格が厳格に決められたものではなく、シンプルでスケーラブルなAPIをつくるための「設計原則10」です。そのため、RESTでは原則に沿って自分で仕様を決めて実装することが求められます。一方RPCフレームワークは、規格や仕様に沿って実装されたライブラリーやフレームワークとして提供されます。RESTはかなり普及した技術なので、原則にしたがってきちんと設計されたREST APIは、多くの開発者の参加を容易にします。RPCフレームワークの場合は、そのフレームワークを学習するというところが、まず課題になるかもしれません。

 次節以降では、RESTと比較してgRPCにどのような長所や短所があるのかという点から、gRPCとRESTの違いについて具体的に解説していきたいと思います。

1.2 RESTと比較した場合のgRPCの長所

 gRPCの長所から見ていきます。gRPCの優れた点として大きく3つの点が挙げられます。

 ・HTTP/2による高速な通信

 ・Protocol Buffers

 ・柔軟なストリーミング形式

 順に解説していきます。

1.2.1 HTTP/2による高パフォーマンス

 gRPCではHTTP/2のプロトコル上で通信が行われます。そもそも、HTTP/2は既存のHTTP/1.1と比較して、何が違うのでしょうか。

 まず、HTTP/2では通信時にデータがテキストではなくバイナリにシリアライズされて送られます。そのため、小さな容量で転送でき、ネットワーク内のリソースをより効率的に使用することができます。また、HTTP/2ではひとつのコネクションで複数のリクエスト/レスポンスをやり取りできます。そのためgRPCでも、コネクションは常時張られっぱなしの状態になります。リクエストの度に接続と切断をおこなう必要がなく、またヘッダーを都度送る必要がないので、より効率的な通信になります。

 この高速であるという点において、gRPCはマイクロサービスの内部APIの通信規格として、評価されています。マイクロサービスでは、複数のサービスがそれぞれのAPIを通じて緊密な連携を行う必要があるため、プライベートなAPIへのリクエストが増える傾向にあり、それぞれの応答速度の遅延が、サービス全体のボトルネックになりがちだからです。

1.2.2 Protocol Buffersによるデータ転送

 gRPCではProtocol Buffersのフォーマットにシリアライズしてデータをやり取りします。シリアライザ自体はカスタマイズすることでJSONなどのレスポンスに変えることもできます11

 Protocol BuffersもgRPCと同様にGoogleが開発しました。こちらはgRPCよりも少し早く2008年にオープンソース化されました。バイナリ形式なので、そのような意味ではMessagePack12に似ているといえるかもしれません。

 Protocol Buffersの一番の特徴は.protoファイルというIDL(インタフェース記述言語)です。.protoファイルを書いて、コンパイラを実行すると任意の言語のサーバー/クライアント用コードを自動生成してくれます。自分でAPIインターフェースを実装したり、シリアライズされたデータのエンコード/デコード処理を書く必要はありません。

 このためgRPCで開発する場合は、まずスキーマを書くことになります。API側とクライアント側でそれぞれ別の開発者が担当しているような状況下で、API仕様書が無かったり、もしくは仮に仕様書が存在しても古いまま更新が遅れてしまったことで、開発に支障が生じてしまったという経験は珍しくないと思います。

 gRPCではスキーマが最初に書かれるので、.protoファイルを見ればAPIの仕様は常に明確です。そのため、必然的にスキーマファーストの開発を行うことになります13

 また.protoファイルでは静的型付けを行います。IDLで記述された仕様は、各言語のコード生成時に適切な型へ変換されます。

 言語はC++JavaPythonGoRubyPHPC#Node.jsAndorid JavaObjective-CDartブラウザ上でのJavaScript(gRPC Web)に公式対応しています。

 Protocol BuffersはgRPCの最大のメリットと考えてよいかと思います。

図1.1: gRPCの流れ

1.2.3 柔軟なストリーミング方式

 gRPCではAPIで一般的な、ひとつのリクエストに対してひとつのレスポンスを送る形式以外に、単方向/双方向のストリーミングRPCに対応しています。この節ではgRPCが対応している4つのRPC方式について説明します。

1.2.3.1 シンプルなRPC

 クライアントから送られたひとつのリクエストに対して、レスポンスを一度返して終了する、もっとも一般的な形式です。同期的に処理できるので、サーバーやクライアントの実装もシンプルなものになります。14

1.2.3.2 サーバーストリーミングRPC

 クライアントから送られたリクエストに対して、レスポンスを複数回に分けて返します。時間のかかる処理について、非同期的にレスポンスを返すことができます。

 クライアントからのリクエスト後、クライアントは待機状態とし、サーバーの状態変更に応じて、情報を随時プッシュしていくこともできます。この形式はお知らせ通知やタイムラインのリアルタイム更新などに有効です。

 サーバー側の状態変化をクライアントで検知したい場合にストリーミングが無いと、定期的な通信(ポーリング)をクライアントから行なう必要があり、無駄な通信が多く発生してしまいます。サーバーストリーミングRPCを使えば、サーバー側から状態変化時にクライアントに直接伝えることができるので、ネットワーク負荷を最小化することができます。

1.2.3.3 クライアントストリーミングRPC

 クライアントからリクエストを分割して送り、サーバーは全てのリクエストを受け取る前に処理を逐次開始できます。サーバー側は全てのリクエストを受け取ってからレスポンスを返します。

 大きなデータを分割してアップロードしたい場合などに有用です。

1.2.3.4 双方向ストリーミングRPC

 クライアントから初めのリクエストが送られた後、サーバー・クライアントどちらも任意のタイミングでリクエスト・レスポンスを送ることができます。まず思いつくユースケースとしてはチャットやゲームなどでしょうか。

 REST APIではストリーミングが行えないので、WebSocketサーバーを別途立てる必要などがありました。gRPCの場合、単一のサーバーで双方向通信が必要なAPIとしても対応可能です。

1.3 RESTと比較した場合のgRPCの短所

 これまでgRPCの優れた部分について記述してきました。しかし、現状ではgRPCを導入する際に支障になるであろうことも少なからずあります。

1.3.1 HTTP/2非対応である危険性

 現時点(2020年3月)において、AWSのALB(Application Load Balancer)はgRPCに対応していません15

 HTTP/2のリクエストを受け付けることはできますが、バックエンドのサーバーへの転送をHTTP/1で行うためです。同様に通信経路上でHTTP/2に非対応なサービスがある場合に、gRPCを使う上で問題が生じる場合があります。

 NginxがgRPCをサポートする16など少しずつ対応環境は増えつつありますが、枯れたHTTP/1.1の上で行われるRESTの通信と比べて、HTTP/2に関連した地雷を踏みやすいことは否めません。

1.3.2 ブラウザの対応状況が不十分

 2018年11月にgRPC-Webが正式リリースになりました17。ただしEnvoyなどを使ってProxyサーバーを立てる必要があったり、双方向あるいはクライアントストリーミングRPCに非対応であるなど、まだまだ十分な対応状況とはいえないようです18。フロントエンドのアプリケーションとの通信に使うにはまだ少し早いかもしれません。

 REST APIの方が、JSONとの親和性の高さもありSPA19との相性はよいといえます。

1.3.3 言語によって機能の実装状況にばらつきがある

 前に.protoファイルからgRPCクライアント/サーバー用のボイラープレートコード生成に、多くの言語で公式対応していると述べました。ただし生成に対応しているとはいっても、各言語でのgRPCの実装状況はまちまちであり、使える機能には差があります。

 たとえば、GoとJavaにはクライアントロードバランシングの機能が実装されていますが、Rubyにはありません20。インターセプタなどの機能もほとんどの言語で対応しているものの、RubyやPHPなどではExperimentalとされており、実運用時には注意が必要です。GoやJavaといった言語と比べて他の言語(特にgRPC Coreの実装を待って機能が追加される言語)は機能追加が遅くなる傾向にあります。

1.3.4 バイナリにシリアライズすると人間が読めない

 JSONにシリアライズされたフォーマットを返すAPIであれば、ローカル開発中にcURLやブラウザなどで叩いて、簡単に確認することができます。REST API開発中は出力を目視で確認することも多いと思います。

 gRPCの場合は専用のクライアントをインストールする必要があります21

1.3.5 RESTも十分速い

 ベンチマーク22によると、gRPCはREST(JSON)に比べて確かに速いのですが、何倍も改善できるほど劇的に速いわけではありません。このベンチマークでは並列リクエストかつ高負荷な状態でも40%の高速化であり、低負荷なテストの場合はRESTの方が速い結果になることもありました。

 Webアプリケーションのパフォーマンスチューニングでは考慮すべき点は多数あり、慣れないフレームワークの導入に工数をかけるよりも、たとえばデータベースやキャッシュ戦略などに注力する方が大きな改善を得られる可能性はあります。

1.4 スキーマファースト開発による生産性の高さが最大の長所

 gRPCの長所と短所を振り返ってみましょう。

 これまで述べてきたように、gRPCには次の利点があります。*HTTP/2による軽量かつ高速な通信*Protocol Buffersによる型安全なコード自動生成*柔軟なストリーミング形式に対応

 一方でRESTに慣れている開発者も多く、環境によってはRESTを選択するケースの方が有効な場合も、現状ではまだあるといえます。

 その上で筆者がgRPCを推すもっとも大きなポイントはProtocol Buffersによるスキーマファースト開発の生産性です。

 速度が求められるような環境や、ストリーミングが求められるユースケースは常にあるわけではありませんが、スキーマを最初に書き、ボイラープレートコードを自動生成してから開発を始めるという流れは、どのようなチームでも非常に有益です。

 ケースバイケースで考えてRESTを選択する場合もまだありますが、うまく適用できそうな要件には、ぜひ積極的に検討してみていただきたいと思います。

 次章以降では、.protoファイルの書き方について解説していきます。

コラム gRPCは何の略?

 gRPCのRPCはRemote Procedure Callです。

 ではgは何でしょう?

 Googleのgでしょうか?

 実はリリースバージョンによって、先頭のgの意味は違います。

 v1.0ではgはgRPCの略です。そして、1.19ではgoldの略です。この原稿執筆時点で、1.28まで決められています。

 gの意味について、表1.1にまとめてみました。それぞれのgの由来は公開されていませんが、単語の意味や固有名詞、gRPC開発者リストなどからそれらしいものを調べています。

表1.1: 各バージョンにおけるgRPCのg
バージョン 意味 日本語訳
1.0 gRPC gRPC
1.1 good よき
1.2 green 緑の。gRPCのイメージカラーはエメラルドグリーン。
1.3 gentle やさしい
1.4 gregarious 草などがわらわら生えてる様子
1.6 garcia gRPC開発者のDavid Garcia Quintasと思われる。
1.7 gambit (チェスなどの)初手
1.8 generous 思いやりがある
1.9 glossy つやつやしている
1.10 glamorous 豊満で魅力的な
1.11 gorgeous 豪華な。なおTwitterアンケートでgoawayに決まったが無視された。
1.12 glorious 栄光ある。
1.13 gloriosa 熱帯に生える花の名前で球根に毒がある。
1.14 gladiolus 花の名前(グラジオラス)で根は湿布の材料になる。
1.15 glider (滑空する)グライダー。速そうである。
1.16 gao gRPC開発者のYang Gaoと思われる
1.17 gizmo 映画グレムリンにでる始めだけ可愛い生き物。
1.18 goose ガチョウ
1.19 gold 金(たまにガチョウから出てくる)
1.20 godric ハリーポッターに出てくる古の魔法使いゴドリック・グリフィンドール
1.21 gandalf 指輪物語に出てくる魔法使い
1.22 gale 疾風
1.23 gangnam 韓国ソウル特別市江南区。江南スタイルで有名。
1.24 ganges ガンジス川
1.25 game ゲーム
1.26 gon 角度の単位か、あるいはHunter × Hunterの主人公
1.27 guantao gRPC開発者のGuantao Liuと思われる
1.28 galactic 銀河のように大きい


1. https://cloud.google.com/blog/products/gcp/grpc-a-true-internet-scale-rpc-framework-is-now-1-and-ready-for-production-deployments

2. https://grpc.io/blog/principles/

3. https://developers-jp.googleblog.com/2015/03/http2-rpc-grpc.html

4. https://twitter.com/bernardgolden/status/1108771530455031810

5. Google以外でも海外でNetflixやSquare、国内でLINEやメルペイなどさまざまな企業で使われています

6. https://www.jsonrpc.org/

7. https://www.w3.org/TR/soap12-part0/

8. https://thrift.apache.org/

9. RESTAPIのレスポンスとして、テキストではなく、後述するProtocol Buffersを使うこともできます

10. URIによるリソース志向、HTTPメソッドによる操作、ステートレスであることなどがあります

11. https://qiita.com/yugui/items/238dcdb75cd40d0f1ece

12. バイナリ形式の軽量高速なシリアライズフォーマット。複数言語に対応しており、fluentdで使われていることで知られています。

13. RESTにもOpenAPIという仕様があり、これを使うことスキーマファーストの開発を行うことは可能ですが、gRPCのようにサーバー側とクライアント側の両方のインターフェースを自動生成することは難しく、限界があります。

14. この形式はUnary(単項)RPCとも呼ばれます

15. NLBを使えば可能なので、AWS上でマネージドなロードバランサーが使えないわけではありません。またEnvoyやECS Service Discoveryなどを活用することで、AWSでもgRPCを使った運用は可能です。

16. https://www.nginx.com/blog/nginx-1-13-10-grpc/

17. https://www.cncf.io/blog/2018/10/24/grpc-web-is-going-ga/

18. https://qiita.com/Morix1500/items/7d9a152f8f12b73a1cf4

19. Single Page Applicationの略。React.jsやVue.jsなどで最近はよく作られます。

20. v1.27.0現在。gRPC Coreには入っているので、いずれ使えるようになる可能性はあります

21. 第3章で解説します

22. https://www.yonego.com/nl/why-milliseconds-matter/



第2章 .protoファイルを書いてみよう

 gRPCではシリアライズフォーマットとしてProtocol Buffersを使います。Protocol Buffersでは.protoを拡張子としてもつファイル上にスキーマ定義を行い、protocコマンドで各言語用のコードを生成します。したがって、gPRCでもこの.protoファイル上に、Protocol Buffersの仕様に基づいてスキーマ定義をおこなう必要があります。

 この章では、gRPCのAPI定義ファイルとなる.protoファイルの書き方を解説します。

2.1 Potocol Buffers

 Potocol Buffersの最新バージョンは本書執筆時点(2020年3月)で3.11.4です。2016年に登場したv3で破壊的な変更が行われており、以降のバージョンについてproto3と呼ばれています。安定性の面からも、それ以前のproto2ではなくproto3を使うことが推奨されます。

 説明用のサンプルとして、簡単な.protoファイルをひとつ用意してみました。電話帳検索のAPIを想定しています。

リスト2.1: proto3ファイルの例

syntax = "proto3";

package myapp;

service AddressBookService {
  rpc Search (SearchRequest) returns (SearchResponse);
}

message SearchRequest {
        string name = 1;
}

message SearchResponse {
        Person person = 1;
}

message Person {
        int32 id = 1;
        string name = 2;
        string email = 3;
        repeated PhoneNumber phone_numbers = 4;

        enum PhoneType {
                UNKNOWN = 0;
                MOBILE = 1;
                HOME = 2;
                WORK = 3;
        }

        message PhoneNumber {
                string number = 1;
                PhoneType phone_type = 2;
        }
}

 このサンプルを使って、順に文法を解説していきます。

試し読みはここまでです。
この続きは、製品版でお楽しみください。