pet_home_server/src/server/db/Redis.go
2026-03-09 19:30:18 +08:00

272 lines
6.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package db
import (
"context"
"server/conf"
"server/pkg/github.com/name5566/leaf/log"
"strings"
"time"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
var RdbWrite *redis.Client
var RdbRead *redis.Client
// helper: 创建单个客户端addr 可以为 host:port 或 host
func connectClient(addr string) (*redis.Client, error) {
if addr == "" {
return nil, nil
}
// 如果没有端口且配置了旧的 RedisPort则尝试补端口
if !strings.Contains(addr, ":") && conf.Server.RedisPort != "" {
addr = addr + ":" + conf.Server.RedisPort
}
rdb := redis.NewClient(&redis.Options{
Addr: addr,
Password: conf.Server.RedisPwd,
DB: conf.Server.RedisDb,
})
if _, err := rdb.Ping(ctx).Result(); err != nil {
return nil, err
}
return rdb, nil
}
// InitRedis: 初始化读写分离客户端(向后兼容旧配置)
func InitRedis() {
// 决定写地址:优先使用 RedisWriteAddr其次使用旧的 RedisAddr:RedisPort
writeAddr := conf.Server.RedisWriteAddr
if writeAddr == "" {
if conf.Server.RedisAddr != "" {
writeAddr = conf.Server.RedisAddr
if conf.Server.RedisPort != "" && !strings.Contains(writeAddr, ":") {
writeAddr = writeAddr + ":" + conf.Server.RedisPort
}
}
}
// 决定读地址:优先使用 RedisReadAddrs逗号分隔若为空则回退到写地址
readAddrs := conf.Server.RedisReadAddrs
if strings.TrimSpace(readAddrs) == "" {
readAddrs = writeAddr
}
// 取第一个可用的只读地址(简单实现)
var readClient *redis.Client
for _, a := range strings.Split(readAddrs, ",") {
a = strings.TrimSpace(a)
if a == "" {
continue
}
c, err := connectClient(a)
if err == nil {
readClient = c
break
}
log.Debug("connect read addr %s failed: %v", a, err)
}
// 如果所有只读都不可用,尝试连接写地址作为回退
writeClient, err := connectClient(writeAddr)
if err != nil {
log.Debug("连接redis写节点出错错误信息%v", err)
// 若读已连上则也作为写回退,否则返回
if readClient != nil {
RdbWrite = readClient
RdbRead = readClient
log.Debug("只有只读节点可用,读写共用该节点")
return
}
return
}
// 如果读未连接成功,读回退到写
if readClient == nil {
readClient = writeClient
}
RdbWrite = writeClient
RdbRead = readClient
log.Debug("成功初始化 redis读写分离写: %v, 读: %v", writeAddr, readAddrs)
}
// 写操作使用 RdbWrite
func RedisSetKey(key string, value string, expiration time.Duration) {
if RdbWrite == nil {
log.Debug("redis write client is nil")
return
}
err := RdbWrite.Set(ctx, key, value, expiration).Err()
if err != nil {
log.Debug("redis set failed, err:%v\n", err)
}
}
// 新增:写入字节数据,避免 string 转换拷贝
func RedisSetKeyBytes(key string, value []byte, expiration time.Duration) {
if RdbWrite == nil {
log.Debug("redis write client is nil")
return
}
err := RdbWrite.Set(ctx, key, value, expiration).Err()
if err != nil {
log.Debug("redis set failed, err:%v\n", err)
}
}
// 获取锁(写)
func RedisLock(key string, value string, expiration time.Duration) bool {
if RdbWrite == nil {
log.Debug("redis write client is nil")
return false
}
ok, err := RdbWrite.SetNX(ctx, key, value, expiration).Result()
if err != nil {
log.Debug("redis lock failed, err:%v\n", err)
return false
}
return ok
}
// 释放锁(写)
func RedisUnlock(key string, value string) bool {
if RdbWrite == nil {
log.Debug("redis write client is nil")
return false
}
script := `
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
`
result, err := RdbWrite.Eval(ctx, script, []string{key}, value).Result()
if err != nil {
log.Debug("redis unlock failed, err:%v\n", err)
return false
}
return result.(int64) == 1
}
// 读操作使用 RdbRead
func RedisGetKey(key string) (string, error) {
if RdbRead == nil {
return "", nil
}
val, err := RdbRead.Get(ctx, key).Result()
if err != nil {
return "", err
}
return val, nil
}
func RedisGetKeyBytes(key string) ([]byte, error) {
if RdbRead == nil {
return nil, nil
}
val, err := RdbRead.Get(ctx, key).Bytes()
if err != nil {
return nil, err
}
return val, nil
}
func RedisDelKey(key string) {
if RdbWrite == nil {
log.Debug("redis write client is nil")
return
}
err := RdbWrite.Del(ctx, key).Err()
if err != nil {
log.Debug("redis del failed, err:%v\n", err)
}
}
func RedisZAdd(key string, member string, score float64) {
if RdbWrite == nil {
log.Debug("redis write client is nil")
return
}
err := RdbWrite.ZAdd(ctx, key, redis.Z{Score: score, Member: member}).Err()
if err != nil {
log.Debug("redis zadd failed, err:%v\n", err)
}
}
func RedisZRangeWithScores(key string, start, stop int64) ([]redis.Z, error) {
if RdbRead == nil {
return nil, nil
}
val, err := RdbRead.ZRangeWithScores(ctx, key, start, stop).Result()
if err != nil {
return nil, err
}
return val, nil
}
func RedisZRevRangeWithScores(key string, start, stop int64) ([]redis.Z, error) {
if RdbRead == nil {
return nil, nil
}
val, err := RdbRead.ZRevRangeWithScores(ctx, key, start, stop).Result()
if err != nil {
return nil, err
}
return val, nil
}
func RedisZGetByIndexWithScore(key string, index int64) (*redis.Z, error) {
if RdbRead == nil {
return nil, nil
}
val, err := RdbRead.ZRangeWithScores(ctx, key, index, index).Result()
if err != nil {
return nil, err
}
if len(val) == 0 {
return nil, nil
}
return &val[0], nil
}
func RedisZRankWithScores(key, member string) (int64, float64, error) {
if RdbRead == nil {
return 0, 0, nil
}
val, err := RdbRead.ZRank(ctx, key, member).Result()
if err != nil {
return 0, 0, err
}
score, err := RdbRead.ZScore(ctx, key, member).Result()
if err != nil {
return 0, 0, err
}
return val, score, nil
}
func RedisZCard(key string) (int64, error) {
if RdbRead == nil {
return 0, nil
}
val, err := RdbRead.ZCard(ctx, key).Result()
if err != nil {
return 0, err
}
return val, nil
}
func RedisDel(key string) {
if RdbWrite == nil {
log.Debug("redis write client is nil")
return
}
err := RdbWrite.Del(ctx, key).Err()
if err != nil {
log.Debug("redis del failed, err:%v\n", err)
}
}