已采纳
回答正文
回复
讨论正文
该问题的正文、回答与讨论。
背景
我们维护一个 Go 1.21 gRPC(v1.59.0)定价服务,被 5 个独立上游服务共享:搜索、购物车、结算、营销、风控。每个上游都是独立的团队,各自用 Resilience4j(Java)或自研 Go 中间件配置重试策略,下游对上游的重试策略完全无感知:
| 上游 | 超时 | 重试次数 | 退避策略 | |------|------|----------|----------| | 搜索 | 300ms | 2 | 固定 200ms | | 购物车 | 500ms | 3 | 指数 100ms 起步 | | 结算 | 400ms | 2 | 固定 150ms | | 营销 | 800ms | 1 | — | | 风控 | 200ms | 2 | 固定 100ms |
下游 10 副本(K8s),每副本 worker 池 200,稳态总入流约 1400 QPS,P99 约 80ms。
现象
上周一次 4.2s 的 STW GC(JVM 侧是上游结算服务触发的,但影响的是下游定价服务的 worker 池排队):
核心卡点:下游无法区分哪些是首请、哪些是重试。即使全局限流,也是无差别拒绝,误伤首请的同时,真正的重试流量(往往是"垃圾负载")还在继续涌入。
已尝试的排查与结果
x-retry-count header:让 5 个上游各自在重试时加 header。结果:a) 依赖各团队自觉,落地 3 个月后 1 个上游改了 SDK 版本丢了 header;b) 中间件/代理层(如 Envoy sidecar)如果也做了重试,这个 header 就不准确了。不可靠。期望得到的答案形态
retry-count / attempt 约定?Retry-After / Idempotency-Key 能否被利用?trace_id 去重的方案(同一 trace_id 在同一时间窗口内出现 2 次 → 判定为重试)?traceparent 或 gRPC metadata 的"请求首次到达"标记方案?补充说明
社区里已有几篇相关讨论我读过:
但这些讨论都聚焦在单上游视角或上游侧的重试策略优化。我的问题是下游视角:当下游是多个上游的共享依赖、且上游重试策略互相独立时,下游怎么保护自己的准入控制不变成"无差别拒绝"?
希望有在生产环境落地过"重试感知准入"或"跨上游重试协调"的同学分享具体实现。