From 4f24dd7fd7558748295827b0a75c4af212bf1df1 Mon Sep 17 00:00:00 2001 From: xfy Date: Fri, 24 Apr 2026 13:13:40 +0800 Subject: [PATCH] =?UTF-8?q?perf(compression):=20=E9=A2=84=E5=8E=8B?= =?UTF-8?q?=E7=BC=A9=E6=96=87=E4=BB=B6=E5=AD=98=E5=9C=A8=E6=80=A7=E7=BC=93?= =?UTF-8?q?=E5=AD=98=EF=BC=8C=E9=81=BF=E5=85=8D=E9=87=8D=E5=A4=8D=20os.Sta?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 使用 sync.Map 缓存预压缩文件的存在性检查结果,与 nginx gzip_static 行为一致采用永不过期策略,减少高频请求下的 文件系统调用开销。 Co-Authored-By: Claude Opus 4.7 --- .../middleware/compression/gzip_static.go | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/internal/middleware/compression/gzip_static.go b/internal/middleware/compression/gzip_static.go index fc0d02b..e19697f 100644 --- a/internal/middleware/compression/gzip_static.go +++ b/internal/middleware/compression/gzip_static.go @@ -28,11 +28,22 @@ import ( "os" "path/filepath" "strings" + "sync" "github.com/valyala/fasthttp" "rua.plus/lolly/internal/mimeutil" ) +// existResult 预压缩文件存在性缓存结果。 +// +// 采用永不过期策略,与 nginx gzip_static 行为一致: +// 预压缩文件通常在构建时生成,运行时不会频繁增删。 +// 如需更新缓存,重启服务即可。 +type existResult struct { + // exists 预压缩文件是否存在 + exists bool +} + // GzipStatic 预压缩文件支持中间件。 // // 检查是否存在预压缩的 .gz 或 .br 文件,如果存在且客户端支持对应编码, @@ -46,6 +57,8 @@ type GzipStatic struct { extensions []string // enabled 是否启用预压缩支持 enabled bool + // existCache 预压缩文件存在性缓存,避免重复 os.Stat 调用 + existCache sync.Map } // NewGzipStatic 创建预压缩文件处理器。 @@ -116,8 +129,23 @@ func (g *GzipStatic) ServeFile(ctx *fasthttp.RequestCtx, filePath string) bool { compressedPath := filePath + ext fullPath := filepath.Join(g.root, compressedPath) - // 检查文件是否存在 - if _, err := os.Stat(fullPath); err != nil { + // 检查文件是否存在(优先查缓存) + var exists bool + if cached, ok := g.existCache.Load(fullPath); ok { + if er, ok := cached.(existResult); ok { + exists = er.exists + } else { + // 意外类型,回退到 os.Stat + _, err := os.Stat(fullPath) + exists = err == nil + g.existCache.Store(fullPath, existResult{exists: exists}) + } + } else { + _, err := os.Stat(fullPath) + exists = err == nil + g.existCache.Store(fullPath, existResult{exists: exists}) + } + if !exists { continue }