膨大な文法にパニックなあなたに向けたgRPCサーバー側
はじめに
このドキュメントは、Go言語の基本文法を一通り習得済みながら、長期記憶に定着しにくい初心者・中級開発者を主な対象としています。gRPCの学習を通じてGoの文法要素を体系的に復習・強化し、分散システム開発の基盤を築きたい方に適しています。本資料作成者自身も、Goの文法習得後、記憶の定着に課題を抱えており、このドキュメントを自身のスキル棚卸(自己診断)と復習ツールとして活用する意図でまとめています。これにより、対象者と作成者の共通課題を共有し、互いの学習プロセスを加速させることを目指します。
gRPCの2層構造
Protocol Buffers = 世界共通語
Protocol Buffers: どの言語でも同じデータとして扱える「世界共通語」
HTTP/2: 一つの接続で複数のやりとりを同時にできる「高速道路」
gRPCの性能の秘密を理解するには、2つの異なる層での最適化を分けて考える必要があります。
技術層 | 担当領域 | 特徴 | 依存性 |
---|---|---|---|
Protocol Buffers(シリアライズ層) | データの表現化 | 意味・互換性重視 | グローバル:言語・プラットフォーム非依存 |
HTTP/2多重化(トランスポート層) | 通信の効率化 | 性能・規格重視 | ローカル:ネットワーク・実装依存 |
シリアライズ・デシリアライズとは
シリアライズ: メモリ上のデータ → バイト列(送信可能な形)
デシリアライズ: 受信したバイト列 → メモリ上のデータ
Protocol Buffers = 世界共通語
実際の変換例
Go言語: User{Name: "田中", Age: 25}
↓ シリアライズ(翻訳)
バイナリ: [謎のバイト列]
↓ デシリアライズ(翻訳)
C#: new User{Name = "田中", Age = 25}
Protocol Buffersの主な利点
側面 | 利点 |
---|---|
言語非依存 | 複数言語で同一スキーマ使用可能。システム柔軟性向上 |
更新時 | 後方互換性が高く、フィールド追加/削除が容易。ダウンタイム最小限。 |
サイズ | バイナリ形式でJSONより倍小さく、転送/ストレージ効率的。高速処理。 |
HTTP/2 = 高速道路
HTTP/1.1の問題(渋滞)
リクエスト1 [████████] → レスポンス1
リクエスト2 [████████] → レスポンス2
リクエスト3 [████████] → レスポンス3
↑ 一個ずつしか処理できない
HTTP/2の解決(並行処理)
リクエスト1 [██] [██] [██] → レスポンス1
リクエスト2 [██] [██] [██] → レスポンス2
リクエスト3 [██] [██] [██] → レスポンス3
↑ 同時に複数処理できる(多重化・インターリーブ)
REST + JSON の問題点
// 手作業でのシリアライズ・デシリアライズ
const response = await fetch('/api/user', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({name: '田中', age: 25}) // 手動変換
});
const data = await response.json(); // エラーの可能性
gRPC 解決
// 自動生成されたコードを使うだけ
response, err := client.CreateUser(ctx, &pb.User{
Name: "田中",
Age: 25,
})
// 型安全、エラー処理も自動
gRPCの概要
gRPCは、Googleが開発した高性能なRPC(Remote Procedure Call)フレームワークで、Protocol BuffersとHTTP/2という2つの独立した技術を基盤に、データの互換性と通信効率の両方を同時に解決します。開発者はシンプルに.protoファイル(サービス定義とメッセージ構造を記述)を作成するだけで、これらの恩恵を自動的に享受できます。
環境構築
git clone [リポジトリ]
Go1.25 install済
go version
go version go1.25.0 windows/amd64
gRPCの特徴
gRPC | RESTful API (OpenAPI) | |
---|---|---|
定義ファイル | Protocol Buffers (.proto) | OpenAPI Specification(openapi.yaml) |
データ形式 | バイナリ(Protocol Buffers) | テキスト(JSON) |
プロトコル | HTTP/2 | HTTP/1.1 or HTTP/2 |
開発フロー | Proto-first サービス間の「契約」を最初に決める。 | Code-first サーバー側を実装してから、そのAPI仕様を共有する。 |
用途 | 主にマイクロサービス間の通信 | Web APIの標準、外部連携など |
gRPC Protocol Buffersの定義
//version定義
syntax = "proto3";
//自動生成させるGoのコードをpkg/api直下に配置する指定
option go_package = "gen/api";
// Protocol Buffersのパッケージ名を指定
package helloworld;
//サービスとメソッドの定義
service GreetingService{
//SayHelloというメソッドをリモートプロシージャコール(RPC)に定義
rpc SayHello(HelloReuest)returns(HelloResponse){}
}
//メッセージ型の定義
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
自動生成の準備と実行
Protocol Buffers コンパイラ(protoc)
Go言語用のprotocプラグイン(protoc-gen-goとprotoc-gen-go-grpc)
以下の手順でインストールできます。
1. protocのインストール
GitHubからダウンロード:
お使いのWindowsのOSとアーキテクチャ(win32またはwin64)に対応したprotoc-xx.x-winxx.zipというファイルをダウンロードします。
ダウンロードしたzipファイルを解凍し、中にあるprotoc.exeを任意のディレクトリ(例: C:\protoc\bin)に配置します。
2. Go言語用プラグインのインストール
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
protoc --version
libprotoc 32.0
3.自動生成コマンドの実行
protoc --go_out=. --go-grpc_out=. proto/helloworld.proto
(毎回長いコマンドを入力するのは面倒なのでMakefileで可能だそうです今回は割愛)
このコマンドの説明:
--go_out=.: Protocol BuffersのメッセージとシリアライゼーションコードをGoで生成
--go-grpc_out=.: gRPCサービスのクライアント/サーバーコードをGoで生成
prpto/helloworld.proto: 入力となる.protoファイル
4.最終的なディレクトリ構成
grpc-app-sample/
├── cmd/
│ └── server/ #サーバーのエントリーポイントを格納するディレクトリ
│ └── main.go # gRPCサーバーのエントリーポイント
├── proto/
│ └── helloworld.proto
├── gen/ # genは「generated(生成された)」の略
│ └── api/
│ ├── helloworld.pb.go # Protocol Buffersメッセージの定義
│ └── helloworld_grpc.pb.go # gRPCサービスの定義
├── go.mod
└── go.sum
補足
gRPCは、クライアントとサーバー間の通信に特化した技術です。
HTTP/2の多重化という機能により、1つのポートで複数のリクエストを同時に処理できます。
複数のサービスを同じサーバー内で提供するため、ポートを(例: 8080/3030)分ける必要がありません
サーバーを起動する部分のコード
1. パッケージ宣言とインポート文法
package main
2. 型定義(Type Definition)
import (
"context"
"fmt"
"log"
"net"
"os"
"os/signal"
hellopb "grpc-app-sample/gen/api" //生成されたコードのインポート
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
3. 関数定義とポインタ
func NewMyServer() *myServer {
return &myServer{}
}
4. メソッド定義とレシーバー
func (s *myServer) Hello(ctx context.Context, req *hellopb.HelloRequest) (*hellopb.HelloResponse, error) {
return &hellopb.HelloResponse{
Message: fmt.Sprintf("Hello, %s!", req.GetName()),
}, nil
}
5. main関数と変数宣言
func main() {
port := 8080
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
panic(err)
}
エラーハンドリングパターン
if err != nil {
panic(err)
}
6. 変数宣言と代入
s := grpc.NewServer()
7. 関数呼び出し
hellopb.RegisterGreetingServiceServer(s, NewMyServer())
reflection.Register(s)
gRPCサーバーにGreetingServiceを登録
hellopb.RegisterGreetingServiceServer(s, [サーバーに登録するサービス])
8. Goroutineと無名関数
go func() {
log.Printf("start gRPC server port: %v", port)
s.Serve(listener)
}()
9. チャンネル(Channel)とシグナル(Signal)
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
<-quit
10. ログ出力とメソッド呼び出し
log.Println("stopping gRPC server...")
s.GracefulStop()
サーバー側の実装まとめ
package main
//パッケージ宣言とインポート文法
import (
"context"
"fmt"
"log"
"net"
"os"
"os/signal"
hellopb "grpc-app-sample/gen/api" //生成されたコードのインポート
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
// 型定義
// hellopb.UnimplementedGreetingServiceServer の全メソッドが myServer で使用可能
// 継承ではなく「委譲」の仕組み
type myServer struct {
hellopb.UnimplementedGreetingServiceServer
}
// 関数定義とポインタレシー
func NewMyServer() *myServer {
return &myServer{}
}
// メソッド定義とレシーバー
func (s *myServer) Hello(ctx context.Context, req *hellopb.HelloRequest) (*hellopb.HelloResponse, error) {
// return
return &hellopb.HelloResponse{
Message: fmt.Sprintf("Hello, %s!", req.GetName()),
}, nil
}
// main関数と変数宣言
func main() {
// 8080番ポートのListenerを作成
port := 8080
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
//エラーハンドリングパターン
if err != nil {
panic(err)
}
//変数宣言と代入
s := grpc.NewServer()
//関数呼び出し
hellopb.RegisterGreetingServiceServer(s, NewMyServer())
//この行がリフレクション機能を有効にしている。おかげでgrpcurlが使える!
reflection.Register(s)
go func() {
log.Printf("start gRPC server port: %v", port)
s.Serve(listener)
}()
//チャンネル(Channel)とシグナル(Signal)
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
<-quit
//ログ出力とメソッド呼び出し
log.Println("stopping gRPC server...")
s.GracefulStop()
}
grpcurlでこのコードの動作確認
Go経由でインストール(最も確実)
Goは既にインストールされているので、これが一番簡単です。
powershellgo install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
powershellgrpcurl --version
go run main.go
2025/09/22 00:33:28 start gRPC server port: 8080
新しいPowerShellウィンドウを開いてgrpcurlでテスト
grpcurl -plaintext localhost:8080 list
期待される出力
grpc.reflection.v1.ServerReflection - リフレクション機能
grpc.reflection.v1alpha.ServerReflection - 古いバージョンのリフレクション
helloworld.GreetingService - あなたが実装したサービス
次回、client側の連携の理解を試みたいと思います。
参考
Discussion