1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Go で始める gRPC 入門 — Protocol Buffers, `protoc` から自動生成されるコードのしくみ

Last updated at Posted at 2025-02-13

概要

この記事では、Go 言語における gRPC 開発の初歩的な流れをまとめます。Protocol Buffers (以下 Protobuf) で定義した .proto ファイルから自動生成されるコードや、Go Modules におけるモジュールパスの考え方など、PHP や Composer など他言語のエコシステムに慣れた方には少し驚きがあるかもしれません。ここでは、そのポイントをわかりやすく解説します。

前提

Protobuf:
データを効率よくバイナリシリアライズ/デシリアライズする仕組み。言語を超えたデータ交換が容易になる。

gRPC:
Protobuf を使った RPC (リモート呼び出し) フレームワーク。Protobuf がデフォルトだが、実は gRPC では他の形式も拡張次第で扱えなくはない。

使いどころ:
高速・軽量なデータ交換が求められる場面全般(ファイル保存、メッセージキュー、他の通信フレームワークなど)で、必ずしも gRPC がセットではない。Protobuf 単独での利用例も多々ある。

1. Go Modules の基礎知識

Go Modules とは?

Go は依存関係管理のために Go Modules を使います。プロジェクトルートで以下のコマンドを実行し、モジュールを初期化します。

go mod init github.com/user_id/project_name
  • go.mod ファイル が生成され、これが依存関係を管理する上でのエントリーポイントとなります。
  • github.com/user_id/project_name の部分はモジュールパスで、一般的に リポジトリの URL(または一意な識別子)を指定します。
    • 一意性を確保するため、URL 形式にするのが慣例です。
    • PHP/Composer では「vendor/package」形式を使うのが一般的で、ここが Go の文化と大きく異なります。

2. Protocol Buffers (.proto) ファイルの役割

.proto ファイルとは?

.proto ファイルは Protobuf で定義したメッセージやサービス(gRPC を使用する場合)を記述するスキーマファイルです。たとえば、以下のように定義します。

syntax = "proto3";

package hello;

// Go 用のパッケージ指定 (インポートパス; パッケージ名)
option go_package = "github.com/user_id/project_name/proto/hello;hello";

// Greeter サービスの定義
service Greeter {
  // リクエストとレスポンスの型を指定
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

// リクエストメッセージ
message HelloRequest {
  string name = 1;
}

// レスポンスメッセージ
message HelloResponse {
  string message = 1;
}
  • package hello;
    Protobuf 上の名前空間を示すものです。
  • option go_package = ...
    Go 用にコード生成する際の インポートパスパッケージ名 を指定します。
    例:
    option go_package = "github.com/user_id/project_name/proto/hello;hello";
    
    • github.com/user_id/project_name/proto/hello はインポートパス
    • hello はパッケージ名

gRPC を利用しない単なる Protobuf シリアライズ(データを効率よくバイナリシリアライズ/デシリアライズする仕組みのこと)だけなら service は不要ですが、gRPC で遠隔呼び出しするサービスを作るには service を定義します。


3. protoc を使った Go コード生成

Go で Protobuf と gRPC を使うには、以下のプラグインが必要です。

  • protoc-gen-go
  • protoc-gen-go-grpc

これらは go install コマンドでインストール可能です。

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

コード生成コマンドの例

.proto ファイルから Go コードを生成するには、以下のようにコマンドを実行します。

protoc --go_out=. --go-grpc_out=. proto/hello.proto
  • --go_out=.
    Protobuf のメッセージ定義を Go の構造体として出力
  • --go-grpc_out=.
    gRPC サービスのインターフェースやクライアントスタブを Go で出力

このとき、.proto ファイルに書いた option go_package の設定に基づいて、ディレクトリ構造 が自動的に作成され、そこに hello.pb.gohello_grpc.pb.go が生成されます。

hello.proto 内で指定したパッケージが
option go_package = "github.com/user_id/project_name/proto/hello;hello";
となっている場合、以下のように生成されます。

.
├── proto
│   └── hello.proto
└── github.com
    └── user_id
        └── project_name
            └── proto
                └── hello
                    ├── hello.pb.go
                    └── hello_grpc.pb.go

「こんな階層ができるのはエキセントリックだ!」 と感じる方もいるかもしれませんが、これは Go がモジュールパスをそのままディレクトリ構造にマッピングする仕様 に起因しています。

※もし、.proto ファイルのある場所に直接生成したい場合は、paths=source_relative オプションを使います。

補足説明: go_package と .proto ファイルの配置に関する注意点

本記事で紹介している手順に従って進めると、hello.proto 内で設定した go_package のパスと、実際の .proto ファイルの配置場所が一致ません。これにより、生成されたコードが想定外のディレクトリに配置され、最終的に main.go のインポートでエラーが発生します。

具体的な問題例

  • 設定例
    hello.proto 内の設定:

    option go_package = "github.com/yourusername/yourproject/proto/hello;hello";
    
  • 配置の問題
    指示どおりに hello.protoproto/ 配下に置いてしまうと、生成されたコードが
    yourproject/github.com/yourusername/yourproject/proto/hello/
    のような余計なディレクトリ階層に出力される可能性があります。

  • 結果としてのエラー
    その結果、main.go

    import pb "github.com/yourusername/yourproject/proto/hello"
    

    と記述しても、実際の生成ファイルが存在する場所と一致せず、VSCode やコンパイラから「no required module provides package ...」というエラーが発生します。

解決方法

この問題を回避するためには、以下の2つの方法のいずれかを実施してください。

  1. .proto ファイルの配置場所を調整する

    • 具体的には、hello.protoproto/hello/ フォルダに移動します。
    • その上で、プロジェクトルート(例: yourproject/)から以下のように実行します。
      protoc --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. proto/hello/hello.proto
      
    • これにより、生成されたファイルが yourproject/proto/hello/ に正しく配置され、インポートパス github.com/yourusername/yourproject/proto/hello と一致します。
  2. protoc コマンドのオプションを利用する

    • 既存のディレクトリ構造を維持したい場合は、paths=source_relative オプションを用いて生成ファイルを .proto ファイルと同じディレクトリに出力するように調整します。

どちらの方法でも、モジュールルートとインポートパスが一致すれば、main.go の import エラーは解消されます。プロジェクトの構成や運用に合わせて、適切な方法を選択してください。

詳しくは以下の記事を参考にしてください。
参考記事: Go + gRPC + Protocol Buffers 入門 〜 go_package の正しい設定とディレクトリ構成でハマらないために 〜


4. 自動生成されるコードの正体

.proto から生成されるコードには、次のようなものが含まれています。

  1. メッセージ定義

    • Protobuf の message を Go の struct として表現
    • バイナリ形式への変換メソッドなどが自動的に含まれる
  2. gRPC サービスのインターフェースとクライアント

    • service に対応する Go インターフェース(例: GreeterServer
    • クライアントスタブ(GreeterClient)およびサービスの登録関数(RegisterGreeterServer)など

これによって、手書きで低レベルのシリアライズやネットワーク通信コードを書く必要がなく、生成されたインターフェースを実装したり、クライアントを呼び出したりする形で gRPC の機能が使えるようになります。


5. PHP や Composer との比較

  • PHP / Composer

    • composer.jsonvendor/package のようにパッケージを指定
    • インストールすると vendor/ ディレクトリに依存ライブラリが配置される
    • リポジトリ URL を直接「ディレクトリ」として使うことは少ない
  • Go / Go Modules

    • モジュールパスがそのままインポートパスとなる(github.com/ユーザー名/リポジトリ名 など)
    • ディレクトリ構造とインポートパスが対応している
    • Protocol Buffers のコード生成でも、このモジュールパスが利用されるため、やや複雑に見えるディレクトリ構造が作られる

6. まとめ

  1. Go Modules を使ってプロジェクトを初期化
    go mod init github.com/user_id/project_name
    
  2. .proto ファイルでメッセージとサービスを定義
    • option go_package で Go のインポートパスとパッケージ名を指定
  3. protoc + Go プラグインでコード生成
    • protoc --go_out=. --go-grpc_out=. proto/*.proto
    • ディレクトリ構造は go_package に基づく
  4. 生成されたコード(.pb.go, _grpc.pb.go)を使用
    • サーバー側では、インターフェースを実装 & Register〜Server で登録
    • クライアント側では、自動生成されたスタブを呼び出す

Go の世界では「モジュールパス=インポートパス」が大前提となり、protoc のコード生成もこの仕様に強く依存します。PHP エンジニアの方には少しエキセントリックに映るかもしれませんが、慣れてしまうとわかりやすい仕組みです。ぜひ試してみてください!


参考リンク


以上が、Go と Protocol Buffers による gRPC 開発の超入門的なまとめです。質問やフィードバックがあればお気軽にコメントください!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?