From 23fdcf89ae5c3678496ee9dacb3b172accb74854 Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 30 Apr 2026 16:03:59 +0800 Subject: [PATCH] perf(handler): use cached ETag to avoid regeneration on cache hits - Use entry.ETag instead of generateETag() in cache hit branches - Add 304 response check before returning cached data - Reduces ETag computation overhead for cached files Co-Authored-By: Claude Opus 4.7 --- internal/handler/static.go | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/internal/handler/static.go b/internal/handler/static.go index aa7d826..df6f704 100644 --- a/internal/handler/static.go +++ b/internal/handler/static.go @@ -646,19 +646,19 @@ func (h *StaticHandler) handleStandard(ctx *fasthttp.RequestCtx, reqPath string) // Phase 2: 缓存查找 + TTL 验证 // 在 serveFile 调用前检查缓存,减少 os.ReadFile 调用 // 注意: CachedAt 迁移已在 FileCache.Get() 内部完成,确保并发安全 - etag := generateETag(info.ModTime(), info.Size()) - if isNotModified(ctx, etag, info.ModTime()) { - ctx.Response.SetStatusCode(fasthttp.StatusNotModified) - ctx.Response.Header.Set("ETag", etag) - ctx.Response.Header.Set("Last-Modified", info.ModTime().UTC().Format(httpTimeFormat)) - ctx.Response.SkipBody = true - return - } if h.fileCache != nil { if entry, ok := h.fileCache.Get(filePath); ok { // TTL 验证(cacheTTL > 0 时启用) if h.cacheTTL > 0 && time.Since(entry.CachedAt) < h.cacheTTL { // TTL 内直接返回(无需验证 ModTime) + // 使用缓存的 ETag,避免重新生成 + if isNotModified(ctx, entry.ETag, info.ModTime()) { + ctx.Response.SetStatusCode(fasthttp.StatusNotModified) + ctx.Response.Header.Set("ETag", entry.ETag) + ctx.Response.Header.Set("Last-Modified", info.ModTime().UTC().Format(httpTimeFormat)) + ctx.Response.SkipBody = true + return + } ctx.Response.SetBody(entry.Data) ctx.Response.Header.SetContentType(mimeutil.DetectContentType(filePath)) ctx.Response.Header.Set("ETag", entry.ETag) @@ -669,6 +669,14 @@ func (h *StaticHandler) handleStandard(ctx *fasthttp.RequestCtx, reqPath string) // TTL 过期或未启用 TTL,验证文件新鲜度 if entry.ModTime.Equal(info.ModTime()) { // 文件未修改,刷新 TTL 并返回 + // 使用缓存的 ETag,避免重新生成 + if isNotModified(ctx, entry.ETag, info.ModTime()) { + ctx.Response.SetStatusCode(fasthttp.StatusNotModified) + ctx.Response.Header.Set("ETag", entry.ETag) + ctx.Response.Header.Set("Last-Modified", info.ModTime().UTC().Format(httpTimeFormat)) + ctx.Response.SkipBody = true + return + } if h.cacheTTL > 0 { h.fileCache.RefreshCachedAt(filePath) } @@ -726,9 +734,10 @@ func (h *StaticHandler) serveFile(ctx *fasthttp.RequestCtx, filePath string, inf // 检查文件是否被修改 if entry.ModTime.Equal(info.ModTime()) { // 缓存命中且文件未修改 + // 使用缓存的 ETag,避免重新生成 ctx.Response.SetBody(entry.Data) ctx.Response.Header.SetContentType(mimeutil.DetectContentType(filePath)) - ctx.Response.Header.Set("ETag", etag) + ctx.Response.Header.Set("ETag", entry.ETag) ctx.Response.Header.Set("Last-Modified", info.ModTime().UTC().Format(httpTimeFormat)) h.setCacheHeaders(ctx) return