// 文件ttl缓存实现,每次都会读取文件,由于没有泛型,此处需要调用者自行解决序列化问题. // 2.存储在文件里面,以便重启之后可以使用. // 3.当缓存存在的时候,并且ttl在时间范围内,使用ttl,当缓存不存在的时候,使用回调拉取数据.当缓存过期的时候,使用回调拉取数据. // 4.每一次使用都会读取文件 // 5.当某一次缓存拉取出现错误的时候,直接返回错误给调用者 func FileTtlCache(key string, f func() (b []byte, ttl time.Duration, err error)) (b []byte, err error) { entry := ttlCacheEntryV2{} cacheFilePath := getFileTtlCachePath(key) now := time.Now() err = kmgGob.ReadFile(cacheFilePath, &entry) if err == nil && entry.Timeout.After(now) { return entry.Value, nil } b, ttl, err := f() if err != nil { return nil, err } entry.Value = b entry.Timeout = now.Add(ttl) err = kmgFile.MkdirForFile(cacheFilePath) if err != nil { return nil, err } err = kmgGob.WriteFile(cacheFilePath, entry) if err != nil { return nil, err } return b, nil }
func (ctx *goRunInstallContext) goRunInstallIsValidAndInvalidCache(goPath string, pathOrPkg string) bool { err := kmgGob.ReadFile(ctx.targetCachePath, &ctx.targetPkgMapCache) if err != nil { //此处故意忽略错误 没有缓存文件 TODO 此处需要折腾其他东西吗? return false } err = kmgGob.ReadFile(ctx.pkgCachePath, &ctx.pkgMapCache) if err != nil { //此处故意忽略错误 没有缓存文件 return false } //kmgDebug.Println(info) isValid := true task := kmgTask.NewLimitThreadTaskManager(runtime.NumCPU()) for pkgName := range ctx.targetPkgMapCache { pkgName := pkgName task.AddFunc(func() { pkgInfo, ok := ctx.pkgMapCache[pkgName] if !ok { // 缓存不一致,删文件? pkgPath := filepath.Join("pkg", ctx.platform, pkgName+".a") if debug { fmt.Println("pkgname not found in pkgMapCache delete ", pkgPath) } kmgFile.MustDelete(pkgPath) isValid = false return } pkgPath := pkgInfo.getPkgBinPath(goPath, ctx.platform) if !checkFileWithMd5(pkgPath, pkgInfo.PkgMd5) { if debug { oldMd5 := uint64(0) if kmgFile.MustFileExist(pkgPath) { oldMd5 = mustCheckSumFile(pkgPath) } fmt.Println("pkgBinFile md5 not equal delete ", pkgPath, pkgInfo.PkgMd5, oldMd5) } kmgFile.MustDelete(pkgPath) isValid = false return } srcpkgPath := filepath.Join(goPath, "src", pkgName) fileList, err := kmgFile.ReadDirFileOneLevel(srcpkgPath) if err != nil { if !os.IsNotExist(err) { panic(err) } kmgFile.MustDelete(pkgPath) if debug { fmt.Println("dir not exist File delete ", pkgPath, srcpkgPath) } isValid = false return } isThisPkgValid := true for _, file := range fileList { ext := filepath.Ext(file) if ext == ".go" { // 多了一个文件 _, ok := pkgInfo.GoFileMap[file] if !ok { kmgFile.MustDelete(pkgPath) if debug { fmt.Println("more file ", file, " delete ", pkgPath) } isValid = false isThisPkgValid = false break } } } if !isThisPkgValid { return } for name, md5 := range pkgInfo.GoFileMap { goFilePath := filepath.Join(srcpkgPath, name) if !checkFileWithMd5(goFilePath, md5) { kmgFile.MustDelete(pkgPath) if debug { fmt.Println("gofile not match ", name, " delete ", pkgPath) } isValid = false break } } }) } task.Close() return isValid }
// key 表示一组缓存,pathList 即这组缓存相关的文件 // 比较容易用错的情况: // key := 1 // pathList := []string{"/a.txt","/b.txt"} // a.txt 或者 b.txt 中任意有文件发生变化,这个 key 对应的缓存都会被更新 // 如果要对单个文件进行缓存控制,那么应该一个文件一个 key func MustMd5FileChangeCache(key string, pathList []string, f func()) { // 此处需要考虑, // 用户新添加了一个文件 // 用户删除了一个文件 // 用户编辑了一个文件 // 用户在目录里面添加了一个文件 // 用在在目录里面删除了一个文件 //读取文件修改时间缓存信息 toChange := false cacheInfo := map[string]string{} cacheFilePath := getFileChangeCachePath(key) err := kmgGob.ReadFile(cacheFilePath, &cacheInfo) if err != nil { //忽略缓存读取的任何错误 cacheInfo = map[string]string{} } hasReadFileMap := map[string]bool{} for _, path := range pathList { statList, err := kmgFile.GetAllFileAndDirectoryStat(path) if err != nil { if os.IsNotExist(err) { toChange = true //fmt.Printf("[MustFileChangeCache] path:[%s] not exist\n", path) break } panic(err) } for _, stat := range statList { if stat.Fi.IsDir() { continue } hasReadFileMap[stat.FullPath] = true if stat.Fi.Mode()&os.ModeSymlink == os.ModeSymlink { if "symlink_"+kmgFile.MustReadSymbolLink(stat.FullPath) != cacheInfo[stat.FullPath] { toChange = true break } continue } if kmgCrypto.MustMd5File(stat.FullPath) != cacheInfo[stat.FullPath] { toChange = true //fmt.Printf("[MustMd5FileChangeCache] path:[%s] mod md5 not match save[%s] file[%s]\n", stat.FullPath, // cacheMd5, kmgCrypto.MustMd5File(stat.FullPath)) break } } if toChange { break } } //删除一个已经存储在缓存列表里面的文件,是一个修改. for fullPath := range cacheInfo { if !hasReadFileMap[fullPath] { toChange = true break } } if !toChange { return } f() cacheInfo = map[string]string{} for _, path := range pathList { statList, err := kmgFile.GetAllFileAndDirectoryStat(path) if err != nil { panic(err) } for _, stat := range statList { if stat.Fi.IsDir() { continue } if stat.Fi.Mode()&os.ModeSymlink == os.ModeSymlink { linkToPath := kmgFile.MustReadSymbolLink(stat.FullPath) cacheInfo[stat.FullPath] = "symlink_" + linkToPath continue } cacheInfo[stat.FullPath] = kmgCrypto.MustMd5File(stat.FullPath) } } kmgFile.MustMkdirForFile(cacheFilePath) kmgGob.MustWriteFile(cacheFilePath, cacheInfo) //保存文件缓存信息 return }
// 这个函数bug太多,以废弃,请使用 MustMd5FileChangeCache // 根据文件变化,对f这个请求进行缓存 // key表示这件事情的缓存key // pathList表示需要监控的目录 // 文件列表里面如果有文件不存在,会运行代码 // 已知bug,小于1秒的修改不能被检测到. // @deprecated func MustFileChangeCache(key string, pathList []string, f func()) { //读取文件修改时间缓存信息 toChange := false cacheInfo := map[string]time.Time{} cacheFilePath := getFileChangeCachePath(key) err := kmgGob.ReadFile(cacheFilePath, &cacheInfo) if err != nil { cacheInfo = map[string]time.Time{} } for _, path := range pathList { statList, err := kmgFile.GetAllFileAndDirectoryStat(path) if err != nil { if os.IsNotExist(err) { toChange = true //fmt.Printf("[MustFileChangeCache] path:[%s] not exist\n", path) break } panic(err) } for _, stat := range statList { if stat.Fi.IsDir() { continue } cacheTime := cacheInfo[stat.FullPath] if cacheTime.IsZero() { toChange = true //fmt.Printf("[MustFileChangeCache] path:[%s] no save mod time\n", stat.FullPath) break } if stat.Fi.ModTime() != cacheTime { toChange = true //fmt.Printf("[MustFileChangeCache] path:[%s] mod time not match save[%s] file[%s]\n", stat.FullPath, // cacheTime, stat.Fi.ModTime()) break } } if toChange { break } } if !toChange { return } f() cacheInfo = map[string]time.Time{} for _, path := range pathList { statList, err := kmgFile.GetAllFileAndDirectoryStat(path) if err != nil { panic(err) } for _, stat := range statList { if stat.Fi.IsDir() { continue } cacheInfo[stat.FullPath] = stat.Fi.ModTime() } } kmgFile.MustMkdirForFile(cacheFilePath) kmgGob.MustWriteFile(cacheFilePath, cacheInfo) //保存文件缓存信息 return }