签入版本
This commit is contained in:
Vendored
+118
@@ -0,0 +1,118 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
)
|
||||
|
||||
// 统一缓存门面:Redis 优先(g.Redis,配置读取 manifest/config 的 redis 节),
|
||||
// Redis 不可用时自动降级为进程内存(gcache),保证单机开发可用。
|
||||
var memFallback atomic.Bool
|
||||
|
||||
// Init 探测 Redis 连通性,失败则切换内存模式
|
||||
func Init(ctx context.Context) {
|
||||
if _, err := g.Redis().Do(ctx, "PING"); err != nil {
|
||||
memFallback.Store(true)
|
||||
g.Log().Warningf(ctx, "Redis 不可用(%v),缓存降级为进程内存模式", err)
|
||||
return
|
||||
}
|
||||
g.Log().Info(ctx, "Redis 缓存已连接")
|
||||
}
|
||||
|
||||
// UsingMemory 是否处于内存降级模式
|
||||
func UsingMemory() bool { return memFallback.Load() }
|
||||
|
||||
// Set 写入缓存(ttlSeconds<=0 表示不过期)
|
||||
func Set(ctx context.Context, key, value string, ttlSeconds int) error {
|
||||
if !memFallback.Load() {
|
||||
var err error
|
||||
if ttlSeconds > 0 {
|
||||
_, err = g.Redis().Do(ctx, "SETEX", key, ttlSeconds, value)
|
||||
} else {
|
||||
_, err = g.Redis().Do(ctx, "SET", key, value)
|
||||
}
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
memFallback.Store(true)
|
||||
g.Log().Warningf(ctx, "Redis 写入失败(%v),缓存降级为内存模式", err)
|
||||
}
|
||||
if ttlSeconds > 0 {
|
||||
return gcache.Set(ctx, key, value, time.Duration(ttlSeconds)*time.Second)
|
||||
}
|
||||
return gcache.Set(ctx, key, value, -1)
|
||||
}
|
||||
|
||||
// Get 读取缓存;第二个返回值表示是否存在
|
||||
func Get(ctx context.Context, key string) (string, bool) {
|
||||
if !memFallback.Load() {
|
||||
v, err := g.Redis().Do(ctx, "GET", key)
|
||||
if err == nil {
|
||||
if v.IsNil() {
|
||||
return "", false
|
||||
}
|
||||
return v.String(), true
|
||||
}
|
||||
memFallback.Store(true)
|
||||
g.Log().Warningf(ctx, "Redis 读取失败(%v),缓存降级为内存模式", err)
|
||||
}
|
||||
v, err := gcache.Get(ctx, key)
|
||||
if err != nil || v.IsNil() {
|
||||
return "", false
|
||||
}
|
||||
return v.String(), true
|
||||
}
|
||||
|
||||
// Del 删除缓存
|
||||
func Del(ctx context.Context, keys ...string) error {
|
||||
if len(keys) == 0 {
|
||||
return nil
|
||||
}
|
||||
if !memFallback.Load() {
|
||||
args := make([]any, 0, len(keys))
|
||||
for _, k := range keys {
|
||||
args = append(args, k)
|
||||
}
|
||||
if _, err := g.Redis().Do(ctx, "DEL", args...); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for _, k := range keys {
|
||||
_, _ = gcache.Remove(ctx, k)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exists 判断键是否存在
|
||||
func Exists(ctx context.Context, key string) bool {
|
||||
if !memFallback.Load() {
|
||||
v, err := g.Redis().Do(ctx, "EXISTS", key)
|
||||
if err == nil {
|
||||
return v.Int() > 0
|
||||
}
|
||||
}
|
||||
ok, err := gcache.Contains(ctx, key)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// SetNX 不存在时写入(防重复提交),成功返回 true
|
||||
func SetNX(ctx context.Context, key, value string, ttlSeconds int) bool {
|
||||
if !memFallback.Load() {
|
||||
v, err := g.Redis().Do(ctx, "SET", key, value, "NX", "EX", ttlSeconds)
|
||||
if err == nil {
|
||||
return !v.IsNil() && v.String() == "OK"
|
||||
}
|
||||
}
|
||||
if Exists(ctx, key) {
|
||||
return false
|
||||
}
|
||||
_ = Set(ctx, key, value, ttlSeconds)
|
||||
return true
|
||||
}
|
||||
Vendored
+69
@@ -0,0 +1,69 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// 有序集合(ZSET)门面:在线用户等需要按分数排序与区间查询的场景。
|
||||
// Redis 模式走 ZADD/ZREM/ZRANGEBYSCORE;
|
||||
// 内存降级用进程内 map,读取时由调用方自行排序(单机语义)。
|
||||
|
||||
var (
|
||||
memZMu sync.RWMutex
|
||||
memZSet = map[string]map[string]float64{} // key → member → score
|
||||
)
|
||||
|
||||
// ZAdd 添加有序集合成员
|
||||
func ZAdd(ctx context.Context, key, member string, score float64) error {
|
||||
if !memFallback.Load() {
|
||||
_, err := g.Redis().Do(ctx, "ZADD", key, score, member)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
memFallback.Store(true)
|
||||
g.Log().Warningf(ctx, "Redis 写入失败(%v),缓存降级为内存模式", err)
|
||||
}
|
||||
memZMu.Lock()
|
||||
defer memZMu.Unlock()
|
||||
if memZSet[key] == nil {
|
||||
memZSet[key] = map[string]float64{}
|
||||
}
|
||||
memZSet[key][member] = score
|
||||
return nil
|
||||
}
|
||||
|
||||
// ZRem 移除有序集合成员
|
||||
func ZRem(ctx context.Context, key, member string) error {
|
||||
if !memFallback.Load() {
|
||||
if _, err := g.Redis().Do(ctx, "ZREM", key, member); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
memZMu.Lock()
|
||||
defer memZMu.Unlock()
|
||||
delete(memZSet[key], member)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ZRangeByScore 按分数区间返回成员(闭区间;max 传 math.Inf(1) 表示无上限)
|
||||
func ZRangeByScore(ctx context.Context, key string, min, max float64) ([]string, error) {
|
||||
if !memFallback.Load() {
|
||||
v, err := g.Redis().Do(ctx, "ZRANGEBYSCORE", key, min, max)
|
||||
if err == nil {
|
||||
return v.Strings(), nil
|
||||
}
|
||||
}
|
||||
memZMu.RLock()
|
||||
defer memZMu.RUnlock()
|
||||
m := memZSet[key]
|
||||
out := make([]string, 0, len(m))
|
||||
for member, score := range m {
|
||||
if score >= min && score <= max {
|
||||
out = append(out, member)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
Reference in New Issue
Block a user