feat: add stateful HTTP/3 negotiation with probe and cooldown#3281
feat: add stateful HTTP/3 negotiation with probe and cooldown#3281zbchi wants to merge 8 commits intoapache:developfrom
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #3281 +/- ##
===========================================
+ Coverage 48.96% 49.60% +0.63%
===========================================
Files 467 470 +3
Lines 34006 34694 +688
===========================================
+ Hits 16652 17210 +558
- Misses 16019 16091 +72
- Partials 1335 1393 +58 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
AlexStocks
left a comment
There was a problem hiding this comment.
Code Review: feat: add stateful HTTP/3 negotiation with probe and cooldown
评价
设计思路清晰,HTTP/3 探测和冷却机制实现合理。通过独立 probe 请求验证 HTTP/3 可用性,避免了在业务请求上重试带来的数据完整性问题。
[P1] 规范级问题
1. protocol/triple/dual_transport.go:95-108 - 状态机可读性
shouldUseH3 方法使用 originMode 枚举值直接比较,建议使用更具表达力的方式:
// 建议定义状态常量
const (
stateUnknown originMode = iota
stateCandidate
stateProbing
stateH3Healthy
stateCooldown
)
func (dt *dualTransport) shouldUseH3(host string) bool {
// 使用状态常量提高可读性
switch dt.state.mode {
case stateH3Healthy:
return true
case stateCooldown:
// ...
}
}2. 测试覆盖
protocol/triple/dual_transport_test.go 测试了主要场景,但缺少:
- Probe timeout 后立即重试的场景
- 并发请求时的状态一致性
- HTTP/3 连接池复用测试
3. 缺少连接池管理说明
代码中每次 probe 创建新请求,建议添加对 HTTP/3 连接复用的说明或测试。
[P2] 建议级
1. 配置化超时参数
defaultH3ProbeTimeout, defaultH3BaseCooldown, defaultH3MaxCooldown 作为常量定义,建议改为可配置参数:
type DualTransportConfig struct {
ProbeTimeout time.Duration
BaseCooldown time.Duration
MaxCooldown time.Duration
}总体评价:实现质量良好,解决了 HTTP/3 回退时的数据完整性问题,建议补充并发场景测试。
AlexStocks
left a comment
There was a problem hiding this comment.
PR 3281 代码审查总结
已提交 4 条具体行级评论,以下是其他关键问题:
[P0] 并发安全问题
位置: maybeStartProbe 函数
问题: 在持有锁期间修改状态后,在锁外启动 goroutine。两个并发请求可能重复启动 probe。
建议: 在锁内完成状态检查和标记,确保只有一个 goroutine 启动 probe。
[P1] 整数溢出风险
位置: nextCooldown 函数 line 441-446
问题: 指数退避计算 d *= 2 可能溢出,溢出后与 maxCooldown 比较行为未定义。
建议: 检测溢出:if next < d || next >= maxCooldown { return maxCooldown }
[P1] 测试覆盖不足
位置: dual_transport_test.go
缺少场景:
- 并发多请求同时触发 probe
- probe 超时场景
- cooldown 期间收到新 Alt-Svc
- 多次失败后 cooldown 指数增长验证
[P2] 代码冗余
- shouldMarkH3Failure 中 err == nil 检查冗余(调用方已保证 err != nil)
- markH3Success/markH3Failure 中 host 空检查冗余(调用方已保证非空)
gh 命令调用次数: 6 次
a71288f to
74823c4
Compare
|



Description
Before
#3266
If HTTP/3 failed, the client could then fall back to HTTP/2 with the same
http.Request.That behavior is risky for Triple, because request bodies come from
io.Pipeor other non-replayable streams.It also means a broken HTTP/3 path can keep being retried instead of letting the client stay on the healthy HTTP/2 path.
After
The client now keeps HTTP/2 as the stable path, learns about HTTP/3 from
Alt-Svcon HTTP/2 responses, and validates the HTTP/3 path with an independent probe before switching later requests. Once HTTP/3 is known to be healthy, future requests may use it. If HTTP/3 later fails, the client does not replay the current request on HTTP/2; instead, it marks the HTTP/3 path unhealthy, enters cooldown, and keeps subsequent requests on HTTP/2 until it is time to probe again.This is also closer to the approach used in
dubbo: treat HTTP/3 as an upgrade that must be validated out of band, rather than retrying the current business request across transports.in short, the behavior now is :
Alt-Svcon HTTP/2 responses makes the origin a candidate for HTTP/3.OPTIONS /) is used to validate the path.Checklist
develop