connect-go
gRPCサーバとHTTPサーバを両立できるのはなぜ?
A. 内部的にそれぞれのプロトコルに応じた処理が入っているから
Connect servers and clients support three protocols: gRPC, gRPC-Web, and Connect's own protocol.
...
By default, Connect servers support ingress from all three protocols. Clients default to using the Connect protocol, but can switch to gRPC or gRPC-Web with a configuration toggle — no further code changes required. The APIs for errors, headers, trailers, and streaming are all protocol-agnostic.
https://connectrpc.com/docs/introduction/#seamless-multi-protocol-support
3種類のプロトコルに応じたhandlerが存在していて
このレイヤーで選択したprotocolに対応したhandlerが実行される
おおまかな流れメモ
自動生成されたコードを元に、Connect Server Handlerが作成される
- connect.NewUnaryHandlerで、対応するプロトコルごとのhandlerが作成される。(connect、gRPC、gRPC-Web)
https://github.com/connectrpc/connect-go/blob/2a694ed9af72cb047fadff39dcad386f51fd5d92/handler.go#L78-L85
https://github.com/connectrpc/connect-go/blob/2a694ed9af72cb047fadff39dcad386f51fd5d92/handler.go#L285-L289
サーバがリクエストをハンドリングする
-
connect.Handler.ServeHTTP が呼ばれる
https://github.com/connectrpc/connect-go/blob/2a694ed9af72cb047fadff39dcad386f51fd5d92/handler.go#L159-L160 -
リクエストのメソッド方式(GET、POSTなど)に応じて、handlerの候補群が選択される
https://github.com/connectrpc/connect-go/blob/2a694ed9af72cb047fadff39dcad386f51fd5d92/handler.go#L174-L179 -
contentTypeなどをベースに、該当のhandlerを最終的に決定する
https://github.com/connectrpc/connect-go/blob/2a694ed9af72cb047fadff39dcad386f51fd5d92/handler.go#L181-L189
connect上のinterceptorと、HTTPレベルのmiddlewareの使い分けは必要か?
Logger 等の Protocol 共通の処理は net/http の Middleware、Connect 固有の実装を行う場合は Interceptor というように使い分けています。
「net/httpレイヤーのmiddleware処理」と「connectレイヤーのmiddleware処理」で使い分けるのは確かに良さそう。
「connectレイヤーのみで実現できるmiddleware処理」の具体例があるかあまりイメージがわかない
CNCFのslackで質問してみた。
やはり実現したいことがHTTPレイヤーとConnectレイヤーのどちらでやったほうがいいか判断して決めてるようだ。
メトリクス、トレース周りではHTTPレイヤーとConnectレイヤーでそれぞれ取得できる情報に違いがあるっぽいので参考になりそう
I personally use a connect interceptor because it has a bit more context about what is actually being called (more specific than just the HTTP path) and way more information about the type of errors.
You didn’t ask, but for metrics and traces, I use both otelhttp and otelconnect-go because they give different information.
I use a mix of both, depending on what you goal is. Being able to leverage HTTP level middleware is nice for generic access log style logging, but you don't have visibility into what the RPC calls are, etc. but you get the upside of being able to log errors or 404s on paths that don't map to RPC handlers.
So they're just different things. It's also nice to do things at HTTP level if you multiplex with different non RPC APIs/endpoints. So I don't think there's any best practice advice. It's just what detail of insight you want.