投稿日:2025/02/17

gRPCを使った高速で簡単な通信

   

こんにちは!
代表兼プログラマーの川本です。

以前、プロセス間通信の記事を上げていますが、通信には様々な手法が存在しています。

今回は昨今のゲームでも利用事例のある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つとして上げられます。

是非、実際に試してみてください!