connect-goでHTTP GETリクエストを受け取る

こちらは TVer Advent Calendar 2024 の2日目の記事です。
1日目の記事は@ukitakaさんのTVerにおける技術統括事務局の取り組みでした。


趣味で開発しているWebサービスでconnect-goを利用しています。

github.com

connect-goはgRPC, gRPC-Web, Connectと3つのプロトコルに対応しています。
特にConnectプロトコルを利用すると、Webブラウザからでもenvoyなどのプロキシサーバー不要でprotobufからコード生成した型を利用したRPCメソッド呼び出しができるため大変便利です。

connectrpc.com

ただキャッシュの面で困るケースがあるため、その回避策を試してみます。

Connectプロトコルを使った通信

まずは素直に公式ドキュメントのGetting started | Connectに従ってサンプル実装をつくります。
Connectプロトコルを使ったAPIサーバーはHTTP POST形式でリクエストができます。

> curl \
    --header "Content-Type: application/json" \
    --data '{"name": "Jane"}' \
    http://localhost:8080/greet.v1.GreetService/Greet

{"greeting":"Hello, Jane!"}

> curl \
    --header "Content-Type: application/json" \
    --data '{"name": "Emma"}' \
    http://localhost:8080/greet.v1.GreetService/Greet

{"greeting":"Hello, Emma!"}

ところがパラメータがリクエストbodyに含まれるため、動的なAPI(今回は名前がパラメータ)の場合
CDNを使ってAPIレスポンスをキャッシュしたいケースで困ります。

ConnectプロトコルのHTTP GET利用モード

それを回避するためConnectにはHTTP GETなリクエストでRPCメソッドを呼び出す機能が存在します。

buf.build

この機能を有効にすることで、リクエストパラメータがURLのクエリパラメータとして埋め込まれHTTP GETリクエストが発行されます。
今回の例ではGoのクライアントを使用しています。

protoファイルのrpcメソッドにオプションを追加

service GreetService {
  rpc Greet(GreetRequest) returns (GreetResponse) {
    option idempotency_level = NO_SIDE_EFFECTS;
  }
}

Connect Clientにオプションを追加。

client := greetv1connect.NewGreetServiceClient(
        http.DefaultClient,
        "http://localhost:8080",
        connect.WithHTTPGet(),
    )

この状態でサーバーのプロセスを起動し、クライアントからAPIリクエストしてみます。

> go run cmd/client/main.go
2024/11/17 16:10:12 Hello, Jane!

# サーバーのログ
time=2024-11-17T16:09:27.878+09:00 
level=INFO 
msg=request 
req.addr=127.0.0.1:50027 
req.protocol=connect 
req.method=GET 
req.procedure=/greet.v1.GreetService/Greet 
req.query="base64=1&connect=v1&encoding=proto&message=CgRKYW5l" 
req.duration=5.625µs

protobufに定義されているパラメータはbase64エンコードされているため、ASCII文字以外のパラメータの送信も可能です。

> go run cmd/client/main.go
2024/11/17 16:11:02 Hello, 👍!

# サーバーのログ
time=2024-11-17T16:11:02.306+09:00 
level=INFO 
msg=request 
req.protocol=connect 
req.method=GET 
req.procedure=/greet.v1.GreetService/Greet 
req.query="base64=1&connect=v1&encoding=proto&message=CgTwn5GN" 
req.duration=5.25µs

最初のサンプルと違い2つのリクエストではURLが異なるため、レスポンスに Cache-Control ヘッダーを付与すればCDNでのキャッシュが可能です。

まとめ

connect-goを使いつつHTTP GET形式のリクエスト発行ができました。
またこの検証のためにサーバーのアクセスログを出力するmiddlewareも実装しましたが、connect-goのサーバーはnet/httpを利用するためこれまでの資産も使いやすいです。

今回のサンプル実装はこちらのリポジトリに公開しています。

GitHub - takanamito/connect-go-example


明日の記事は@ko-ya346さんの「terraform + github でデータマート管理する」です。

qiita.com