goでgRPCのAPIを実装する go and Connect
Connectというシンプルでスリムなライブラリを使って、gRPCのAPIサーバを開発してみます。
Connect は、ブラウザーおよび gRPC と互換性のある HTTP API を構築するためのスリムなライブラリです。Protocol Buffer スキーマを使用してサービスを定義すると、Connect がタイプ セーフなサーバー コードとクライアント コードを生成します。サーバーのビジネス ロジックを入力すれば完了です。手書きのマーシャリング、ルーティング、またはクライアント コードは必要ありません。
前提:
go 1.18
buf
プロジェクト作成
$ mkdir connect-go-example
$ cd connect-go-example
initとインストール
go mod init example
go install github.com/bufbuild/buf/cmd/buf@latest
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install github.com/bufbuild/connect-go/cmd/protoc-gen-connect-go@latest
bufが必要なので、インストールします。(Mac,Linux)
brew install bufbuild/buf/buf
これで、サービスを定義する Protocol Buffer スキーマを作成する準備が整いました。
$ mkdir -p greet/v1
$ touch greet/v1/greet.proto
greet/v1/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) {}
}
このファイルは、greet.v1Protobuf パッケージ、 というサービス GreetService、およびその要求構造と応答構造で呼び出される単一のメソッドを宣言しますGreet。これらのパッケージ、サービス、およびメソッドの名前は、まもなく HTTP API の URL に再表示されます。
生成
Google の protobuf コンパイラの最新の代替品であるBuf を使用してコードを生成します。前に Buf をインストールしましたが、作業を開始するにはいくつかの構成ファイルも必要です。(必要に応じて、このセクションをスキップしてprotoc代わりに使用できます—他のプラグインと同じように動作します。) protoc-gen-connect-go
buf.yamlまず、を実行して基本をスキャフォールディングしますbuf mod init。次に、これを に入れ、コードを生成する方法を Buf に伝えます buf.gen.yaml。
version: v1
plugins:
- name: go
out: gen
opt: paths=source_relative
- name: connect-go
out: gen
opt: paths=source_relative
これらの構成ファイルを配置したら、スキーマをリントしてコードを生成できます。
$ buf lint
$ buf generate
genディレクトリとgoファイルが生成されるはずです。
gen
└── greet
└── v1
├── greet.pb.go
└── greetv1connect
└── greet.connect.go
パッケージには、Google の によって生成された がgen/greet/v1含まれており、と 構造体、および関連するコードが含まれています。このパッケージ によって生成されたファイルが含まれており、HTTP ハンドラ、クライアント インターフェイス、およびコンストラクタが含まれています。
サーバーを生成する。
生成したコードはボイラープレートを処理しますが、greetのロジックを実装する必要があります。
生成されたコードでは、これはgreetv1connect.GreetServiceHandlerインターフェイスとして表されます。
インターフェイスが非常に小さいため、1 つの Go パッケージですべてを実行できます。
mkdir -p cmd/server
go run cmd/server/main.go
ファイル:main.goの記載します。
package main
import (
"context"
"fmt"
"log"
"net/http"
"github.com/bufbuild/connect-go"
"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{}),
)
}
このGreetメソッドはジェネリックを使用しています。 connect.Requestandconnect.Response型は、ヘッダーとトレーラーへの直接アクセスを提供する一方で、生成された greetv1.GreetRequestおよびgreetv1.GreetResponse構造体への厳密に型指定されたアクセスを提供します。
ターミナル ウィンドウで、go.modサーバーを更新して起動できるようになりました。
$ go get golang.org/x/net/http2
$ go get github.com/bufbuild/connect-go
$ go run ./cmd/server/main.go
新しい API を使用する最も簡単な方法は、JSON ペイロードを使用した HTTP/1.1 POST です。
最新バージョンの cURL がインストールされている場合は次のワンライナーで確認可能です。
$ curl \
--header "Content-Type: application/json" \
--data '{"name": "Jane"}' \
http://localhost:8080/greet.v1.GreetService/Greet
次のように応答されます。
{"greeting": "Hello, Jane!"}
新しいハンドラーは gRPC リクエストも自動的にサポートします。
$ grpcurl \
-protoset <(buf build -o -) -plaintext \
-d '{"name": "Jane"}' \
localhost:8080 greet.v1.GreetService/Greet
これは次のように応答します。
{
"greeting": "Hello, Jane!"
}
Connect の生成されたクライアントを使用してリクエストを行うこともできます。mkdir -p cmd/clientこれを入れますcmd/client/main.go:
package main
import (
"context"
"log"
"net/http"
greetv1 "example/gen/greet/v1"
"example/gen/greet/v1/greetv1connect"
"github.com/bufbuild/connect-go"
)
func main() {
client := greetv1connect.NewGreetServiceClient(
http.DefaultClient,
"http://localhost:8080",
)
res, err := client.Greet(
context.Background(),
connect.NewRequest(&greetv1.GreetRequest{Name: "Jane"}),
)
if err != nil {
log.Println(err)
return
}
log.Println(res.Msg.Greeting)
}
サーバーが別のターミナル ウィンドウで実行されている状態で、クライアントを実行できるようになりました。
$ go run ./cmd/client/main.go
これで、最初の Connect サービスが構築されました。
ありがとうございます。