mirror of
https://gitee.com/samwaf/SamWaf.git
synced 2026-10-31 10:11:42 +08:00
215 lines
5.6 KiB
Go
215 lines
5.6 KiB
Go
package cache
|
||
|
||
import (
|
||
"SamWaf/common/zlog"
|
||
"encoding/json"
|
||
"errors"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
type WafCache struct {
|
||
cache map[string]WafCacheItem
|
||
mu sync.Mutex
|
||
}
|
||
type WafCacheItem struct {
|
||
value interface{}
|
||
createTime time.Time
|
||
expireTime time.Time
|
||
ttl time.Duration
|
||
}
|
||
|
||
func InitWafCache() *WafCache {
|
||
wafcache := &WafCache{
|
||
cache: make(map[string]WafCacheItem),
|
||
mu: sync.Mutex{},
|
||
}
|
||
go wafcache.ClearExpirationCacheRoutine()
|
||
return wafcache
|
||
}
|
||
func (wafCache *WafCache) Set(key string, value interface{}) {
|
||
wafCache.SetWithTTl(key, value, 100*365*24*time.Hour)
|
||
}
|
||
|
||
// SetWithTTl 写入并设置存活时长。
|
||
//
|
||
// 注意语义(踩过坑,别再改回去):对**已存在**的 key 会沿用原 createTime,
|
||
// 即到期时刻 = 原 createTime + 新 ttl,而不是 now + ttl。所以它只适合
|
||
// "首次写入" 或 "刷新值但不想改变到期时刻" 的场景;凡是要"续命"的地方
|
||
// 一律用 SetWithTTlRenewTime,否则会把在线会话凭空缩短甚至当场判死
|
||
// (历史上改密续期就是这样让令牌立刻失效的)。
|
||
//
|
||
// 另需注意:Redis 后端(RedisCache.SetWithTTl)没有 createTime 概念,等价于 now + ttl。
|
||
// 两个后端在"已存在 key"上的行为并不一致,写业务代码时不要依赖该差异。
|
||
func (wafCache *WafCache) SetWithTTl(key string, value interface{}, ttl time.Duration) {
|
||
wafCache.mu.Lock()
|
||
defer wafCache.mu.Unlock()
|
||
createTime := time.Now()
|
||
item, found := wafCache.cache[key]
|
||
|
||
if found {
|
||
createTime = item.createTime
|
||
}
|
||
wafCache.cache[key] = WafCacheItem{
|
||
value: value,
|
||
createTime: createTime,
|
||
expireTime: createTime.Add(ttl),
|
||
ttl: ttl,
|
||
}
|
||
}
|
||
|
||
// SetWithTTlRenewTime 并重置时间
|
||
func (wafCache *WafCache) SetWithTTlRenewTime(key string, value interface{}, ttl time.Duration) {
|
||
wafCache.mu.Lock()
|
||
defer wafCache.mu.Unlock()
|
||
createTime := time.Now()
|
||
|
||
wafCache.cache[key] = WafCacheItem{
|
||
value: value,
|
||
createTime: createTime,
|
||
expireTime: createTime.Add(ttl), // 计算过期时间
|
||
ttl: ttl,
|
||
}
|
||
}
|
||
func (wafCache *WafCache) GetAs(key string, out interface{}) error {
|
||
val := wafCache.Get(key)
|
||
if val == nil {
|
||
return errors.New("数据不存在")
|
||
}
|
||
b, err := json.Marshal(val)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return json.Unmarshal(b, out)
|
||
}
|
||
|
||
func (wafCache *WafCache) GetBytes(key string) ([]byte, error) {
|
||
key1Value := wafCache.Get(key)
|
||
if str, ok := key1Value.([]byte); ok {
|
||
return str, nil
|
||
}
|
||
return nil, errors.New("数据不存在")
|
||
}
|
||
func (wafCache *WafCache) GetString(key string) (string, error) {
|
||
key1Value := wafCache.Get(key)
|
||
if str, ok := key1Value.(string); ok {
|
||
return str, nil
|
||
}
|
||
return "", errors.New("数据不存在")
|
||
}
|
||
func (wafCache *WafCache) GetInt(key string) (int, error) {
|
||
key1Value := wafCache.Get(key)
|
||
if str, ok := key1Value.(int); ok {
|
||
return str, nil
|
||
}
|
||
return -1, errors.New("数据不存在")
|
||
}
|
||
func (wafCache *WafCache) IsKeyExist(key string) bool {
|
||
wafCache.mu.Lock()
|
||
defer wafCache.mu.Unlock()
|
||
item, found := wafCache.cache[key]
|
||
if !found {
|
||
return false
|
||
}
|
||
if time.Since(item.createTime) <= item.ttl {
|
||
return true
|
||
}
|
||
delete(wafCache.cache, key)
|
||
return false
|
||
}
|
||
func (wafCache *WafCache) Get(key string) interface{} {
|
||
wafCache.mu.Lock()
|
||
defer wafCache.mu.Unlock()
|
||
item, found := wafCache.cache[key]
|
||
if !found {
|
||
return nil
|
||
}
|
||
if time.Since(item.createTime) <= item.ttl {
|
||
return item.value
|
||
}
|
||
delete(wafCache.cache, key)
|
||
return nil
|
||
}
|
||
|
||
func (wafCache *WafCache) Remove(key string) interface{} {
|
||
wafCache.mu.Lock()
|
||
defer wafCache.mu.Unlock()
|
||
_, found := wafCache.cache[key]
|
||
if !found {
|
||
return nil
|
||
}
|
||
delete(wafCache.cache, key)
|
||
return nil
|
||
}
|
||
func (wafCache *WafCache) GetExpireTime(key string) (time.Time, error) {
|
||
wafCache.mu.Lock()
|
||
defer wafCache.mu.Unlock()
|
||
item, found := wafCache.cache[key]
|
||
if !found {
|
||
return time.Time{}, errors.New("数据不存在")
|
||
}
|
||
if time.Since(item.createTime) <= item.ttl {
|
||
return item.expireTime, nil
|
||
}
|
||
zlog.Debug("GetExpireTime CLEAR CACHE EXPIRE :" + key)
|
||
delete(wafCache.cache, key)
|
||
return time.Time{}, errors.New("数据已过期")
|
||
}
|
||
func (wafCache *WafCache) ClearExpirationCache() {
|
||
wafCache.mu.Lock()
|
||
defer wafCache.mu.Unlock()
|
||
now := time.Now()
|
||
for key, item := range wafCache.cache {
|
||
if now.Sub(item.createTime) > item.ttl {
|
||
zlog.Debug("ClearExpirationCache CLEAR CACHE EXPIRE :" + key)
|
||
delete(wafCache.cache, key)
|
||
}
|
||
}
|
||
}
|
||
func (wafCache *WafCache) ClearExpirationCacheRoutine() {
|
||
ticker := time.NewTicker(60 * time.Second)
|
||
defer ticker.Stop()
|
||
for range ticker.C {
|
||
wafCache.ClearExpirationCache()
|
||
}
|
||
}
|
||
|
||
// ListAvailableKeys 列出所有可用的键和剩余时间
|
||
func (wafCache *WafCache) ListAvailableKeys() map[string]time.Duration {
|
||
wafCache.mu.Lock()
|
||
defer wafCache.mu.Unlock()
|
||
now := time.Now()
|
||
availableKeys := make(map[string]time.Duration)
|
||
|
||
for key, item := range wafCache.cache {
|
||
remainingTime := item.ttl - now.Sub(item.createTime)
|
||
if remainingTime > 0 {
|
||
availableKeys[key] = remainingTime
|
||
} else {
|
||
delete(wafCache.cache, key) // 删除过期项
|
||
}
|
||
}
|
||
return availableKeys
|
||
}
|
||
|
||
// ListAvailableKeysWithPrefix 列出指定前缀的可用键和剩余时间
|
||
func (wafCache *WafCache) ListAvailableKeysWithPrefix(prefix string) map[string]time.Duration {
|
||
wafCache.mu.Lock()
|
||
defer wafCache.mu.Unlock()
|
||
now := time.Now()
|
||
availableKeys := make(map[string]time.Duration)
|
||
|
||
for key, item := range wafCache.cache {
|
||
if strings.HasPrefix(key, prefix) {
|
||
remainingTime := item.ttl - now.Sub(item.createTime)
|
||
if remainingTime > 0 {
|
||
availableKeys[key] = remainingTime
|
||
} else {
|
||
delete(wafCache.cache, key) // 删除过期项
|
||
}
|
||
}
|
||
}
|
||
return availableKeys
|
||
}
|