Skip to content

Commit e4ca3b6

Browse files
committed
refactor(contract): wire metadata 控制键统一小驼峰——17 键原子重命名 + 文档全面对齐
按契约命名规范把 wire 调用链 metadata 控制键从 snake_case 统一为 lowerCamelCase(禁止双读兼容,同一变更改完所有读写方): gameId/serviceId/targetServiceId/hashKey/timeoutMs/traceId/taskId/ agentId/scheduleId/sdkLanguage/sdkVersion/sdkName/approvalBypass/ pageSnapshotGovernance/approvalActor/approvalId/idempotencyKey (traceparent 保留——W3C 标准名) - Go 平台侧:dispatcher/function helpers/agent(local_handler·upstream· app·task_runner)/approval/task/scheduler/logic/console/telemetry 常量 - SDK:Go/Python/JS trace 辅助键、JS handler context 的 idempotencyKey - 范围外数据面(本轮明确不动,避免误伤):analytics 事件 schema(数仓 SQL 惯例)、审计/审批 REST details map、ops 快照归一化层(双读别名 为历史数据兼容)、C++ SDK 配置文件键——均已登记为后续对齐项 - 文档对齐:wire 协议文档(超时小节/字段分层)、otel 传播文档、 REST API 文档漂移修正(targetServiceId/hashKey 对齐代码实际键)、 SDK 配置推荐表改 canonical 小驼峰、data-flow Instance.Metadata 键、 architecture index serviceId 验证:go test ./internal/... ./cmd/... 0 FAIL;go/python/js SDK 全绿 (python 489+ / js 350);docs build 通过。
1 parent d42e24f commit e4ca3b6

61 files changed

Lines changed: 327 additions & 1299 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CLAUDE.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,9 @@ cmd/ # Binary entry points (server, agent, unified CLI)
175175
proto/ # Protobuf definitions (Buf workspace)
176176
internal/server/ # Server business logic (control, function, http, registry)
177177
internal/agent/ # Agent logic (tunnel, local server, jobs)
178-
internal/auth/ # RBAC, JWT, TOTP, user management
179-
internal/function/ # Descriptor loading and validation
178+
internal/security/ # RBAC, JWT, TOTP, identity providers (local/LDAP/OIDC)
179+
internal/api/ # REST API handlers (auth, admin, role, permission, approval, audit, ...)
180+
internal/function/ # Descriptor loading and validation
180181
internal/jobs/ # Job state machine and execution
181182
internal/loadbalancer/ # Load balancing strategies (RR, consistent hash, least conn)
182183
sdks/ # Multi-language SDKs (go, python, java, js, cpp, csharp)
@@ -224,9 +225,9 @@ examples/ # Demo game servers and invokers
224225

225226
Unit tests focus on:
226227

227-
- RBAC policy grant/deny logic (`internal/auth/rbac/`)
228+
- RBAC policy grant/deny logic (`internal/security/rbac/`)
228229
- Job executor state transitions and idempotency (`internal/agent/jobs/`)
229-
- Sensitive field masking (`internal/server/http/`)
230+
- Sensitive field masking (`internal/api/` response helpers)
230231
- Pack import/export workflows
231232
- Registry agent session management
232233

docs/api/function.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,8 @@ type FunctionInvokeRequest struct {
231231
Env string `json:"env,optional"` // 兼容字段;生效 scope 以 X-Game-ID/X-Env 请求头为准
232232
Mode string `json:"mode,optional"`
233233
Route string `json:"route,optional"`
234-
TargetServiceID string `json:"target_service_id,optional"`
235-
HashKey string `json:"hash_key,optional"`
234+
TargetServiceID string `json:"targetServiceId,optional"`
235+
HashKey string `json:"hashKey,optional"`
236236
}
237237
```
238238

docs/architecture/data-flow.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,9 @@ sequenceDiagram
7070

7171
当一个 Agent 后挂多个游戏服务(service)时,Agent 内部按 **Nacos 风格的双层索引**选择目标实例:
7272

73-
- **注册期**:SDK 的 `ProviderConnectRequest` 携带 `service_id``Metadata``sdk_language`/`sdk_version`/`game_id`/`env` 等)。Agent 以 `functionId → serviceId → []Instance` 双层索引登记,实例带 `LastSeen` 用于健康判断(`internal/platform/agentlocal/store.go`)。
73+
- **注册期**:SDK 的 `ProviderConnectRequest` 携带 `service_id``Metadata`proto 字段 `sdk_language`/`sdk_version`/`game_id`/`env` 等)。Agent 以 `functionId → serviceId → []Instance` 双层索引登记,实例带 `LastSeen` 用于健康判断(`internal/platform/agentlocal/store.go`)。
7474
- **调用期**`pickInstance``internal/agent/local_handler.go`)先按 `functionId` 取 service 索引;若 invoke metadata 带 `service_id` 则精确落到该 service 的实例集合,否则合并该函数下所有 service 的实例;再按 `LastSeen` 过滤健康实例并做负载均衡。
75-
- `Instance.Metadata` 负责透传 SDK 元信息(`sdk_language`/`sdk_version`),最终经 `AgentProcess → ProviderSession` 暴露到 opsNodes。
75+
- `Instance.Metadata` 负责透传 SDK 元信息(键为 `sdkLanguage`/`sdkVersion`/`gameId` 等小驼峰),最终经 `AgentProcess → ProviderSession` 暴露到 opsNodes。
7676

7777
## 3. SDK 注册到 Agent
7878

docs/architecture/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Croupier 当前的目标架构已经从"多条回拨链路 + 历史 旧传输/gR
2525
- `env` 表达逻辑生命周期阶段,不直接表示物理部署位置
2626
- `scope``target` 必须分离
2727
- **数据库采用按游戏分库架构**
28-
- **多服务可共享同一个 Agent**:一个 Agent 可接入多个 game service,函数路由按 `functionId → serviceId → Instance` 双层索引(Nacos 风格),invoke 可在 metadata 带 `service_id` 精确路由
28+
- **多服务可共享同一个 Agent**:一个 Agent 可接入多个 game service,函数路由按 `functionId → serviceId → Instance` 双层索引(Nacos 风格),invoke 可在 metadata 带 `serviceId` 精确路由
2929

3030
## 总体拓扑
3131

docs/architecture/sdk-agent-transport-redesign.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ Agent 至少需要实现:
393393
- `trace_id`
394394
- `game_id`
395395
- `env`
396-
- `timeout_ms`
396+
- `timeoutMs`(metadata 键)
397397
- `retry` / `priority` 这类平台治理字段
398398

399399
应留在 JSON payload 的字段:
@@ -580,20 +580,20 @@ SDK 收到这些信号后,可以:
580580

581581
所有 SDK 建议统一收敛到以下字段语义:
582582

583-
| Field | 说明 |
584-
| ------------------------------ | ---------------- |
585-
| `transport.kind` | 固定优先为 `tcp` |
586-
| `transport.address` | Agent 地址 |
587-
| `transport.connect_timeout_ms` | 连接超时 |
588-
| `transport.request_timeout_ms` | 请求超时 |
589-
| `transport.tls` | TLS 配置 |
590-
| `reconnect.enabled` | 是否自动重连 |
591-
| `reconnect.initial_delay_ms` | 初始退避 |
592-
| `reconnect.max_delay_ms` | 最大退避 |
593-
| `reconnect.backoff_multiplier` | 指数退避倍率 |
594-
| `reconnect.jitter_factor` | 抖动因子 |
595-
| `backpressure.max_concurrency` | 本地并发上限 |
596-
| `backpressure.max_queue_size` | 本地排队上限 |
583+
| Field | 说明 |
584+
| ----------------------------- | ---------------- |
585+
| `transport.kind` | 固定优先为 `tcp` |
586+
| `transport.address` | Agent 地址 |
587+
| `transport.connectTimeoutMs` | 连接超时 |
588+
| `transport.requestTimeoutMs` | 请求超时 |
589+
| `transport.tls` | TLS 配置 |
590+
| `reconnect.enabled` | 是否自动重连 |
591+
| `reconnect.initialDelayMs` | 初始退避 |
592+
| `reconnect.maxDelayMs` | 最大退避 |
593+
| `reconnect.backoffMultiplier` | 指数退避倍率 |
594+
| `reconnect.jitterFactor` | 抖动因子 |
595+
| `backpressure.maxConcurrency` | 本地并发上限 |
596+
| `backpressure.max_queue_size` | 本地排队上限 |
597597

598598
### 废弃字段
599599

docs/architecture/sdk-otel-propagation.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Croupier 的实现遵循两个业界标准,使用者无需学习私有概念
4040
│ trace 开始(或延续外部 traceparent)
4141
│ telemetry.InjectContext(ctx, metadata)
4242
│ metadata["traceparent"] = "00-<traceId>-<spanId>-01"
43-
│ metadata["trace_id"] = "<traceId>" ← 冗余明文,便于弱端读取
43+
│ metadata["traceId"] = "<traceId>" ← 冗余明文,便于弱端读取
4444
4545
function.dispatch.invoke (SpanKind=Client)
4646
│ ExtractContext 延续 trace → 选 Agent → TCP 转发
@@ -86,7 +86,7 @@ SpanKind 语义:`Server` = 承接入站请求,`Client` = 发起出站调用
8686
| ------------- | ----------------------------------------------------------- | --------------------------------------------- |
8787
| `traceparent` | W3C:`00-{32位hex traceId}-{16位hex spanId}-{2位hex flags}` | 标准跨进程传播字段,otel SDK 可直接 Extract |
8888
| `tracestate` | W3C:厂商扩展键值对(通常为空) | 透传保留 |
89-
| `trace_id` | 32 位 hex 明文 | 冗余字段:不解析 `traceparent` 的简单端直接读 |
89+
| `traceId` | 32 位 hex 明文 | 冗余字段:不解析 `traceparent` 的简单端直接读 |
9090

9191
W3C `traceparent` 示例:
9292

@@ -168,7 +168,7 @@ OTEL_ENABLE_TRACING=true
168168
```
169169

170170
未开启 tracing(或 collector 不可达)时:**传播字段照常注入 metadata**
171-
`traceparent`/`trace_id` 仍存在),但 span 不会被导出,响应 `traceId`
171+
`traceparent`/`traceId` 仍存在),但 span 不会被导出,响应 `traceId`
172172
为空。属部署配置,非代码缺口。
173173

174174
### Dashboard 跳转(决定"能不能一键跳")
@@ -188,14 +188,14 @@ docker run --rm -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one:latest
188188

189189
## SDK 现状(六语言)
190190

191-
| 语言 | 响应透出 traceId | 请求注入 traceparent | Provider 提取延续 | 备注 |
192-
| ------ | ---------------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------ |
193-
| Go | ✅(DTO `TraceID`| | ✅ 一期(ctx 注入 `WithTraceMetadata` + `TraceParentFromContext`/`TraceIDFromContext`| 一期 |
194-
| Python | ✅(invoker `trace_id`| | ✅ 一期(context JSON 随 metadata 透传 + `croupier.trace` 读取辅助) | 一期 |
195-
| JS | ✅(`InvokeResult.traceId`| | ✅ 一期(context JSON 随 metadata 透传 + `traceParentFromContext`/`traceIdFromContext`| 一期 |
196-
| Java || || 二期按需 |
197-
| C# || || 二期按需 |
198-
| C++ | 部分(task 状态) | 部分(`trace_id` 明文注入 metadata,非 W3C) || `InvokeOptions.trace_id` |
191+
| 语言 | 响应透出 traceId | 请求注入 traceparent | Provider 提取延续 | 备注 |
192+
| ------ | ---------------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------- | ----------------------- |
193+
| Go | ✅(DTO `TraceID`|| ✅ 一期(ctx 注入 `WithTraceMetadata` + `TraceParentFromContext`/`TraceIDFromContext`| 一期 |
194+
| Python | ✅(invoker `trace_id`|| ✅ 一期(context JSON 随 metadata 透传 + `croupier.trace` 读取辅助) | 一期 |
195+
| JS | ✅(`InvokeResult.traceId`|| ✅ 一期(context JSON 随 metadata 透传 + `traceParentFromContext`/`traceIdFromContext`| 一期 |
196+
| Java |||| 二期按需 |
197+
| C# |||| 二期按需 |
198+
| C++ | 部分(task 状态) | 部分(`traceId` 明文注入 metadata,非 W3C) || `InvokeOptions.traceId` |
199199

200200
一期口径说明:Provider 侧"提取延续"当前为**无 otel 依赖的传播**——trace
201201
字段进入 handler 上下文(Go 为 context value,Python/JS 为 context JSON

docs/architecture/sdk-wire-protocol.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,15 @@ v1 不引入独立 `Magic`,而是直接用首条应用层消息识别子协议
120120
- `responseMsgID = requestMsgID + 1` 仍是默认约定
121121
-`TaskEvent` 这样的单向事件消息不属于标准 request/response 配对
122122

123-
### 同步调用超时:metadata `timeout_ms` 约定
123+
### 同步调用超时:metadata `timeoutMs` 约定
124124

125125
调用方声明的一次同步调用预算(毫秒,字符串十进制),放在 invoke 请求的
126126
metadata map 中端到端传播,各跳取 **min(本跳配置, 声明值)** 生效(Go
127127
context deadline 的天然 min 语义):
128128

129129
|| 行为 |
130130
| ----------- | ------------------------------------------------------------------------------------------------------- |
131-
| HTTP API | 请求体 `timeoutMs`(可选)→ 注入 `metadata["timeout_ms"]` |
131+
| HTTP API | 请求体 `timeoutMs`(可选)→ 注入 `metadata["timeoutMs"]` |
132132
| Server 派发 | `requestTimeoutBudget`:clamp [1000, 60000],收紧 ctx;全局默认 15s 只作上限 |
133133
| Agent | `providerCallDeadline`:clamp [1000, Agent 配置上限],与默认(`agent.invokeTimeoutMs`,默认 15000)取小 |
134134

@@ -306,11 +306,11 @@ v1 默认规则:
306306
- `function_id`
307307
- `session_id`
308308
- `idempotency_key`
309-
- `trace_id`
309+
- `traceId`
310310
- W3C trace 传播详见 `docs/architecture/sdk-otel-propagation.md`(SDK 只做传播不做导出)
311-
- `game_id`
311+
- `gameId`
312312
- `env`
313-
- `timeout_ms`
313+
- `timeoutMs`
314314
- `priority`
315315
- 能力协商、限流、重试、审计相关字段
316316

docs/operations/config-agent.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ tag:
2626

2727
### agent(身份与可达性)
2828

29-
|| 默认 | 说明 |
30-
| ---------------------------- | --------------- | ---------------------------------------------------------------------------------------------------- |
31-
| `agent.id` | `""` | 留空自动生成;多 Agent 部署建议显式指定(拓扑页可读性) |
32-
| `agent.gameId` / `agent.env` | `""` | 作用域绑定;留空由注册的游戏服声明 |
33-
| `agent.localAddr` | `0.0.0.0:19091` | 本地 TCP 监听(游戏服函数注册入口) |
34-
| `agent.httpAddr` | 必填 | **Server→Agent 回调地址**,必须是 Server 视角可达的地址(容器网络用服务名,裸机用 IP) |
35-
| `agent.labels` | `{}` | 自定义标签(机房/机型等,节点页过滤用) |
36-
| `agent.invokeTimeoutMs` | `15000` | Agent→游戏服同步调用默认预算(毫秒);请求 `metadata["timeout_ms"]` 声明更小值时取更小者,上限 60000 |
29+
|| 默认 | 说明 |
30+
| ---------------------------- | --------------- | --------------------------------------------------------------------------------------------------- |
31+
| `agent.id` | `""` | 留空自动生成;多 Agent 部署建议显式指定(拓扑页可读性) |
32+
| `agent.gameId` / `agent.env` | `""` | 作用域绑定;留空由注册的游戏服声明 |
33+
| `agent.localAddr` | `0.0.0.0:19091` | 本地 TCP 监听(游戏服函数注册入口) |
34+
| `agent.httpAddr` | 必填 | **Server→Agent 回调地址**,必须是 Server 视角可达的地址(容器网络用服务名,裸机用 IP) |
35+
| `agent.labels` | `{}` | 自定义标签(机房/机型等,节点页过滤用) |
36+
| `agent.invokeTimeoutMs` | `15000` | Agent→游戏服同步调用默认预算(毫秒);请求 `metadata["timeoutMs"]` 声明更小值时取更小者,上限 60000 |
3737

3838
> `httpAddr` 是最常见的部署错误:写 `0.0.0.0``localhost` 会导致 Server 回调失败——job 下发/文件传输走不通。
3939

internal/agent/local_handler.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ type LocalHandler struct {
6868
expectedGameID string // Agent 配置的 gameId,用于校验 SDK 注册
6969
expectedEnv string // Agent 配置的 env,用于校验 SDK 注册
7070
// providerCallTimeout 是 Agent → Provider 同步调用的默认预算;
71-
// 请求 metadata["timeout_ms"] 声明更小值时取更小者(Go deadline
71+
// 请求 metadata["timeoutMs"] 声明更小值时取更小者(Go deadline
7272
// min 语义)。此前硬编码 10s 与 Server 派发层 15s 倒挂。
7373
providerCallTimeout time.Duration
7474
mu sync.RWMutex
@@ -124,7 +124,7 @@ func (h *LocalHandler) providerCallDeadline(meta map[string]string) time.Duratio
124124
if def <= 0 {
125125
def = 15 * time.Second
126126
}
127-
raw := strings.TrimSpace(meta["timeout_ms"])
127+
raw := strings.TrimSpace(meta["timeoutMs"])
128128
if raw == "" {
129129
return def
130130
}
@@ -248,8 +248,8 @@ func (h *LocalHandler) handleInvoke(ctx context.Context, data []byte) ([]byte, e
248248
trace.WithAttributes(
249249
attribute.String("function.id", functionID),
250250
attribute.String("agent.id", h.agentID),
251-
attribute.String("service.id", req.GetMetadata()["service_id"]),
252-
attribute.String("task.id", req.GetMetadata()["task_id"]),
251+
attribute.String("service.id", req.GetMetadata()["serviceId"]),
252+
attribute.String("task.id", req.GetMetadata()["taskId"]),
253253
),
254254
)
255255
defer span.End()
@@ -307,7 +307,7 @@ func (h *LocalHandler) callProvider(ctx context.Context, functionID string, meta
307307
sessions := h.providerSessions
308308
h.mu.RUnlock()
309309
if sessions != nil {
310-
serviceID := metadata["service_id"]
310+
serviceID := metadata["serviceId"]
311311
if serviceID != "" {
312312
if session, ok := sessions.GetByServiceID(serviceID); ok && session.Conn() != nil {
313313
_, response, err := session.Conn().Call(ctx, msgID, data)
@@ -385,7 +385,7 @@ func (h *LocalHandler) pickInstance(functionID string, metadata map[string]strin
385385
}
386386

387387
// 按 service_id 路由(一级过滤)
388-
targetService := metadata["service_id"]
388+
targetService := metadata["serviceId"]
389389
var arr []agentlocal.Instance
390390
if targetService != "" {
391391
arr = serviceMap[targetService]
@@ -462,7 +462,7 @@ func (h *LocalHandler) executeTask(ctx context.Context, req *sdkv1.InvokeRequest
462462
trace.WithAttributes(
463463
attribute.String("function.id", req.GetFunctionId()),
464464
attribute.String("agent.id", h.agentID),
465-
attribute.String("task.id", req.GetMetadata()["task_id"]),
465+
attribute.String("task.id", req.GetMetadata()["taskId"]),
466466
),
467467
)
468468
defer span.End()
@@ -818,11 +818,11 @@ func (h *LocalHandler) handleProviderConnect(ctx context.Context, data []byte) (
818818
}
819819
// 提取元数据(参考 Nacos metadata)
820820
metadata := map[string]string{
821-
"sdk_language": req.SdkLanguage,
822-
"sdk_version": req.SdkVersion,
823-
"sdk_name": req.SdkName,
821+
"sdkLanguage": req.SdkLanguage,
822+
"sdkVersion": req.SdkVersion,
823+
"sdkName": req.SdkName,
824824
"protocol_version": req.ProtocolVersion,
825-
"game_id": req.GameId,
825+
"gameId": req.GameId,
826826
"env": req.Env,
827827
}
828828
// Use empty addr here; the TCP onConnect path sets the real address.

0 commit comments

Comments
 (0)