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
	})
}
相关推荐
ShuiShenHuoLe2 分钟前
OS的常用函数
go
踏着七彩祥云的小丑1 小时前
Go学习第8天:接口 + 泛型 + 错误处理
开发语言·学习·golang·go
小胖xiaopangss2 小时前
Redis 基础入门与实践指南
数据库·redis·缓存
syt_biancheng15 小时前
Redis初识
数据库·redis·缓存
杨运交16 小时前
[032][缓存模块]基于Redis Bitmap的用户行为统计实战:签到与日活分析
数据库·redis·缓存
无关868819 小时前
Redis Bitmaps 用户签到系统设计方案
数据库·redis·缓存
蓝宝石的傻话1 天前
rpi-cam:给 Raspberry Pi 造的轻量级 ONVIF 相机服务
go·iot·nvr
zzz_23681 天前
【Java基础】链表的七十二变——从LRU缓存到手写浏览器前进后退
java·链表·缓存
蓝宝石的傻话1 天前
VictoriaMetrics指标流聚合三年回顾与现状(2026)
go·prometheus·victoriametrics
踏着七彩祥云的小丑1 天前
Go学习第7天:Map集合 + 递归函数 + 类型转换
开发语言·学习·golang·go