gRPCとは?スタブ・チャネルの仕組みからREST APIとの違いまで
gRPC(gRPC Remote Procedure Call)は、Googleが開発したオープンソースのRPC(Remote Procedure Call)フレームワークです。HTTP/2の上で、Protocol Buffersというバイナリ形式のデータをやり取りし、別のサーバー上の関数をローカル関数のように呼び出せます。この記事を「gRPC スタブ チャネルとは」で読み始めた人が多いことから分かるとおり、gRPCを理解する鍵はチャネル(接続)とスタブ(自動生成される呼び出し口)の2語にあります。まずこの2つを押さえ、続いて通信方式・REST APIとの違い・使いどころまで、実際のコードで確認していきます。
まとめ:先に結論
- gRPCはGoogle製のRPCフレームワーク。HTTP/2+Protocol Buffers(バイナリ)で、多言語・低レイテンシの通信を実現する。
- チャネル(Channel)はサーバーへの仮想的な接続、スタブ(Stub)は
.protoから自動生成されるクライアント側の代理オブジェクト。開発者は「チャネルを作る→スタブを作る→スタブのメソッドを呼ぶ」だけでリモート呼び出しができる。 - 通信方式は4種類(Unary/サーバーストリーミング/クライアントストリーミング/双方向ストリーミング)。
- REST(JSON+HTTP/1.1)より高速で契約が厳密だが、ブラウザから直接叩けず人間可読でもない。
- 使いどころは社内マイクロサービス間・多言語連携・ストリーミング。ブラウザ向けの公開Web APIはRESTのほうが無難。
以下、それぞれを具体的に見ていきます。
gRPCとは:RPCフレームワークとしての位置づけ
RPC(Remote Procedure Call)は、ネットワーク越しの処理を「自分の関数を呼ぶのと同じ書き方」で実行する仕組みです。gRPCはそのRPCを、2つの技術で現代的に作り直したものと考えると分かりやすいです。
- Protocol Buffers:やり取りするデータの構造を
.protoファイルで定義し、バイナリにシリアライズする。JSONのようなテキストより小さく・速い。 - HTTP/2:1本の接続で複数リクエストを多重化(マルチプレキシング)し、ストリーミングやヘッダー圧縮に対応する。
この「.protoで契約を先に決め、通信路はHTTP/2」という設計が、後述するスタブ・チャネル・4つの通信方式すべての土台になります。gRPCは社内のサービス間通信基盤として広まり、マイクロサービスとは?モノリスとの違い・メリット・デメリットと選び方で解説するようなアーキテクチャで採用されることが多いプロトコルです。
gRPCのスタブとチャネルとは
gRPCで最も多く検索される疑問が「スタブとチャネルとは何か」です。役割が明確に分かれているので、ここを押さえるとgRPCのコードが一気に読めるようになります。
チャネル(Channel)とは:サーバーへの仮想接続
チャネルは、クライアントとサーバー間の接続を抽象化したオブジェクトです。内部でHTTP/2コネクションを張り、複数のRPCを多重化して流します。1つのチャネルは複数のスタブで共有でき、接続の再利用やロードバランシングもチャネルの責務です。
import grpc
# サーバーへのチャネル(接続)を1本作る
channel = grpc.insecure_channel('localhost:50051')
Goでは、grpc-goのv1.63.0で従来のgrpc.Dialが非推奨となり、現在はgrpc.NewClientが推奨です(既定の名前解決がpassthroughからdnsへ変わり、初回のRPCまで実際の接続I/Oを行わない挙動になりました)。古い記事のgrpc.Dialのままコピーしないよう注意してください。
スタブ(Stub)とは:自動生成されるクライアントの代理
スタブは、.protoのサービス定義から自動生成されるクライアント側の代理オブジェクトです。開発者はネットワーク処理を書かず、スタブのメソッドを呼ぶだけでリモートのサーバー処理が実行されます。サーバー側にも対になる基底クラス(PythonではServicer)が生成され、そこに実処理を実装します。
import helloworld_pb2, helloworld_pb2_grpc
# チャネルからスタブを生成
stub = helloworld_pb2_grpc.GreeterStub(channel)
# ローカル関数のように呼ぶだけでリモート実行される
response = stub.SayHello(helloworld_pb2.HelloRequest(name='world'))
print(response.message)
チャネルとスタブの関係と処理の流れ
両者の関係は「チャネル=道路、スタブ=その道路を使う運送業者」と整理できます。実際の開発は次の順に進みます。
.protoでサービスとメッセージを定義する。- protocでクライアント/サーバーのコード(スタブ含む)を生成する。
- クライアントでチャネルを作る。
- チャネルからスタブを生成する。
- スタブのメソッドを呼ぶ(=リモート呼び出し)。
Goでも流れは同じで、生成した接続からスタブを作ります。
// grpc-go v1.63以降の推奨API(insecureは google.golang.org/grpc/credentials/insecure をimport)
conn, err := grpc.NewClient("localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
defer conn.Close()
stub := pb.NewGreeterClient(conn) // チャネルからスタブを生成
resp, _ := stub.SayHello(ctx, &pb.HelloRequest{Name: "world"})
なお、テスト分野で出てくる「スタブ」(依存先を差し替えるTestDoubleの一種としてのスタブ・モック)とgRPCのスタブは別概念です。gRPCのスタブは通信の代理コードであって、テスト用のダミー実装ではない点に注意してください。
gRPCの4つの通信方式
gRPCはHTTP/2のストリーミングを活かし、リクエスト・レスポンスの組み合わせで4つの通信方式を提供します。同じ.protoのrpc定義で、戻り値やストリーム指定を変えるだけで使い分けられます。
| 通信方式 | クライアント | サーバー | 典型用途 |
|---|---|---|---|
| Unary | 1リクエスト | 1レスポンス | 通常のAPI呼び出し |
| サーバーストリーミング | 1リクエスト | 複数レスポンス | 通知・進捗の配信 |
| クライアントストリーミング | 複数リクエスト | 1レスポンス | ログ・計測値の集約 |
| 双方向ストリーミング | 複数リクエスト | 複数レスポンス | チャット・リアルタイム同期 |
双方向ストリーミングはRESTでは素直に実現できない方式で、gRPCを選ぶ強い動機になります。
gRPCとREST APIの違い
「gRPCとRESTの違い」「gRPCとHTTPの違い」は判断に直結する論点です。両者はどちらもHTTPを使いますが、前提とする層が異なります。
| 観点 | gRPC | REST API |
|---|---|---|
| プロトコル | HTTP/2 | 主にHTTP/1.1 |
| データ形式 | Protocol Buffers(バイナリ) | JSON(テキスト) |
| 契約(スキーマ) | .protoで必須・厳密 | OpenAPI等で任意 |
| ストリーミング | 双方向まで標準対応 | 基本はリクエスト応答 |
| ブラウザ直呼び出し | 不可(gRPC-Web+プロキシが必要) | 可能 |
| 人間可読性 | 低い(要デコード) | 高い |
「gRPCとHTTPの違い」という問いへの答えは、gRPCはHTTPを捨てたのではなくHTTP/2を前提にしているという点にあります。多重化やストリーミングはHTTP/2の機能を使っており、テキストのJSONをHTTP/1.1で運ぶRESTとは同じHTTPでも土台が違います。
いつgRPCを使い、いつRESTを使うか
使い分けの結論は明確です。gRPCが向くのは、社内のマイクロサービス間通信・多言語混在・低レイテンシ・ストリーミングが要る領域。逆にブラウザや外部の不特定多数に公開するWeb APIはRESTが無難です。
ありがちな失敗は、公開Web APIにgRPCを選び、後からブラウザ対応のためにgRPC-WebとEnvoyなどのプロキシを重ねて構成が複雑化するパターンです。クライアントがブラウザ中心なら、最初からRESTやGraphQLとREST APIの違いで扱うような選択肢を検討したほうが運用は楽になります。gRPCの真価は、契約が厳密でスキーマ駆動の内部通信で発揮されます。
Protocol Buffers(.proto)とコード生成
gRPCの起点は.protoファイルです。サービス(呼べるメソッド)とメッセージ(送受信するデータ構造)を定義します。
syntax = "proto3";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
この.protoからprotocでコードを生成すると、前述のスタブやメッセージクラスが出力されます。Pythonの場合、必要なパッケージをpipで導入してから生成します(「pip vs grpc」で迷う人が多いですが、両者は競合しません。pipはPythonのパッケージ管理ツールで、それを使ってgRPCの本体grpcioを入れます)。
pip install grpcio grpcio-tools
python -m grpc_tools.protoc -I. --python_out=. --pyi_out=. --grpc_python_out=. helloworld.proto
生成物はhelloworld_pb2.py(メッセージ)とhelloworld_pb2_grpc.py(スタブ・Servicer)です。Goではprotoc-gen-goとprotoc-gen-go-grpcを使い、同じ.protoからGo用のコードを生成します。1つの.protoを各言語のコードに変換できることが、多言語のマイクロサービスをgRPCでつなぐ最大の利点です。
よくある質問
pipとgRPCの違いは何ですか?
別のものです。pipはPythonのパッケージ管理ツール、gRPCはRPCフレームワークで、比較対象ではありません。PythonでgRPCを使うときはpip install grpcio grpcio-toolsのようにpipでgRPCを導入します。
gRPCとRESTはどちらが速いですか?
一般に通信効率はgRPCが有利です。バイナリのProtocol Buffersでデータが小さく、HTTP/2の多重化で往復も減ります。ただしブラウザから直接叩けない・人間が読みにくいという制約があるため、速度だけで選ばず用途で判断します。
gRPCはHTTPと何が違うのですか?
gRPCはHTTPを使わないのではなく、HTTP/2を前提にしています。テキストのJSONをHTTP/1.1で運ぶRESTに対し、gRPCはバイナリをHTTP/2で運び、ストリーミングや多重化といったHTTP/2の機能を活用します。
スタブとチャネルはどう使い分けますか?
チャネルはサーバーへの接続、スタブはその接続の上でRPCを呼ぶ代理です。接続は重いので、1つのチャネルを複数のスタブや複数の呼び出しで共有し、リクエストごとに作り直さないのが基本です。
gRPCはブラウザから直接呼び出せますか?
そのままでは呼び出せません。ブラウザから使うにはgRPC-Web対応のコードと、Envoyなどの変換プロキシが必要です。ブラウザが主なクライアントなら、RESTを選んだほうが構成はシンプルになります。