go-文件缓存与锁

语言:golang

在抓取网站前,可能会多次读取文件中的cookie。频繁读取文件,性能不佳,故在go包中创建了一个map全局变量存储文件内容,只有键不存在时才去读取文件。

因为对go并不熟悉,才发现,不同请求读取到的是同一个变量(地址)。对于性能来说,这其实是好的。但出现一个问题:文件更新后,该变量的值依然是文件更新之前的内容,需要重新运行go包,而cookie随时可能失效而去更新,频繁重启这显然不现实。

解决方法

依旧采用map存储文件内容,但map的值为:1、文件的内容,2、文件的修改时间。

每次读取加读锁,并检查实际文件的修改时间,若与map中的值一致,则直接返回map中文件内容。

若不一致,则加写锁,再次检查修改时间,防止获取到写锁之前,文件已经修改。随后读取文件,获取文件中的内容,并更新内容和当前时间到map中。

go 复制代码
type cacheEntry struct {
	content []string
	modTime time.Time
}

var (
	fileCache = make(map[string]cacheEntry)
	mu        sync.RWMutex
	group     singleflight.Group
)

func getFileModTime(path string) (time.Time, error) {
	fileInfo, err := os.Stat(path)
	if err != nil {
		return time.Time{}, err
	}

	return fileInfo.ModTime(), nil
}
func GetFile(file string) string {
	//合并请求
	result, err, _ := group.Do(file, func() (interface{}, error) {
		mu.RLock()
		entry, ok := fileCache[file]
		mu.RUnlock()
		path := GetFullPath(file)
		if ok {
			currentModTime, err := getFileModTime(path)
			if err != nil {
				return nil, err
			}
			if currentModTime.Equal(entry.modTime) {
				return entry.content, nil // 缓存有效
			}
		}
	
		mu.Lock()
		defer mu.Unlock()
	
		//再次检查,避免在获取到写锁的前一刻已经更新
		entry, ok = fileCache[file]
		if ok {
			currentModTime, err := getFileModTime(path)
			if err != nil {
				return nil, err
			}
			if currentModTime.Equal(entry.modTime) {
				return entry.content, nil // 缓存有效
			}
		}
	
		//读取文件并缓存
		content, err := os.ReadFile(path)
		if err != nil || len(content) == 0 {
			return nil, errors.New("文件不存在或为空")
		}
		//可选,按行分割内容
		lines := strings.Split(string(content), "\n")
		var branchContent []string
		for _, line := range lines {
			if line != "" {
				line = strings.TrimSpace(line)
				branchContent = append(branchContent, line)
			}
		}
	
		fileCache[file] = cacheEntry{
			content: branchContent,
			modTime: time.Now(),
		}
	
		return branchContent, nil
	})
}
相关推荐
谢白羽1 分钟前
从传统运维到 AI Infra: K8s 上调度 100 张 GPU踩坑笔记
运维·人工智能·缓存·kubernetes·vllm·sglang·vllm-omni
萧瑟余晖1 小时前
Spring缓存抽象详解
java·spring·缓存
李纲明1 小时前
WordPress 前台改完还是旧页:浏览器、CDN、页面缓存、对象缓存怎么分层清
缓存·cdn·wordpress·devdiy
vipxieliang3 小时前
Go 1.25 Green Tea GC 深度解析:以页为单位的垃圾回收新范式
go
乐观的Terry3 小时前
AI翻译助手-JWT与redis无状态认证
数据库·redis·缓存
孔令飞3 小时前
AI 时代岗位调研分析:调研了近3000份AI岗位描述,分享下调研结果
云原生·go·ai编程
我不会起名字3224 小时前
Redis 从 0 到 1(十二):先删缓存还是先更新库?4 种方案的 Go 并发实测
redis·go·并发·延迟双删·缓存一致性
牧瀬クリスだ4 小时前
Redis String深度解析:从底层到五大实战场景
数据库·redis·缓存
鬼手点金4 小时前
缓存作为架构约束
开发语言·前端·人工智能·python·microsoft·缓存·架构
云上工程笔记5 小时前
2026年大模型Token成本测算推荐清单:输入输出、缓存、并发与峰值用量
人工智能·spring boot·缓存·token