videoplayer/worker/memorycache.go

187 lines
4.0 KiB
Go

package worker
import (
"encoding/json"
"log"
"math"
"os"
"sync"
"time"
)
// MemoryCacheValue 缓存值结构,包含值和过期时间
type MemoryCacheValue struct {
Value string `json:"value"`
ExpireAt int64 `json:"expireAt"` // 过期时间戳,秒级
}
var (
memoryCacheData = NewMemoryCache()
)
// MemoryCache 线程安全的内存缓存
type MemoryCache struct {
data map[string]MemoryCacheValue
mu sync.RWMutex
}
// NewMemoryCache 创建新的内存缓存实例
func NewMemoryCache() *MemoryCache {
return &MemoryCache{
data: make(map[string]MemoryCacheValue),
}
}
// InitStaticMemoryCache 初始化全局缓存实例
func InitStaticMemoryCache() {
// 先尝试从文件加载
ReadMemoryCacheFromJsonFile()
}
// SetWithExp 设置带过期时间的键值对
func (mc *MemoryCache) SetWithExp(key string, value string, expireAt int64) {
mc.mu.Lock()
defer mc.mu.Unlock()
mc.data[key] = MemoryCacheValue{value, expireAt}
}
// Get 获取键值,如果已过期则返回空并删除
func (mc *MemoryCache) Get(key string) string {
// 先加读锁检查
mc.mu.RLock()
value, ok := mc.data[key]
mc.mu.RUnlock()
if !ok {
return ""
}
// 检查是否过期
now := time.Now().Unix()
if value.ExpireAt > now {
return value.Value
}
// 已过期,删除该键
mc.mu.Lock()
// 二次检查,防止并发情况下已被删除
if v, exists := mc.data[key]; exists && v.ExpireAt <= now {
delete(mc.data, key)
}
mc.mu.Unlock()
return ""
}
// Set 设置永不过期的键值对
func (mc *MemoryCache) Set(key string, value string) {
mc.SetWithExp(key, value, math.MaxInt64)
}
// Del 删除指定键
func (mc *MemoryCache) Del(key string) {
mc.mu.Lock()
defer mc.mu.Unlock()
delete(mc.data, key)
}
// Clear 清空所有缓存
func (mc *MemoryCache) Clear() {
mc.mu.Lock()
defer mc.mu.Unlock()
mc.data = make(map[string]MemoryCacheValue)
}
const CRON_MAX_DEL_NUMBER = 100 // 每次清理的最大数量
// DeleteMemoryCacheCron 定时清理过期键
func DeleteMemoryCacheCron() {
memoryCacheData.mu.Lock()
defer memoryCacheData.mu.Unlock()
now := time.Now().Unix()
i := 0
for key, value := range memoryCacheData.data {
if i >= CRON_MAX_DEL_NUMBER {
break
}
if value.ExpireAt < now {
delete(memoryCacheData.data, key)
i++
}
}
}
// GetMemoryCacheFilePath 获取缓存持久化文件路径
func GetMemoryCacheFilePath() string {
if os.Getenv("OS") == "Windows_NT" {
return "C:/Users/Administrator/vp_mc.json"
}
return "/etc/vp_mc.json"
}
// WriteMemoryCacheToFile 将缓存写入文件持久化
func WriteMemoryCacheToFile() {
memoryCacheData.mu.RLock()
defer memoryCacheData.mu.RUnlock()
path := GetMemoryCacheFilePath()
data, err := json.MarshalIndent(memoryCacheData.data, "", " ")
if err != nil {
log.Println("mc write file json err:", err)
return
}
// 先写入临时文件,再原子替换,防止文件损坏
tempPath := path + ".tmp"
if err := os.WriteFile(tempPath, data, 0644); err != nil {
log.Println("mc write temp file err:", err)
return
}
if err := os.Rename(tempPath, path); err != nil {
log.Println("mc rename file err:", err)
os.Remove(tempPath) // 清理临时文件
}
}
// ReadMemoryCacheFromJsonFile 从文件加载缓存
func ReadMemoryCacheFromJsonFile() {
path := GetMemoryCacheFilePath()
_, err := os.Stat(path)
if err != nil {
log.Println("mc file not exists:", err)
return
}
file, err := os.Open(path)
if err != nil {
log.Println("mc open file err:", err)
return
}
defer file.Close()
var data map[string]MemoryCacheValue
decoder := json.NewDecoder(file)
if err := decoder.Decode(&data); err != nil {
log.Println("mc decode file err:", err)
return
}
// 过滤已过期的数据
now := time.Now().Unix()
memoryCacheData.mu.Lock()
for k, v := range data {
if v.ExpireAt > now || v.ExpireAt == math.MaxInt64 {
memoryCacheData.data[k] = v
}
}
memoryCacheData.mu.Unlock()
}
// 提供全局缓存的访问方法
func GetGlobalCache() *MemoryCache {
return memoryCacheData
}