// Package handler 提供 HTTP 请求处理器,包括路由、静态文件服务和零拷贝传输。 // // 该文件实现 FileInfo 缓存,用于减少 os.Stat 调用。 // 替代原 fd 池设计,避免 fd 所有权问题。 // // 设计说明: // - 使用 TTL-only 新鲜度策略:缓存命中时不验证 ModTime // - 理由:每次验证 ModTime 仍需 os.Stat 调用,违背缓存目的 // - 风险:TTL 内文件修改可能返回旧 FileInfo,但静态文件通常不频繁修改 // // 作者:xfy package handler import ( "container/list" "os" "sync" "time" ) const ( fileInfoCacheMaxEntries = 2000 fileInfoCacheTTL = 10 * time.Second ) // fileInfoEntry FileInfo 缓存条目 type fileInfoEntry struct { path string info os.FileInfo cachedAt time.Time element *list.Element } // FileInfoCache FileInfo 缓存(O(1) LRU) type FileInfoCache struct { entries map[string]*fileInfoEntry lruList *list.List mu sync.Mutex } // NewFileInfoCache 创建 FileInfo 缓存 func NewFileInfoCache() *FileInfoCache { return &FileInfoCache{ entries: make(map[string]*fileInfoEntry, fileInfoCacheMaxEntries), lruList: list.New(), } } // Get 获取缓存的 FileInfo func (c *FileInfoCache) Get(filePath string) (os.FileInfo, bool) { c.mu.Lock() defer c.mu.Unlock() entry, ok := c.entries[filePath] if !ok { return nil, false } // 检查 TTL if time.Since(entry.cachedAt) > fileInfoCacheTTL { // 过期,删除并返回未命中 c.lruList.Remove(entry.element) delete(c.entries, filePath) return nil, false } // 命中,移动到 LRU 头部 c.lruList.MoveToFront(entry.element) return entry.info, true } // Set 缓存 FileInfo func (c *FileInfoCache) Set(filePath string, info os.FileInfo) { c.mu.Lock() defer c.mu.Unlock() // 已存在,更新 if entry, ok := c.entries[filePath]; ok { entry.info = info entry.cachedAt = time.Now() c.lruList.MoveToFront(entry.element) return } // 淘汰最久未用的 if c.lruList.Len() >= fileInfoCacheMaxEntries { if oldest := c.lruList.Back(); oldest != nil { if entry, ok := oldest.Value.(*fileInfoEntry); ok { delete(c.entries, entry.path) } c.lruList.Remove(oldest) } } // 插入新条目 entry := &fileInfoEntry{ path: filePath, info: info, cachedAt: time.Now(), } entry.element = c.lruList.PushFront(entry) c.entries[filePath] = entry } // Delete 删除缓存条目 func (c *FileInfoCache) Delete(filePath string) { c.mu.Lock() defer c.mu.Unlock() if entry, ok := c.entries[filePath]; ok { c.lruList.Remove(entry.element) delete(c.entries, filePath) } } // Clear 清空缓存 func (c *FileInfoCache) Clear() { c.mu.Lock() defer c.mu.Unlock() c.entries = make(map[string]*fileInfoEntry, fileInfoCacheMaxEntries) c.lruList = list.New() } // Stats 返回缓存统计 func (c *FileInfoCache) Stats() FileInfoCacheStats { c.mu.Lock() defer c.mu.Unlock() return FileInfoCacheStats{ Entries: len(c.entries), } } // FileInfoCacheStats FileInfo 缓存统计 type FileInfoCacheStats struct { Entries int }