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
	})
}
相关推荐
小刘在重生~11 小时前
Java 常用类|包装类 装箱拆箱、Integer 缓存面试题
java·python·缓存
数字化小张16 小时前
Go语言并发编程——goroutine与channel实战
go
极小狐17 小时前
CI 流水线提速实战:缓存设计与依赖代理的工程实践
缓存·ci/cd·gitlab·devops
莫得感情 o18 小时前
Redis 09 · 高可用:Sentinel 如何发现故障并完成切主
redis·缓存·sentinel
BUG研究员_20 小时前
LangGraph节点缓存-CachePolicy
python·缓存·langraph
tryxr21 小时前
Redis 的背景知识
数据库·redis·缓存
码艺-Alimjan21 小时前
Tauri 2.x + Vue 3 桌面应用开发实战:从踩坑到完美落地
前端·javascript·vue.js·rust·typescript·go
莫得感情 o21 小时前
Redis 08 · 主从复制:全量同步、增量同步与复制延迟
redis·缓存
mldong1 天前
Go 开发者也有自己的轻量工作流引擎了:go get 一行,5 分钟跑通一条审批流
后端·go
刃神太酷啦1 天前
Redis 进阶核心:持久化 (RDB/AOF)、事务与主从复制全解析----《Hello Redis!》(5)
linux·c语言·数据库·c++·redis·缓存·bootstrap