1027 lines
26 KiB
Go
1027 lines
26 KiB
Go
package redis
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"net/url"
|
||
"reflect"
|
||
"strconv"
|
||
"time"
|
||
|
||
"91porn-server/common/log"
|
||
|
||
goredis "github.com/go-redis/redis"
|
||
)
|
||
|
||
//命令参考问题 http://redisdoc.com/persistence/index.html
|
||
|
||
// Handler 缓存操作句柄
|
||
var Handler *Client
|
||
|
||
// Options 配置项
|
||
type Options struct {
|
||
Addr string `json:"addr"` // 地址
|
||
Pwd string `json:"pwd"` // 密码
|
||
MaxRetries int `json:"maxRetries"` // 重试次数
|
||
DB int `json:"db"` //数据库
|
||
PoolSize int `json:"poolSize"` //链接池数量
|
||
MinIdleConns int `json:"minIdleConns"` //最小空闲连接数
|
||
}
|
||
|
||
// Client Redis客户端
|
||
type Client struct {
|
||
client *goredis.Client
|
||
}
|
||
|
||
// Script 是可复用的 Redis Lua 脚本。RunScript 会优先执行 EVALSHA,
|
||
// Redis 尚未缓存脚本时由 go-redis 自动回退到 EVAL。
|
||
type Script struct {
|
||
script *goredis.Script
|
||
}
|
||
|
||
// NewScript 预计算脚本 SHA,调用方应复用返回值而不是每次请求重新创建。
|
||
func NewScript(source string) *Script {
|
||
return &Script{script: goredis.NewScript(source)}
|
||
}
|
||
|
||
// Hash 返回脚本 SHA1,主要用于诊断和测试。
|
||
func (s *Script) Hash() string {
|
||
if s == nil || s.script == nil {
|
||
return ""
|
||
}
|
||
return s.script.Hash()
|
||
}
|
||
|
||
type Member = goredis.Z
|
||
|
||
// Message 消息体
|
||
type Message struct {
|
||
Channel string `json:"channel"`
|
||
Payload string `json:"payload"`
|
||
}
|
||
|
||
// WithOptions 启动redis客户端连接
|
||
func WithOptions(options Options) (*Client, error) {
|
||
client := goredis.NewClient(&goredis.Options{
|
||
Addr: options.Addr,
|
||
Password: options.Pwd,
|
||
MaxRetries: options.MaxRetries,
|
||
DB: options.DB,
|
||
MinIdleConns: options.MinIdleConns,
|
||
PoolSize: options.PoolSize,
|
||
})
|
||
if err := client.Ping().Err(); err != nil {
|
||
log.Error("can't ping redis addr",
|
||
log.Any("addr", options.Addr),
|
||
log.Any("db", options.DB),
|
||
log.E(err))
|
||
return nil, err
|
||
}
|
||
log.Info("redis connections success",
|
||
log.Any("addr", options.Addr),
|
||
log.Any("db", options.DB),
|
||
log.Any("poolSize", options.PoolSize),
|
||
log.Any("minIdleConns", options.MinIdleConns))
|
||
|
||
Handler = &Client{client: client}
|
||
return Handler, nil
|
||
}
|
||
|
||
func WithURL(rawurl string) (*Client, error) {
|
||
u, err := url.Parse(rawurl)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
auth := u.User
|
||
host := u.Host
|
||
path := u.Path
|
||
rdb := 0
|
||
query := u.Query()
|
||
if len(path) > 1 {
|
||
path = path[1:]
|
||
rdb, err = strconv.Atoi(path)
|
||
if err != nil {
|
||
log.Error("redis WithURL wrong db", log.Any("db", path))
|
||
return nil, err
|
||
}
|
||
}
|
||
pwd, _ := auth.Password()
|
||
smr := query.Get("maxRetries")
|
||
maxRetries := 0
|
||
if len(smr) > 0 {
|
||
maxRetries, err = strconv.Atoi(smr)
|
||
if err != nil {
|
||
log.Error("redis WithURL wrong maxRetries", log.Any("maxRetries", smr))
|
||
return nil, err
|
||
}
|
||
}
|
||
poolSize := 70
|
||
poolSizeStr := query.Get("poolSize")
|
||
if poolSizeStr != "" {
|
||
poolSize, err = strconv.Atoi(poolSizeStr)
|
||
if err != nil {
|
||
poolSize = 50
|
||
}
|
||
}
|
||
minIdleConns := 50
|
||
minIdleConnsStr := query.Get("minIdleConns")
|
||
if minIdleConnsStr != "" {
|
||
minIdleConns, err = strconv.Atoi(minIdleConnsStr)
|
||
if err != nil {
|
||
minIdleConns = 30
|
||
}
|
||
}
|
||
opt := Options{
|
||
Addr: host,
|
||
Pwd: pwd,
|
||
MaxRetries: maxRetries,
|
||
DB: rdb,
|
||
PoolSize: poolSize,
|
||
MinIdleConns: minIdleConns,
|
||
}
|
||
return WithOptions(opt)
|
||
}
|
||
|
||
// Close 关闭redis
|
||
func (r *Client) Close() error {
|
||
if r.client == nil {
|
||
return nil
|
||
}
|
||
if err := r.client.Close(); err != nil {
|
||
log.Error("close redis-connection failed")
|
||
return err
|
||
}
|
||
log.Info("close redis-connection successfully")
|
||
return nil
|
||
}
|
||
|
||
// SetBit 位操作
|
||
func (r *Client) SetBit(key string, offset int64, value int) error {
|
||
if key == "" {
|
||
return errors.New("key should not empty")
|
||
}
|
||
return r.client.SetBit(key, offset, value).Err()
|
||
}
|
||
|
||
// GetBit 获取位值
|
||
func (r *Client) GetBit(key string, offset int64) (int, error) {
|
||
if key == "" {
|
||
return 0, errors.New("key should not empty")
|
||
}
|
||
|
||
data, err := r.client.GetBit(key, offset).Result()
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
|
||
return int(data), nil
|
||
}
|
||
|
||
// Set 设置键值对
|
||
func (r *Client) Set(key string, value interface{}, expirTime time.Duration) error {
|
||
if key == "" {
|
||
return errors.New("key should not empty")
|
||
}
|
||
return r.client.Set(key, value, expirTime).Err()
|
||
}
|
||
|
||
// Get 根据键 获取值
|
||
func (r *Client) Get(key string) (*string, error) {
|
||
if key == "" {
|
||
return nil, errors.New("key should not empty")
|
||
}
|
||
data, err := r.client.Get(key).Result()
|
||
if err != nil {
|
||
if err == goredis.Nil {
|
||
return nil, nil
|
||
}
|
||
return nil, err
|
||
}
|
||
return &data, nil
|
||
}
|
||
|
||
func (r *Client) GetString(key string) (string, error) {
|
||
if key == "" {
|
||
return "", errors.New("key should not empty")
|
||
}
|
||
value, err := r.client.Get(key).Result()
|
||
if err != nil && err != goredis.Nil {
|
||
return "", err
|
||
}
|
||
return value, nil
|
||
}
|
||
|
||
// Get 根据键 获取值
|
||
func (r *Client) GetObj(bind interface{}, key string) error {
|
||
if key == "" {
|
||
return errors.New("key should not empty")
|
||
}
|
||
data, err := r.client.Get(key).Bytes()
|
||
if err != nil {
|
||
if err == goredis.Nil {
|
||
return nil
|
||
}
|
||
return err
|
||
}
|
||
return json.Unmarshal(data, &bind)
|
||
}
|
||
|
||
// Get 根据键 获取值
|
||
func (r *Client) Scan(key string, val interface{}) error {
|
||
if key == "" {
|
||
return errors.New("key should not empty")
|
||
}
|
||
return r.client.Get(key).Scan(val)
|
||
}
|
||
|
||
// ScanKeys 异步迭代匹配key,不阻塞存取线程
|
||
func (r *Client) ScanKeys(match string) (keys []string, err error) {
|
||
if match == "" {
|
||
return nil, errors.New("match key should not empty")
|
||
}
|
||
|
||
var cursor uint64
|
||
var tmpKey []string
|
||
tmpKey, cursor, err = r.client.Scan(cursor, match, 100).Result()
|
||
if err != nil {
|
||
return
|
||
}
|
||
keys = append(keys, tmpKey...)
|
||
for cursor > 0 {
|
||
tmpKey, cursor, err = r.client.Scan(cursor, match, 100).Result()
|
||
if err != nil {
|
||
return
|
||
}
|
||
keys = append(keys, tmpKey...)
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// Get 是否存在某一个建
|
||
func (r *Client) IsExist(key string) bool {
|
||
if key == "" {
|
||
return false
|
||
}
|
||
data, err := r.client.Exists(key).Result()
|
||
if err != nil {
|
||
return false
|
||
}
|
||
return data != 0
|
||
}
|
||
|
||
// Del 删除键值
|
||
func (r *Client) Del(keys ...string) (n int64, err error) {
|
||
return r.DelContext(context.Background(), keys...)
|
||
}
|
||
|
||
// DelContext 删除键值并继承调用方取消和超时。
|
||
func (r *Client) DelContext(ctx context.Context, keys ...string) (n int64, err error) {
|
||
if ctx == nil {
|
||
return 0, errors.New("redis DelContext context must not be nil")
|
||
}
|
||
if r == nil || r.client == nil {
|
||
return 0, errors.New("redis DelContext client must not be nil")
|
||
}
|
||
return r.client.WithContext(ctx).Del(keys...).Result()
|
||
}
|
||
|
||
// Subscribe constructs a field
|
||
func (r *Client) Subscribe(channel ...string) (sub *goredis.PubSub, err error) {
|
||
pubsub := r.client.Subscribe(channel...)
|
||
if err := pubsub.Ping("ack:ok"); err != nil {
|
||
log.Error(fmt.Sprintf("pubsub ping failed %+v:", err))
|
||
return nil, err
|
||
}
|
||
if _, err = pubsub.Receive(); err != nil {
|
||
log.Error(fmt.Sprintf("pubsub receive failed %+v:", err))
|
||
return nil, err
|
||
}
|
||
log.Info("pubsub successfully")
|
||
return pubsub, nil
|
||
}
|
||
|
||
// Publish 创建订阅频道 发布消息
|
||
func (r *Client) Publish(channel string, message interface{}) error {
|
||
flag, err := r.client.Publish(channel, message).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("publish failed %+v:", err))
|
||
return err
|
||
}
|
||
log.Info(fmt.Sprintf("publish successfully flag:%d", flag))
|
||
return nil
|
||
}
|
||
|
||
// Lpop 按顺序弹出元素 从队列头部弹出
|
||
func (r *Client) Lpop(key string) (string, error) {
|
||
result, err := r.client.LPop(key).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("LPop failed %+v:", err))
|
||
return "", err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// RPop 从线程安全队列取尾部取出元素
|
||
func (r *Client) RPop(key string) (string, error) {
|
||
result, err := r.client.RPop(key).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("RPop failed %+v:", err))
|
||
return "", err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// Lpush push 添加元素到头部
|
||
func (r *Client) Lpush(key string, values ...interface{}) error {
|
||
if err := r.client.LPush(key, values...).Err(); err != nil {
|
||
log.Error(fmt.Sprintf("LPush failed %+v:", err))
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// RPush push 添加元素到尾部
|
||
func (r *Client) RPush(key string, values ...interface{}) error {
|
||
if err := r.client.RPush(key, values...).Err(); err != nil {
|
||
log.Error(fmt.Sprintf("RPush failed %+v:", err))
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// RPushPipeline 将 values 按 batchSize 拆成多个 RPUSH,并通过一个 Pipeline 顺序执行。
|
||
// 返回最后一个 RPUSH 执行后的列表长度。调用方应限制单次 values 的数量,避免生成过大的 Pipeline。
|
||
func (r *Client) RPushPipeline(key string, values []string, batchSize int) (int64, error) {
|
||
return r.RPushPipelineContext(context.Background(), key, values, batchSize)
|
||
}
|
||
|
||
// RPushPipelineContext 与 RPushPipeline 相同,并继承调用方取消和超时。
|
||
func (r *Client) RPushPipelineContext(
|
||
ctx context.Context,
|
||
key string,
|
||
values []string,
|
||
batchSize int,
|
||
expirations ...time.Duration,
|
||
) (int64, error) {
|
||
if ctx == nil {
|
||
return 0, errors.New("redis RPushPipelineContext context must not be nil")
|
||
}
|
||
if batchSize <= 0 {
|
||
return 0, fmt.Errorf("redis RPushPipeline batchSize must be positive")
|
||
}
|
||
if len(values) == 0 {
|
||
return 0, nil
|
||
}
|
||
if len(expirations) > 1 {
|
||
return 0, errors.New("redis RPushPipelineContext accepts at most one expiration")
|
||
}
|
||
var expiration time.Duration
|
||
if len(expirations) == 1 {
|
||
expiration = expirations[0]
|
||
if expiration <= 0 {
|
||
return 0, errors.New("redis RPushPipelineContext expiration must be positive")
|
||
}
|
||
}
|
||
if r == nil || r.client == nil {
|
||
return 0, errors.New("redis RPushPipelineContext client must not be nil")
|
||
}
|
||
var lastCmd *goredis.IntCmd
|
||
var expireCmd *goredis.BoolCmd
|
||
_, err := r.client.WithContext(ctx).Pipelined(func(pipe goredis.Pipeliner) error {
|
||
for start := 0; start < len(values); start += batchSize {
|
||
end := start + batchSize
|
||
if end > len(values) {
|
||
end = len(values)
|
||
}
|
||
args := make([]interface{}, end-start)
|
||
for i := start; i < end; i++ {
|
||
args[i-start] = values[i]
|
||
}
|
||
lastCmd = pipe.RPush(key, args...)
|
||
}
|
||
if expiration > 0 {
|
||
// 与 RPUSH 位于同一连接、同一 Pipeline。即使响应丢失后清理
|
||
// 与迟到命令交错,重新创建的 building key 也不会永久残留。
|
||
expireCmd = pipe.Expire(key, expiration)
|
||
}
|
||
return nil
|
||
})
|
||
if err != nil {
|
||
log.Error("RPushPipeline failed", log.E(err))
|
||
return 0, err
|
||
}
|
||
length, err := lastCmd.Result()
|
||
if err != nil {
|
||
log.Error("RPushPipeline result failed", log.E(err))
|
||
return 0, err
|
||
}
|
||
if expireCmd != nil {
|
||
expired, expireErr := expireCmd.Result()
|
||
if expireErr != nil {
|
||
log.Error("RPushPipeline expire result failed", log.E(expireErr))
|
||
return 0, expireErr
|
||
}
|
||
if !expired {
|
||
return 0, errors.New("redis RPushPipeline failed to expire list")
|
||
}
|
||
}
|
||
return length, nil
|
||
}
|
||
|
||
// RPopLPush 线程安全队列
|
||
func (r *Client) RPopLPush(key string, value string) (string, error) {
|
||
result, err := r.client.RPopLPush(key, value).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("RPopLPush failed %+v:", err))
|
||
return "", err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// BRPopLPush 阻塞线程安全队列
|
||
func (r *Client) BRPopLPush(key string, value string, timeout time.Duration) (string, error) {
|
||
result, err := r.client.BRPopLPush(key, value, timeout).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("BRPopLPush failed %+v:", err))
|
||
return "", err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// LRem 从列表中删除元素
|
||
func (r *Client) LRem(key string, count int64, value string) (int64, error) {
|
||
result, err := r.client.LRem(key, count, value).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("BRPopLPush failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// MGet 批量获取Get值
|
||
func (r *Client) MGet(keys ...string) ([]interface{}, error) {
|
||
if len(keys) == 0 {
|
||
return nil, nil
|
||
}
|
||
|
||
result, err := r.client.MGet(keys...).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("MGet failed %+v:", err))
|
||
return nil, err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// MSet 批量设置Set值
|
||
func (r *Client) MSet(pairs ...interface{}) error {
|
||
if _, err := r.client.MSet(pairs...).Result(); err != nil {
|
||
log.Error(fmt.Sprintf("MSet failed %+v:", err))
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// LRange 获取列表值
|
||
func (r *Client) LRange(key string, start int64, stop int64) ([]string, error) {
|
||
data, err := r.client.LRange(key, start, stop).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("MSet failed %+v:", err))
|
||
return nil, err
|
||
}
|
||
return data, nil
|
||
}
|
||
|
||
// LCount 获取列表的数量
|
||
func (r *Client) LCount(key string) (int64, error) {
|
||
cnt, err := r.client.LLen(key).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("MSet failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
return cnt, nil
|
||
}
|
||
|
||
// LTrim 保留指定范围内的元素
|
||
func (r *Client) Ltrim(key string, start, end int64) (string, error) {
|
||
result, err := r.client.LTrim(key, start, end).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("MSet failed %+v:", err))
|
||
return "", err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// AppendByte 追加bytes
|
||
func (r *Client) AppendByte(key string, data []byte) (int64, error) {
|
||
str := string(data)
|
||
result, err := r.client.Append(key, str).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("MSet failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// ExpireKey 对键设置过期时间
|
||
func (r *Client) ExpireKey(key string, expirTime time.Duration) (bool, error) {
|
||
result, err := r.client.Expire(key, expirTime).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ExpireKey failed %+v:", err))
|
||
return false, err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// ExpireKey 对键设置指定过期时间
|
||
func (r *Client) ExpireKeAt(key string, expirTime time.Time) (bool, error) {
|
||
result, err := r.client.ExpireAt(key, expirTime).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ExpireKeAt failed %+v:", err))
|
||
return false, err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// Do 批处理执行redis
|
||
func (r *Client) Do(arg ...interface{}) (interface{}, error) {
|
||
result, err := r.client.Do(arg).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("Do failed %+v:", err))
|
||
return nil, err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// Eval 执行Lua脚本。涉及多个Redis键的读写必须通过脚本保证原子性。
|
||
func (r *Client) Eval(script string, keys []string, args ...interface{}) (interface{}, error) {
|
||
return r.EvalContext(context.Background(), script, keys, args...)
|
||
}
|
||
|
||
// EvalContext 执行Lua脚本并继承调用方取消和超时。
|
||
func (r *Client) EvalContext(
|
||
ctx context.Context,
|
||
script string,
|
||
keys []string,
|
||
args ...interface{},
|
||
) (interface{}, error) {
|
||
if ctx == nil {
|
||
return nil, errors.New("redis EvalContext context must not be nil")
|
||
}
|
||
if r == nil || r.client == nil {
|
||
return nil, errors.New("redis EvalContext client must not be nil")
|
||
}
|
||
result, err := r.client.WithContext(ctx).Eval(script, keys, args...).Result()
|
||
if err != nil {
|
||
log.Error("Eval failed", log.E(err))
|
||
return nil, err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// RunScript 执行预编译脚本,优先使用 EVALSHA 并在 NOSCRIPT 时自动回退 EVAL。
|
||
func (r *Client) RunScript(script *Script, keys []string, args ...interface{}) (interface{}, error) {
|
||
return r.RunScriptContext(context.Background(), script, keys, args...)
|
||
}
|
||
|
||
// RunScriptContext 与 RunScript 相同,并将取消和超时传递到 Redis 命令。
|
||
func (r *Client) RunScriptContext(
|
||
ctx context.Context,
|
||
script *Script,
|
||
keys []string,
|
||
args ...interface{},
|
||
) (interface{}, error) {
|
||
if ctx == nil {
|
||
return nil, errors.New("redis RunScriptContext context must not be nil")
|
||
}
|
||
if script == nil || script.script == nil {
|
||
return nil, errors.New("redis RunScriptContext script must not be nil")
|
||
}
|
||
if r == nil || r.client == nil {
|
||
return nil, errors.New("redis RunScriptContext client must not be nil")
|
||
}
|
||
result, err := script.script.Run(r.client.WithContext(ctx), keys, args...).Result()
|
||
if err != nil {
|
||
log.Error("RunScript failed", log.E(err))
|
||
return nil, err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// redis 管道流
|
||
func (r *Client) Piplined(fn func(goredis.Pipeliner) error) ([]goredis.Cmder, error) {
|
||
return r.client.Pipelined(fn)
|
||
}
|
||
|
||
// 生成管道流对象
|
||
func (r *Client) Pipliner() goredis.Pipeliner {
|
||
return r.client.Pipeline()
|
||
}
|
||
|
||
// setNx 锁
|
||
func (r *Client) SetNX(key string, value interface{}, expiration time.Duration) (bool, error) {
|
||
return r.SetNXContext(context.Background(), key, value, expiration)
|
||
}
|
||
|
||
// SetNXContext 原子写入键并继承调用方取消和超时。
|
||
func (r *Client) SetNXContext(
|
||
ctx context.Context,
|
||
key string,
|
||
value interface{},
|
||
expiration time.Duration,
|
||
) (bool, error) {
|
||
if ctx == nil {
|
||
return false, errors.New("redis SetNXContext context must not be nil")
|
||
}
|
||
if r == nil || r.client == nil {
|
||
return false, errors.New("redis SetNXContext client must not be nil")
|
||
}
|
||
result, err := r.client.WithContext(ctx).SetNX(key, value, expiration).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("SetNX failed %+v:", err))
|
||
return false, err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// Setnx 设置键值对 正确的封装
|
||
func (r *Client) Setnx_NewOK(key string, value interface{}, expirTime time.Duration) (v bool, err error) {
|
||
return r.SetNX(key, value, expirTime)
|
||
}
|
||
|
||
// 有序集合操作 添加多个有序集合
|
||
func (r *Client) ZAdd(key string, z ...Member) (int64, error) {
|
||
count, err := r.client.ZAdd(key, z...).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ZAdd failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
return count, nil
|
||
}
|
||
|
||
// 有序集合操作 指定成员增加传入值
|
||
func (r *Client) ZIncrBy(key string, inc float64, m string) (float64, error) {
|
||
count, err := r.client.ZIncrBy(key, inc, m).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ZIncrBy failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
return count, nil
|
||
}
|
||
|
||
// 有序集合 数量
|
||
func (r *Client) ZCard(key string) (int64, error) {
|
||
count, err := r.client.ZCard(key).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ZCard failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
return count, nil
|
||
}
|
||
|
||
// ZScore 获取元素分数
|
||
func (r *Client) ZScore(key, member string) (float64, error) {
|
||
data, err := r.client.ZScore(key, member).Result()
|
||
if err != nil {
|
||
//log.Error(fmt.Sprintf("ZScore failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
|
||
return data, nil
|
||
}
|
||
|
||
// ZCount 统计分数区间的元素个数
|
||
func (r *Client) ZCount(key, min, max string) (int64, error) {
|
||
data, err := r.client.ZCount(key, min, max).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ZCount failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
|
||
return data, nil
|
||
}
|
||
|
||
// 无序集合 数量
|
||
func (r *Client) SCard(key string) (int64, error) {
|
||
count, err := r.client.SCard(key).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("SCard failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
return count, nil
|
||
}
|
||
|
||
// 返回区间成员
|
||
func (r *Client) ZRange(key string, start, end int64) ([]string, error) {
|
||
data, err := r.client.ZRange(key, start, end).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ZRange failed %+v:", err))
|
||
}
|
||
return data, err
|
||
}
|
||
|
||
// ZRangeByScore 按分数取成员
|
||
func (r *Client) ZRangeByScore(key string, start, end string) ([]string, error) {
|
||
z := goredis.ZRangeBy{
|
||
Min: start,
|
||
Max: end,
|
||
}
|
||
data, err := r.client.ZRangeByScore(key, z).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ZRangeByScore failed %+v:", err))
|
||
return nil, err
|
||
}
|
||
return data, nil
|
||
}
|
||
|
||
// ZRevRange 通过索引,分数从高到低
|
||
func (r *Client) ZRevRange(key string, start, stop int64) ([]string, error) {
|
||
data, err := r.client.ZRevRange(key, start, stop).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ZRevRange failed %+v:", err))
|
||
}
|
||
|
||
return data, err
|
||
}
|
||
|
||
// ZRevRangeWithScores 通过索引,分数从高到低 一并返回分值
|
||
func (r *Client) ZRevRangeWithScores(key string, start, stop int64) ([]goredis.Z, error) {
|
||
data, err := r.client.ZRevRangeWithScores(key, start, stop).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ZRevRangeWithScores failed %+v:", err))
|
||
}
|
||
|
||
return data, err
|
||
}
|
||
|
||
// ZRevRangeWithScores2 按分数排序获取前n个元素并返回分数
|
||
func (r *Client) ZRevRangeWithScores2(key string, start, limit int64) ([]string, map[string]float64) {
|
||
stop := start + limit - 1
|
||
z, err := r.client.ZRevRangeWithScores(key, start, stop).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ZRevRangeWithScores failed %+v:", err))
|
||
}
|
||
|
||
var srt []string
|
||
members := make(map[string]float64)
|
||
for _, v := range z {
|
||
srt = append(srt, fmt.Sprint(v.Member))
|
||
members[fmt.Sprint(v.Member)] = v.Score
|
||
}
|
||
|
||
return srt, members
|
||
}
|
||
|
||
// 按照元素排序 删除指定排名的元素
|
||
func (r *Client) ZRemRangeByRank(key string, start, end int64) (int64, error) {
|
||
count, err := r.client.ZRemRangeByRank(key, start, end).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ZRemRangeByRank failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
return count, nil
|
||
}
|
||
|
||
// 按照元素排序 删除按指定的分数区间的的元素
|
||
func (r *Client) ZRemRangeByScore(key, start, end string) (int64, error) {
|
||
count, err := r.client.ZRemRangeByScore(key, start, end).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ZRemRangeByScore failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
return count, nil
|
||
}
|
||
|
||
// ZRem 删除有序集合元素
|
||
func (r *Client) ZRem(key string, members ...interface{}) (int64, error) {
|
||
count, err := r.client.ZRem(key, members...).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("ZRem failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
return count, nil
|
||
}
|
||
|
||
// 判断元素是否存在于当前set中
|
||
func (r *Client) ZSISMember(key string, member string) bool {
|
||
result, err := r.client.ZRank(key, member).Result()
|
||
if err != nil {
|
||
return false
|
||
}
|
||
return result != 0
|
||
}
|
||
|
||
// 无序集合操作 向集合添加元素
|
||
func (r *Client) SAdd(key string, members ...interface{}) (int64, error) {
|
||
count, err := r.client.SAdd(key, members...).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("SAdd failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
return count, nil
|
||
}
|
||
|
||
// 获取无序集合的元素
|
||
func (r *Client) SMembers(key string) ([]string, error) {
|
||
result, err := r.client.SMembers(key).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("SMembers failed %+v:", err))
|
||
}
|
||
return result, err
|
||
}
|
||
|
||
// 无序集合操作 删除集合中某一元素
|
||
func (r *Client) SRem(key string, members ...interface{}) (int64, error) {
|
||
count, err := r.client.SRem(key, members...).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("SRem failed %+v:", err))
|
||
return 0, err
|
||
}
|
||
return count, nil
|
||
}
|
||
|
||
// 无序集合操作 随机删除集合中某一元素
|
||
func (r *Client) SPop(key string, members ...interface{}) (string, error) {
|
||
val, err := r.client.SPop(key).Result()
|
||
if err != nil && err != goredis.Nil {
|
||
log.Error(fmt.Sprintf("SPop failed %+v:", err))
|
||
}
|
||
return val, err
|
||
}
|
||
|
||
// ScanSlice 返回集合成员
|
||
func (r *Client) ScanSlice(key string, container interface{}) error {
|
||
if err := r.client.SMembers(key).ScanSlice(container); err != nil {
|
||
log.Error(fmt.Sprintf("ScanSlice failed %+v:", err))
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 判断元素是否是集合中的成员
|
||
func (r *Client) SISMember(key string, members interface{}) (bool, error) {
|
||
result, err := r.client.SIsMember(key, members).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("SAdd failed %+v:", err))
|
||
return false, err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// 随机从集合中的选取成员
|
||
func (r *Client) SRandMember(key string) (string, error) {
|
||
result, err := r.client.SRandMember(key).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("SRandMember failed %+v:", err))
|
||
return "", err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// 随机从集合中的选取指定数量的成员
|
||
func (r *Client) SRandMemberN(key string, count int64) ([]string, error) {
|
||
result, err := r.client.SRandMemberN(key, count).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("SRandMemberN failed %+v:", err))
|
||
return nil, err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// HMget 获取hash值
|
||
func (r *Client) HMget(key string, fields []string) ([]interface{}, error) {
|
||
result, err := r.client.HMGet(key, fields...).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("HMget failed %+v:", err))
|
||
return nil, err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// Hget 获取hash值
|
||
func (r *Client) Hget(key string, field string) (string, error) {
|
||
result, err := r.client.HGet(key, field).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("Hget failed %+v:", err))
|
||
return "", err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// HMset 设置hash值
|
||
func (r *Client) HMset(key string, fields map[string]interface{}) error {
|
||
if _, err := r.client.HMSet(key, fields).Result(); err != nil {
|
||
log.Error(fmt.Sprintf("HMset failed %+v:", err))
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// HDel 删除hash多个键值
|
||
func (r *Client) HDel(key string, fields ...string) error {
|
||
if _, err := r.client.HDel(key, fields...).Result(); err != nil {
|
||
log.Error(fmt.Sprintf("HDel failed %+v:", err))
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 获取 哈希表的keys
|
||
func (r *Client) Hkeys(key string) []string {
|
||
result, err := r.client.HKeys(key).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("Hkeys failed %+v:", err))
|
||
return nil
|
||
}
|
||
return result
|
||
}
|
||
|
||
// Exists 检查给定 key 是否存在
|
||
func (r *Client) Exists(keys ...string) bool {
|
||
status, err := r.client.Exists(keys...).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("Exists failed %+v:", err))
|
||
return false
|
||
}
|
||
return status == 1
|
||
}
|
||
|
||
// 增加对应的键值 并返回增加后的结果
|
||
func (r *Client) Incr(key string) int64 {
|
||
result, err := r.client.Incr(key).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("Incr failed %+v:", err))
|
||
return 0
|
||
}
|
||
return result
|
||
}
|
||
|
||
// 增加对应的键值 并返回增加后的结果
|
||
func (r *Client) IncrBy(key string, num int64, expire time.Duration) int64 {
|
||
result, err := r.client.IncrBy(key, num).Result()
|
||
if err != nil {
|
||
log.Error(fmt.Sprintf("IncrBy failed %+v:", err))
|
||
return 0
|
||
}
|
||
if expire != 0 {
|
||
r.client.Expire(key, expire)
|
||
}
|
||
return result
|
||
}
|
||
|
||
// 将字符数组转换为 redis member
|
||
func Convert2RedisMemForNoScore(src []string) []Member {
|
||
if len(src) == 0 {
|
||
return nil
|
||
}
|
||
members := make([]Member, 0, len(src))
|
||
for _, v := range src {
|
||
if v != "" {
|
||
members = append(members, Member{Member: v, Score: 0})
|
||
}
|
||
}
|
||
return members
|
||
}
|
||
|
||
// 将字符数组转换为 redis member
|
||
func Convert2RedisMem(src string) Member {
|
||
return Member{Member: src}
|
||
}
|
||
|
||
// ping
|
||
func (r *Client) Ping() (*string, error) {
|
||
data, err := r.client.Ping().Result()
|
||
if err != nil {
|
||
if err == goredis.Nil {
|
||
return nil, nil
|
||
}
|
||
return nil, err
|
||
}
|
||
return &data, nil
|
||
}
|
||
|
||
func (r *Client) GetWithJson2Any(key string, val any) error {
|
||
rv := reflect.ValueOf(val)
|
||
if rv.Kind() != reflect.Ptr {
|
||
return errors.New("[val] params must be a pointer")
|
||
}
|
||
v, err := r.client.Get(key).Bytes()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if v == nil {
|
||
return goredis.Nil
|
||
}
|
||
return json.Unmarshal(v, val)
|
||
}
|
||
|
||
func (r *Client) SetWithAny2Json(key string, val any, exp time.Duration) error {
|
||
v, err := json.Marshal(val)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return r.client.Set(key, string(v), exp).Err()
|
||
}
|