gRPCとは?仕組み・RESTとの違いから実装例まで実際に動かして解説

gRPCは、Googleが開発したオープンソースのRPC(Remote Procedure Call=リモートプロシージャコール)フレームワークです。離れた場所にあるサーバーの関数を、まるで手元のローカル関数のように呼び出せるのが特徴で、特にマイクロサービス間の高速・効率的な通信で広く使われています。この記事では、gRPCとは何かという基本から、HTTP/2とProtocol Buffersを使う仕組み、REST APIとの違い、メリット・デメリット、そして実際に動かして確認したPythonの実装例までを順に解説します。ポート番号やUDPの扱いなど、実務でつまずきやすい点はFAQでまとめています。

まとめ

gRPCは、HTTP/2とProtocol Buffersを土台に、リモートの処理をローカル関数のように呼び出せるGoogle発のRPCフレームワークです。小さく高速なデータ転送、強い型付け、多言語対応、4種類の通信方式といった特徴があり、特にマイクロサービス間の通信で大きな効果を発揮します。一方で、ブラウザから直接使えない、バイナリでデバッグしにくいといった弱点もあるため、外部公開はREST、内部通信はgRPC、というように適材適所で使い分けるのが現実的です。まずは本記事の最小実装を手元で動かし、.protoの定義からコード生成、サーバー・クライアント実装という流れを体感してみてください。

gRPCとは何か

gRPCは、Googleが社内で長年使ってきた「Stubby」と呼ばれるRPC基盤をルーツに持ちます。Stubbyは2000年代初頭からGoogle内部で運用されていましたが独自仕様が多く外部公開されていませんでした。その後、通信部分を標準のHTTP/2に置き換えるなどして整理し、2015年にgRPCとしてオープンソース化されました。現在はCloud Native Computing Foundation(CNCF)のもとで開発が進められ、クラウドネイティブな分散システムの標準的な通信手段の一つになっています。

gRPCの土台にあるのはRPCという考え方です。RPCは、ネットワークの向こう側にある処理を、通信の細かい手続きを意識せずに「関数呼び出し」として扱えるようにする仕組みを指します。gRPCはこのRPCを、HTTP/2という高速なトランスポートと、Protocol Buffersという効率的なデータ形式の上で実現したものだと考えると分かりやすいでしょう。プログラミング言語に依存しないため、Goで書いたサーバーをPythonのクライアントから呼ぶ、といった言語をまたいだ連携も容易です。

gRPCの仕組み(3つの構成要素)

gRPCの仕組みは、大きく「HTTP/2」「Protocol Buffers」「スタブとチャネル」の3つに分けて理解すると整理しやすくなります。

HTTP/2:高速な通信路

gRPCは通信路にHTTP/2を使います。HTTP/2は、1本の接続の上で複数のやり取りを同時に流せる多重化、ヘッダーの圧縮、サーバーとクライアントが双方向にデータを流せるストリーミングといった特徴を持ち、従来のHTTP/1.1に比べて低遅延で効率的です。gRPCのストリーミング機能やパフォーマンスの高さは、このHTTP/2の性質に支えられています。

Protocol Buffers:効率的なデータ形式

gRPCは標準で、Protocol Buffers(protobuf)というデータのシリアライズ方式を使います。JSONやXMLのようなテキストではなくバイナリでデータを表現するため、サイズが小さく転送が高速です。やり取りするデータ構造とサービスのメソッドは、拡張子が「.proto」のテキストファイルにスキーマとして定義します。この定義からコンパイラが各言語向けのコードを自動生成するため、強い型付けと言語間の互換性が同時に得られます。

なお、gRPCとProtocol Buffersは混同されがちですが別物です。gRPCはRPCを実現する通信フレームワークであり、Protocol Buffersはそのデータの表現方式の一つにすぎません。gRPCのトランスポート(HTTP/2)やデータ形式は理屈の上では差し替えも可能ですが、標準ではHTTP/2とProtocol Buffersの組み合わせが使われます。

スタブとチャネル:呼び出しの入り口

クライアント側には「スタブ」と呼ばれる代理オブジェクトが用意されます。開発者はスタブのメソッドを普通の関数のように呼び出すだけで、実際のシリアライズやHTTP/2通信はスタブが裏側で処理してくれます。スタブはサーバーへの接続を表す「チャネル」を通じてリクエストを送り、サーバー側のgRPCサーバーがそれを受け取って処理し、結果を返します。これにより、ネットワーク通信の複雑さを開発者から隠せるのがgRPCの大きな利点です。

gRPCの4つの通信方式

gRPCは単純な1往復の通信だけでなく、HTTP/2のストリーミングを活かした4つの通信方式をサポートしています。用途に応じて使い分けられる点が、REST APIとの大きな違いの一つです。

  • Unary RPC(単一):1つのリクエストに対して1つのレスポンスを返す、もっとも基本的な形。通常の関数呼び出しに近い使い方です。
  • サーバーストリーミングRPC:1つのリクエストに対し、サーバーが複数のレスポンスを順次返します。一覧データの逐次配信などに向きます。
  • クライアントストリーミングRPC:クライアントが複数のリクエストを送り、サーバーが最後にまとめて1つのレスポンスを返します。大量データのアップロードなどに向きます。
  • 双方向ストリーミングRPC:クライアントとサーバーが同時に複数のメッセージを送受信します。チャットやリアルタイムなデータ処理に適しています。

gRPCとREST APIの違い

gRPCを検討する際にもっとも比較されるのがREST APIです。どちらもシステム間の通信に使われますが、設計思想と得意分野が異なります。主な違いを表に整理します。

項目 gRPC REST API
ベースのプロトコル HTTP/2 主にHTTP/1.1(HTTP/2も利用可)
データ形式 Protocol Buffers(バイナリ) JSON・XML(テキスト)
スキーマ定義 .protoで必須・強い型付け 任意(OpenAPI等で補完)
通信方式 4種類(単一・各種ストリーミング) 基本はリクエスト/レスポンス
速度・データサイズ 高速・小さい 相対的に低速・大きい
ブラウザからの直接利用 不可(grpc-web+プロキシが必要) 可能
人による可読性 低い(バイナリ) 高い(テキスト)
主な用途 マイクロサービス間・社内API・リアルタイム 公開Web API・ブラウザ向け

ざっくり言えば、ブラウザや外部公開を前提とする一般的なWeb APIではRESTが扱いやすく、サーバー同士の高頻度・低遅延な通信ではgRPCが力を発揮します。両者は排他的なものではなく、外部公開はREST、内部のサービス間はgRPC、というように使い分ける構成もよく採られます。なお、同じく代替として比較されることの多いGraphQLについては「GraphQLをわかりやすく解説:基本概念と具体例」、RESTとの設計思想の違いは「GraphQLとREST APIの違いは何か?その主要な特徴を比較」もあわせて参考にしてください。

gRPCのメリットとデメリット

gRPCの主なメリットは、Protocol Buffersによる小さく高速なデータ転送、HTTP/2による多重化・低遅延、.protoスキーマに基づく強い型付け、そして多くの言語に対応したクロスプラットフォーム性です。さらに4種類の通信方式により、リアルタイムなストリーミング処理も標準で実装できます。

一方でデメリットもあります。.protoの設計やコンパイルといった独自の手順があるため学習コストがかかること、データがバイナリのためJSONに比べてデバッグやログ確認がしにくいこと、そしてブラウザから直接呼び出せない点です。ブラウザ対応にはgrpc-webとプロキシ(Envoyなどが代表的)を別途用意する必要があります。これらを踏まえ、ブラウザ向けの公開APIにはRESTを使い、サーバー間通信にgRPCを使う、といった適材適所の判断が重要になります。

gRPCが向いている場面

gRPCがもっとも活きるのは、マイクロサービス間の内部通信です。サービスを小さく分割するアーキテクチャでは、サービス同士が頻繁に通信するため、低遅延で型安全なgRPCの利点が大きく効きます。マイクロサービスそのものの考え方は「モノリスとマイクロサービスの違いを徹底解説:基本から理解する」で詳しく扱っています。このほか、限られた帯域で効率よくデータを送りたいIoTデバイス間の通信や、双方向ストリーミングを使ったリアルタイム処理にも適しています。

gRPCとRESTの使い分け:採用判断のポイント

gRPCとRESTは優劣で選ぶものではなく、プロジェクトの性質に合わせて使い分けるものです。導入を迷ったときは、次のような観点で判断すると整理しやすくなります。

gRPCの採用が向いているのは、サーバー同士の内部通信が多いシステムです。具体的には、マイクロサービス間で頻繁に通信する構成、GoやJava・Python・Node.jsなど複数の言語が混在する環境、チャットやライブ配信のように双方向のリアルタイム通信が必要なケース、大規模なチームで.protoによってAPI仕様を型で厳密に管理したい場合などが挙げられます。すでにRESTでの通信がパフォーマンスのボトルネックになっているなら、内部通信をgRPCに置き換える効果は大きくなります。

一方で、次のような場合はRESTのほうが扱いやすい選択になります。ブラウザやモバイルアプリから直接呼び出す公開API、管理画面のようにシンプルなCRUD(作成・読み取り・更新・削除)が中心のシステム、RESTに慣れた少人数チーム、外部やパートナーのAPIとの連携が多いプロダクト、そしてプロトタイプやMVPのように開発・検証の速度を最優先したい段階です。これらの場面では、gRPCの学習コストやデバッグの手間が利点に見合わないことが多くなります。

現実的な進め方としては、まずRESTでシステムを構築し、通信のボトルネックが明確になった部分から内部通信をgRPCへ段階的に移行するアプローチが取りやすいでしょう。外部公開はREST、内部のサービス間はgRPC、というように両者を併用する構成も広く採られています。API設計そのものの基本的な考え方は「Google API設計ガイドの概要と良いAPI設計の基本ポイント」もあわせて参考にしてください。

gRPCの実装例(Python・動作確認済み)

ここでは、もっとも基本的なUnary RPCを例に、最小構成のgRPCサーバーとクライアントをPythonで実装します。以下のコードは実際に動作を確認したものです。まずは通信の設計図となる.protoファイルを作成します。

▼ greeter.proto(サービスとメッセージの定義)

syntax = "proto3";

package greeter;

// サービス定義:1つのメソッドを持つ
service Greeter {
  // Unary RPC:1リクエストに対し1レスポンスを返す
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

次に、この.protoから各言語向けのコードを生成します。Pythonでは、必要なライブラリ(grpcioとgrpcio-tools)を導入したうえで、付属のコンパイラを使います。同じ.protoファイルからGoやJavaなど他言語のコードも生成できます。

▼ ライブラリの導入とコード生成(bash)

pip install grpcio grpcio-tools
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. greeter.proto

これで「greeter_pb2.py」(メッセージ定義)と「greeter_pb2_grpc.py」(サービス定義)が生成されます。生成されたコードを使ってサーバーを実装します。

▼ server.py(gRPCサーバー)

from concurrent import futures
import grpc
import greeter_pb2
import greeter_pb2_grpc

class GreeterServicer(greeter_pb2_grpc.GreeterServicer):
    def SayHello(self, request, context):
        return greeter_pb2.HelloReply(message=f"Hello, {request.name}!")

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    greeter_pb2_grpc.add_GreeterServicer_to_server(GreeterServicer(), server)
    server.add_insecure_port("[::]:50051")
    server.start()
    print("gRPC server started on :50051")
    server.wait_for_termination()

if __name__ == "__main__":
    serve()

続いて、サーバーのメソッドを呼び出すクライアントを実装します。

▼ client.py(gRPCクライアント)

import grpc
import greeter_pb2
import greeter_pb2_grpc

def run():
    with grpc.insecure_channel("localhost:50051") as channel:
        stub = greeter_pb2_grpc.GreeterStub(channel)
        response = stub.SayHello(greeter_pb2.HelloRequest(name="World"))
        print("Server replied:", response.message)

if __name__ == "__main__":
    run()

サーバーを起動した状態で別のプロセスからクライアントを実行すると、次のように応答が返ります。スタブのメソッドを呼ぶだけで、リモートの処理をローカル関数のように利用できていることが分かります。

▼ 実行結果

Server replied: Hello, World!

このように、gRPCでは.protoでインターフェースを定義し、コードを生成し、サーバーとクライアントを実装する、という流れが基本になります。本番環境では、後述するTLSによる暗号化やエラーハンドリング、タイムアウト設定などを加えていきます。

よくある質問(FAQ)

gRPCのポート番号は決まっていますか?

gRPC専用に固定されたポート番号はありません。任意のポートを指定できます。ただし公式のチュートリアルやサンプルでは慣例的に「50051」がよく使われており、開発時の例として広く浸透しています。本番では用途に応じて自由に決めて構いません。

gRPCはUDPを使うのですか?

標準のgRPCはUDPではなく、HTTP/2の上で動作します。HTTP/2はTCPベースのため、gRPCの通信もTCPで行われます。「gRPC UDP」と混同されることがありますが、標準構成ではUDPは使いません。なお、UDPを利用するHTTP/3(QUIC)に対応する動きも進んでおり、将来的にはトランスポートの選択肢が広がる可能性がありますが、現時点の標準はHTTP/2(TCP)です。

gRPCの通信は暗号化できますか?(TLS対応)

はい。gRPCはHTTP/2をベースにしているためTLSを容易に組み込めます。サーバーに証明書と秘密鍵を設定して通信を暗号化でき、クライアントとサーバーが互いに証明書を検証する相互TLS(mTLS)も利用できます。公開ネットワークを越える通信では、TLSによる暗号化が事実上の前提となります。

gRPCはブラウザから直接使えますか?

ブラウザからgRPCを直接呼び出すことはできません。gRPCはHTTP/2の機能を細かく利用するため、ブラウザのJavaScriptからはそのまま扱えないためです。ブラウザ対応にはgrpc-webという仕組みを使い、Envoyなどのプロキシを介して通信します。

gRPCはどの言語で使えますか?

Go、Java、Python、C++、C#、Node.js、Ruby、PHP、Kotlin、Dart など、主要な言語に幅広く対応しています。同じ.protoファイルから各言語向けのコードを生成できるため、異なる言語で書かれたサービス同士を接続できるのがgRPCの強みです。

関連記事

資料請求

RELATED POSTS 関連記事