こんにちは!
代表兼プログラマーの川本です。
以前、プロセス間通信の記事を上げていますが、通信には様々な手法が存在しています。
今回は昨今のゲームでも利用事例のあるgRPCを使った通信プログラムについて記載していきます。
gRPCについて
gRPC(gRPC Remote Procedure Call)は、Googleが開発したオープンソースのRPC(Remote Procedure Call)フレームワークです。
HTTP/2をベースとし、高速でスケーラブルな通信を可能にするプロトコルとして、多くのシステムで採用されています。
また、Protocol Buffers(protobuf)を使用して通信データをシリアライズするため、従来のJSONやXMLよりも高速かつ効率的なデータ転送が可能です。
gRPCの主な特徴
- HTTP/2を使用し、効率的な通信を実現
- Protocol Buffersを利用した軽量なデータフォーマット
- 双方向のストリーミング通信をサポート
- 多言語対応(Python, C#, Java, Go, Rust など)
ゲーム開発での利用事例
株式会社Cysharpから公開されているOSS「MagicOnion」でも採用されています。
MagicOnionはUnityを活用した様々なゲーム開発でも活用されています。
※MagicOnionはC#向けに最適化されている
gRPCのメリット・デメリット
メリット
- 高速な通信: HTTP/2とProtocol Buffersにより、低レイテンシかつ高速なデータ転送が可能
- スケーラビリティ: ストリーミング対応や負荷分散に適している
- 多言語対応: 異なるプログラミング言語間でシームレスな通信が可能
- API設計の統一: .protoファイルを基に自動生成されるコードで開発を効率化
デメリット
- 学習コスト: REST APIに比べると、.protoファイルやgRPCの概念を理解する必要がある
- デバッグの難しさ: JSONと異なり、Protocol Buffersのデータは可読性が低いためデバッグが難しい
- ブラウザとの直接通信が難しい: gRPC-Webを使わないとブラウザでの直接通信ができない
gRPCの仕組み
gRPCの基本的な仕組みは、クライアントとサーバー間でのRPC(Remote Procedure Call)通信です。
サーバーは特定のサービスを提供し、クライアントはそのサービスを呼び出すことでデータを送受信します。
gRPCの通信モデル
gRPCは以下の4つの通信モデルを提供します。
Unary RPC | クライアントがリクエストを送り、サーバーがレスポンスを1回返す |
---|---|
Server Streaming RPC | クライアントが1つのリクエストを送り、サーバーが複数のレスポンスをストリームとして送る |
Client Streaming RPC | クライアントが複数のリクエストをストリームで送り、サーバーが1つのレスポンスを返す |
Bidirectional Streaming RPC | クライアントとサーバーが双方向にストリーミング通信を行う |
PythonとC#間でgRPC接続
ここでは、PythonのgRPCサーバーとC#のgRPCクライアントを実装して、異なる言語間での通信を実現します。
1. protoファイルの作成
まず、通信のインターフェースを定義する `.proto` ファイルを作成します。
- syntax = "proto3";
- service Greeter {
- rpc SayHello (HelloRequest) returns (HelloReply);
- }
- message HelloRequest {
- string name = 1;
- }
- message HelloReply {
- string message = 1;
- }
このファイルを基に、PythonやC#のgRPCコードを自動生成します。
2. Python用のgRPCコード生成
pythonでgRPCコードの生成とサーバーを起動するには以下のコマンドでモジュールのインストールが必要になります。
- pip install grpcio
- pip install grpcio-tools
次にgrpcio-toolsを使ってprotoファイルからコードを生成します。
- python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. greeter.proto
このコマンドを実行すると、以下の2つのファイルが生成されます。
- `greeter_pb2.py`: メッセージのシリアライズ/デシリアライズを担当
- `greeter_pb2_grpc.py`: gRPCのクライアントとサーバーのスタブを提供
3. Pythonで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()
- server.wait_for_termination()
- if __name__ == '__main__':
- serve()
このスクリプトを実行することで、PythonのgRPCサーバーが起動します。
4. C#でgRPCクライアントを作成
次に、C#でgRPCクライアントを作成し、Pythonサーバーと通信を行います。
- using System;
- using System.Threading.Tasks;
- using Grpc.Net.Client;
- using Greeter;
- class Program
- {
- static async Task Main(string[] args)
- {
- // Python側(gRPCサーバー)へのURLを指定
- using var channel = GrpcChannel.ForAddress("http://localhost:50051");
- var client = new Greeter.GreeterClient(channel);
- // Python(サーバー)のSayHello関数を呼び出し
- var reply = await client.SayHelloAsync(new HelloRequest { Name = "C# Client" });
- Console.WriteLine("Server Response: " + reply.Message);
- }
- }
このコードを実行すると、Pythonサーバーにリクエストを送り、レスポンスを受け取ることができます。
5. 実行結果
C#クライアントを実行すると、以下のような出力が得られます。
- Server Response: Hello, C# Client!
これで、PythonサーバーとC#クライアント間のgRPC通信が成功しました。
手順は複数ありますが、コードだけで見ると
- Python(サーバー側):21行(メインコードは約10行)
- C#(クライアント側):17行(通信コードは3行)
とたったこれだけで通信機能を実装する事が出来ます。
まとめ
gRPCは、高速かつ効率的な通信を可能にする強力なフレームワークです。
特に、異なるプログラミング言語間での通信が求められる場面では、gRPCを活用することでスムーズなデータのやり取りが可能になります。
今回はPythonとC#間の接続を実装しましたが、他の言語でも同様の方法で接続が可能です。
これはゲームに限らず有効な通信手段の1つとして上げられます。
是非、実際に試してみてください!