核心概念
Contract-first
Section titled “Contract-first”Protobuf 是單一事實來源。你只寫兩種東西:proto contract 和 logic 實作。gRPC stubs、HTTP/JSON gateway、OpenAPI spec、MCP tool schema 全部由 gortexa regen 從 proto 產生,流程固定是 buf lint → buf breaking → buf generate。
gen/下全是產物:不進版控、不手動編輯。- breaking-change gate 內建在流程裡:會破壞相容性的 proto 修改直接被擋下,additive 修改放行。
單一 h2c port 多工
Section titled “單一 h2c port 多工”一個 cleartext HTTP/2 port,依 Content-Type 與路徑分派:
| 請求 | 去向 |
|---|---|
Content-Type: application/grpc |
gRPC(grpc-go 的 ServeHTTP 模式) |
路徑 /mcp |
MCP bridge(Streamable HTTP) |
| 其他 | grpc-gateway(HTTP/JSON)與內建路由(/healthz、/openapi.json) |
h2c 用的是 Go 原生 http.Server.Protocols,不是已棄用的 x/net/h2c。cleartext 表示部署時 TLS 必須在前面終結,見部署。
八段、順序固定:
recover → request-id → logger → load-shed → rate-limit → circuit-breaker → auth → validation鏈不完整時啟動直接失敗(fail-loud),不會默默少掛一段。OTel 以 gRPC StatsHandler 掛載,unary 與 streaming 都涵蓋。
順序固定,但 auth 段可插拔:預設是 HS256 JWT(interceptor.Config.Verifier),而 auth 段實際跑的是一個 auth.Authenticator——非 JWT 方案(static bearer/mTLS/API-key)改設 Config.Authenticator 即可,八段順序不變。要在鏈上加自己的 interceptor,用 kernel.WithServerOptions(...)(gRPC 會接在框架鏈之後、handler 之前,仍在 recover 之內),而非改動這八段。
鏈上三段治理的預設值定義在 cmd/server/main.go(不在 config.yaml——要調整就改 interceptor.NewSet 的參數重新編譯):
| 段 | 預設值 | 超限行為 |
|---|---|---|
| rate-limit | 每 peer 200 RPS、burst 100、追蹤 TTL 10 分鐘 | RESOURCE_EXHAUSTED(HTTP 429) |
| circuit-breaker | 連續 5 次失敗開路 10 秒、half-open 放 2 個探測 | UNAVAILABLE(HTTP 503) |
| load-shed | in-flight 上限 1,024(gRPC health 檢查豁免) | RESOURCE_EXHAUSTED(HTTP 429) |
量測數字(框架 README 的基準測試,Apple M1/go1.26.5):全鏈 unary 一趟約 1,552 ns、27 allocs;rate-limiter Allow 熱路徑 0 allocs(allocs 機器無關)。
統一錯誤模型
Section titled “統一錯誤模型”一張對應表決定同一個錯誤在三個協議的輸出:gRPC status、HTTP status、MCP error envelope。原則是內部原因不外洩——只有 invalid-argument 與 unauthenticated 兩類會把訊息轉發給呼叫端,其餘類別一律回 registry 中的安全訊息。
錯誤解析熱路徑約 105 ns、2 allocs(用 Go 1.26 的 errors.AsType)。
| 組件 | 說明 |
|---|---|
| Config | 分層載入、fail-loud、secrets 遮罩。見設定 |
| Health | 三態健康對應 gRPC Health;app.Health().Register(...) 接真實依賴 |
| Logging/Observability | slog 結構化日誌 + OTLP 匯出 |
| Database | PgBouncer-safe 的 pgx pool + sqlc |
| Cache | 預設 process-local in-memory;cache.driver: redis 切換分散式(零依賴 RESP client) |
| MQ | NATS(core 或 JetStream);mq.group_id 空=fanout、非空=load-balance |