feat(proxy,config): 代理层集成 stale 缓存回退逻辑

上游请求失败时,根据错误类型(超时/其他)调用 GetStale 尝试返回
过期缓存。配置文件示例补充 stale_if_error 和 stale_if_timeout 字段。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
xfy 2026-04-24 10:06:27 +08:00
parent 8deda73b24
commit be974b2e18
2 changed files with 16 additions and 1 deletions

View File

@ -409,6 +409,8 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) {
buf.WriteString(" # cache_lock: true # 防止缓存击穿\n")
buf.WriteString(" # cache_lock_timeout: 5s # 缓存锁超时时间(默认 5s\n")
buf.WriteString(" # stale_while_revalidate: 30s\n")
buf.WriteString(" # stale_if_error: 1m # 上游错误时使用过期缓存的时间窗口\n")
buf.WriteString(" # stale_if_timeout: 30s # 上游超时时使用过期缓存的时间窗口\n")
buf.WriteString(" # background_update_disable: false # 禁用后台更新(默认启用)\n")
buf.WriteString(" # cache_ignore_headers: [] # 缓存时忽略的响应头\n")
buf.WriteString(" # revalidate: false # 启用条件请求(默认关闭)\n")

View File

@ -181,7 +181,7 @@ func NewProxy(cfg *config.ProxyConfig, targets []*loadbalance.Target, transportC
MaxAge: cfg.Cache.MaxAge,
})
}
p.cache = cache.NewProxyCache(rules, cfg.Cache.CacheLock, cfg.Cache.StaleWhileRevalidate)
p.cache = cache.NewProxyCache(rules, cfg.Cache.CacheLock, cfg.Cache.StaleWhileRevalidate, cfg.Cache.StaleIfError, cfg.Cache.StaleIfTimeout)
}
// 初始化重定向改写器
@ -683,6 +683,19 @@ func (p *Proxy) ServeHTTP(ctx *fasthttp.RequestCtx) {
p.healthChecker.MarkUnhealthy(target)
}
// 尝试使用 stale 缓存
if p.cache != nil {
hashKey, origKey := p.buildCacheKeyHash(ctx)
isTimeout := errors.Is(err, fasthttp.ErrTimeout)
if staleEntry, ok := p.cache.GetStale(hashKey, origKey, isTimeout); ok {
logging.Info().Msgf("[PROXY] 使用 stale 缓存: key=%s, isTimeout=%v", origKey, isTimeout)
p.writeCachedResponse(ctx, staleEntry)
upstreamStatus = staleEntry.Status
upstreamAddr = upstreamCache
return
}
}
// 释放缓存锁
if p.cache != nil && attempt == 0 {
hashKey := p.buildCacheKeyHashValue(ctx)