From be974b2e1896e2e7a5b3d1673748efbd8c08bb8d Mon Sep 17 00:00:00 2001 From: xfy Date: Fri, 24 Apr 2026 10:06:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(proxy,config):=20=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E5=B1=82=E9=9B=86=E6=88=90=20stale=20=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E5=9B=9E=E9=80=80=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上游请求失败时,根据错误类型(超时/其他)调用 GetStale 尝试返回 过期缓存。配置文件示例补充 stale_if_error 和 stale_if_timeout 字段。 Co-Authored-By: Claude Opus 4.7 --- internal/config/defaults.go | 2 ++ internal/proxy/proxy.go | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/internal/config/defaults.go b/internal/config/defaults.go index 2899a94..fc25a5e 100644 --- a/internal/config/defaults.go +++ b/internal/config/defaults.go @@ -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") diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go index b172f09..06eefc8 100644 --- a/internal/proxy/proxy.go +++ b/internal/proxy/proxy.go @@ -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)