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
	})
}
相关推荐
zhuyasen3 小时前
Go语言配置解析:基于viper的conf库优雅解析配置文件
后端·go
wh201412128 小时前
关于webpack的文件打包分割,并防止js文件缓存
javascript·缓存·webpack
木昜先生8 小时前
用Go Fiber快速开发Web应用
go
Warson_L8 小时前
zsh: command not found: goctl
后端·go
恋红尘10 小时前
Rabbitmq--延迟消息
java·分布式·后端·缓存·rabbitmq
Serverless社区12 小时前
一键部署QwQ-32B推理模型,2种方式简单、快速体验
go
大鹏dapeng12 小时前
Gone V2 Provider 机制介绍
后端·go·github
快乐非自愿15 小时前
什么是nginx的强缓存和协商缓存
nginx·spring·缓存
一个热爱生活的普通人16 小时前
如何用golang实现一个MCP Server
llm·go·mcp