# gRPC

Source: https://gortexa.sheng.page/transports/grpc/

gRPC 是 Gortexa 的原生協定。外部連線走同一個 h2c port，由 mux 依 `Content-Type: application/grpc` 分派給 grpc-go 的 `ServeHTTP` 處理模式。

## 服務定義與產生碼

服務定義在 proto。內建的 sample resource：

```protobuf
service ResourceService {
  rpc CreateResource(CreateResourceRequest) returns (Resource);
  rpc GetResource(GetResourceRequest) returns (Resource);
  rpc ListResources(ListResourcesRequest) returns (ListResourcesResponse);
  rpc UpdateResource(UpdateResourceRequest) returns (Resource);
  rpc DeleteResource(DeleteResourceRequest) returns (google.protobuf.Empty);
}
```

`gortexa regen` 把 stubs 產到 `gen/`，logic 實作放在 internal package，兩者不混。

## 呼叫服務

本機測試若要用 grpcurl，先開 reflection（預設關閉）：

```bash
GORTEXA_SERVER__REFLECTION=true gortexa run
```

```bash
grpcurl -plaintext \
  -H 'authorization: Bearer <token>' \
  -d '{"id":"x"}' \
  localhost:8080 myapp.resource.v1.ResourceService/GetResource
```

`myapp` 是 scaffold 專案的 proto namespace（自 v0.28.0 起由 module path 推出，見[建立專案](https://gortexa.sheng.page/quickstart/#建立專案)）；在框架 checkout 內直接跑 sample 時，服務名是 `resource.v1.ResourceService`。

沒帶 token 會得到 `UNAUTHENTICATED`：auth 對三個協定一視同仁。

## Health

三態健康狀態對應到標準 gRPC Health protocol。用 `app.Health().Register(...)` 註冊真實依賴檢查，`/readyz` 與 gRPC Health 才會反映 DB、cache、MQ 的實際可達性。每個 check 有 5 秒逾時，慢的依賴不會卡住整個 health 查詢。`Health.Watch` 因為同時豁免 auth 與 load shedding（伺服器正在 shed 時探測仍須能回答），另有自己的上限：最多 1,024 條並行 watch 串流，超過回 `RESOURCE_EXHAUSTED`；所有 watcher 共用每秒一次的 registry 評估，N 條串流不等於 N 次依賴探測。註冊同名 check 會 panic（開機期的接線錯誤應該吵，而不是默默覆蓋掉某個依賴的監控）；真的要替換請用 `Replace(name, check)`。

## Reflection

gRPC reflection 預設關閉，需要時以 config 或環境變數開啟：

```bash
GORTEXA_SERVER__REFLECTION=true
```

開啟後 `/grpc.reflection.` 同時豁免 auth **與** in-flight 預算（串流整個生命週期都佔一個名額，若只豁免 auth 不豁免計數，未認證的 client 就能釘住預算）。生產環境建議保持關閉，理由見[部署](https://gortexa.sheng.page/deployment/)。

## 常見問題

### Go 可以不用 TLS 直接跑 gRPC（h2c）嗎？

可以。Go 1.24 起 `http.Server.Protocols` 原生支援 h2c，Gortexa 就是用它——不需要已被棄用的 `x/net/http2/h2c` wrapper。正式環境請在前面終結 TLS。

### 正式環境該開 gRPC reflection 嗎？

不建議。開啟後 `/grpc.reflection.` 會同時豁免 auth 與 in-flight 預算，等於把完整的服務 schema 公開給未認證的呼叫端。它預設關閉，留給 `buf curl --reflect` 這類開發工具用。

### keepalive 設定為什麼沒有生效？

因為共用 h2c 埠上的對外 gRPC 跑的是 grpc-go 的 `ServeHTTP` handler 模式，傳輸層選項（`KeepaliveParams`、`MaxStreams` 等）在那裡是無效的。需要 keepalive 強制就得把 gRPC 拆到自己的 listener。
