はじめに
現代のバックエンド開発では、柔軟性があり高速な通信プロトコルが求められています。
そこで注目されるのが、Googleが開発したgRPC
という通信技術です。gRPC
は、HTTP/2
を使って、低遅延かつ効率的にクライアントとサーバー間でのデータ通信を行います。また、バイナリ形式のProtocol Buffers
を用いることで、JSON
やXML
などに比べて非常に軽量な通信を実現します。
しかし、従来のgRPC
はブラウザ対応の難しさや、認証・HTTPサポートの実装コストが課題でした。ここで登場するのが、今回使用する Connect-goです。
Connect-go
は、gRPC
だけでなくgRPC-Web
やHTTP/JSON
通信にも対応しており、シンプルで拡張性の高いAPI構築を支援するフレームワークです。
本記事では、Connect-goを使ったgRPCサービスの構築手順を、公式ドキュメントに沿って実践します。モジュールの初期化から、Protocol Buffersを用いたサービス定義、Bufによるコード生成、Goサーバーの実装まで、一連のプロセスを順を追って進めていきます。
ハンズオン
公式ドキュメント
Goプロジェクトの作成
gRPC
サービスを構築するため、まずGo
プロジェクトを初期化します。
Go
では、プロジェクトの依存関係やモジュール管理にgo mod
を使用します。
(go mod init [プロジェクト名])
go mod init example
ディレクトリとprotoファイルの作成
gRPC
サービスで使用する Protocol Buffers(.proto)
ファイル を作成します。
このファイルには、API
のリクエスト・レスポンスの形式や、RPC
メソッドの定義を記述します。
mkdir -p greet/v1
touch greet/v1/greet.proto
greet.protoの定義
次に、greet.proto
にgRPC
サービスの定義を書き込みます。
greet.proto
ファイルでは、リクエスト・レスポンスのメッセージ形式と、それを使うサービスメソッドを定義します。
greet.protoの内容
syntax = "proto3";
package greet.v1;
option go_package = "example/gen/greet/v1;greetv1";
message GreetRequest {
string name = 1;
}
message GreetResponse {
string greeting = 1;
}
service GreetService {
rpc Greet(GreetRequest) returns (GreetResponse) {}
}
解説
-
syntax = "proto3";
-
Proto3
はProtocol Buffers
の最新バージョンで、シンプルかつ軽量なデータ形式で定義を行います。
-
-
package greet.v1;
- パッケージは、
API
を論理的に分割し、命名の競合を避けるために使います。ここではgreet
のバージョン1(v1)を表しています。
- パッケージは、
-
option go_package
- 生成される
Go
コードの出力先を指定します。example/gen/greet/v1;greetv1
の形式で、greetv1
という名前でGoパッケージとして利用できるようになります。
- 生成される
-
message GreetRequest
- リクエストの形式を表すメッセージです。ここでは、
name
という文字列を含む構造体を定義しています。
- リクエストの形式を表すメッセージです。ここでは、
-
message GreetResponse
- レスポンスの形式を表すメッセージです。
greeting
という文字列を返します。
- レスポンスの形式を表すメッセージです。
-
service GreetService
-
gRPC
のサービスを定義するブロックです。 -
rpc Greet
はリクエストとしてGreetRequest
を受け取り、GreetResponse
を返すRPCメソッドです。
-
Bufモジュールの初期化
次に、Bufを使ってモジュールを初期化します。
これにより、buf.yaml
という設定ファイルが生成されます。
buf mod init
buf.yamlの解説
buf mod init
コマンドを実行すると、次のようなbuf.yaml
が生成されます。
# For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml
version: v2
lint:
use:
- STANDARD
breaking:
use:
- FILE
解説
-
version: v2
- Bufの設定ファイルのバージョンを示します。
- Bufのルールセットや構文チェックの対象が、このバージョンに基づいて動作します。
- lintセクション
use: - STANDARD
- Bufが推奨する 「標準的なLintルール」 を適用します。
- これにより、Protoファイルの構文エラーやフォーマットのミスを検出し、プロジェクトの品質を保ちます。
- breakingセクション
use: - FILE
- ファイル単位でのBreaking Change(互換性を壊す変更) を検出します。
- 例えば、ProtoファイルからメッセージやRPCメソッドを削除するなどの変更を検出し、意図せずAPIが壊れるのを防ぎます。
Bufを使ったProtoファイルのチェック
buf.yaml
ファイルが設定された状態で、lint
チェックやビルドを行えます。
buf lint
このコマンドを実行すると、greet.proto
の構文やスタイルに問題がないかBufがチェックします。
buf build
このコマンドで、BufがProto
ファイルをビルドし、エラーがないか確認します。
buf.gen.yamlの作成
まず、コード生成のためのbuf.gen.yaml
を作成します。
touch buf.gen.yaml
buf.gen.yaml
の内容
(環境に合わせて調整してください)
version: v2
plugins:
- local: protoc-gen-go
out: gen
opt: paths=source_relative
- local: protoc-gen-connect-go
out: gen
opt: paths=source_relative
解説
-
version: v2
- Bufの設定が最新のv2形式であることを示します。
-
plugins
- local:ローカルにインストールされたプラグインを使用します。
-
protoc-gen-go
:Goのコードを生成するためのプラグイン。 -
protoc-gen-connect-go
:Connect-goのためのハンドラコードを生成します。
-
- local:ローカルにインストールされたプラグインを使用します。
-
out: gen
- 生成されたコードの出力先を
gen/
ディレクトリに指定します。
- 生成されたコードの出力先を
-
opt: paths=source_relative
- ファイルが相対パスで生成され、ソースコード内のパッケージ構造に合致するようにします。
コード生成の実行
greet.proto
からGoコードを生成します。
buf generate
gRPCサービスを実装するためのコード生成が完了しました。
生成されるコードの内容
greet.proto に基づいて以下の2種類のコードが生成されます。
-
protoc-gen-go
によるコード生成
ファイル: /gen/greet/v1/greet.pb.go
役割:gRPC
のメッセージ型やサービスの定義をGo
用の構造体として生成します。
GreetRequest
やGreetResponse
がGoの型として使えるようになります。
gRPCサーバー側で使われるサービスインターフェースも定義されます。
-
protoc-gen-connect-go
によるコード生成
ファイル: /gen/greet/v1/greetv1connect/greet.connect.go
役割:Connect-go
用のハンドラコードを生成します。
GreetService
のサービスハンドラが自動生成され、Go
サーバーで使うRPC
エンドポイントを簡単にセットアップできます。
Connect-go
は、gRPC
だけでなく、gRPC-Web
やHTTP/JSON
にも対応した柔軟な通信をサポートします。
Goサーバーの作成
生成された gRPC
メッセージ・ハンドラコードを使って、Go
サーバーを実装します。
このサーバーは、Connect-go
を使ってgRPC
リクエストを受け取り、HTTP/2
で通信を行います。
ディレクトリ構成
次のように、サーバーのエントリーポイントとなるGo
ファイルを作成します。
mkdir -p cmd/server
touch cmd/server/main.go
Goサーバー実装:cmd/server/main.go
package main
import (
"context"
"fmt"
"log"
"net/http"
"github.com/bufbuild/connect-go" // 注意:公式では"connectrpc.com/connect"となっていますが、動作しないです。
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
greetv1 "example/gen/greet/v1" // generated by protoc-gen-go
"example/gen/greet/v1/greetv1connect" // generated by protoc-gen-connect-go
)
type GreetServer struct{}
func (s *GreetServer) Greet(
ctx context.Context,
req *connect.Request[greetv1.GreetRequest],
) (*connect.Response[greetv1.GreetResponse], error) {
log.Println("Request headers: ", req.Header())
res := connect.NewResponse(&greetv1.GreetResponse{
Greeting: fmt.Sprintf("Hello, %s!", req.Msg.Name),
})
res.Header().Set("Greet-Version", "v1")
return res, nil
}
func main() {
greeter := &GreetServer{}
mux := http.NewServeMux()
path, handler := greetv1connect.NewGreetServiceHandler(greeter)
mux.Handle(path, handler)
http.ListenAndServe(
"localhost:8080",
// Use h2c so we can serve HTTP/2 without TLS.
h2c.NewHandler(mux, &http2.Server{}),
)
}
解説
-
GreetServer
構造体-
GreetServer
は、GreetService
のメソッドを実装する構造体です。 -
Greet
メソッドで、リクエストからユーザー名を取得し、Hello, [name]!
という挨拶をレスポンスとして返します。
-
-
connect-go
の使用-
github.com/bufbuild/connect-go
をインポートして使います。公式ではconnectrpc.com/connect
というパスが推奨されていますが、互換性の問題があるためbufbuild
のパスを使います。
-
-
h2c
を使ったHTTP/2
の起動
-
h2c.NewHandler
を使い、TLS
なしでHTTP/2
サーバーを起動しています。 -
gRPC
はHTTP/2
をベースとしているため、この設定が必要です。
- HTTPハンドラへのサービス登録
-
greetv1connect.NewGreetServiceHandler
を使って、GreetService
をHTTPハンドラとして登録します。 - このハンドラは、
gRPC-Web
やHTTP/JSON
にも対応できます。
-
go mod tidy
最後に
go mod tidy
をしてください。
サーバーの実行とテスト
go run
コマンドでサーバーを起動し、curl
とgrpcurl
を使ってサービスが正しく応答するか確認します。
サーバーの起動
go run ./cmd/server/main.go
curlでのテスト
curl
を使って、サービスにリクエストを送信し、JSONレスポンスが正常に返ってくるか確認します。
curl \
--header "Content-Type: application/json" \
--data '{"name": "Jane"}' \
http://localhost:8080/greet.v1.GreetService/Greet
実行結果
{
"greeting": "Hello, Jane!"
}
grpcurlでのテスト
grpcurl
を使ってgRPC形式でリクエストを送信します。
grpcurl \
-protoset <(buf build -o -) -plaintext \
-d '{"name": "Jane"}' \
localhost:8080 greet.v1.GreetService/Greet
実行結果
{
"greeting": "Hello, Jane!"
}
最後に
Connect-go
を使ったgRPC
サービスの構築は、シンプルで拡張性の高いバックエンド開発を実現する優れた選択肢です。本記事では、
公式ドキュメントに沿ってサービスの定義、コード生成、サーバー実装からAPIテストまでの流れを解説しました。今回のハンズオンを通じて、gRPC
とHTTP/JSON
を同一エンドポイントで提供する実装の柔軟さを実感していただけたかと思います。
最後に、本記事で使用した全コードを以下にまとめておきます。各ステップを順に進め、実装が意図通り動作するかぜひ確認してみてください。