注意有个v8版本,是有方式上不太一样
import (
"context"
"errors"
"fmt"
"github.com/go-redis/redis"
"math/rand"
"net"
"os"
"strconv"
"testing"
"time"
)
// 声明一个全局的rdb变量
var rdb *redis.Client
//1.初始化连接初始化连接
func initClient() (err error) {
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
_, err = rdb.Ping().Result()
if err != nil {
fmt.Println("连接redis失败")
os.Exit(1)
}
return nil
}
func redisClientInfo() {
rdb = redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
DB: 0,
Password: "",
Network: "tcp",
//连接池容量及闲置连接数量
PoolSize: 15, // 连接池最大socket连接数,默认为4倍CPU数, 4 * runtime.NumCPU
MinIdleConns: 10, //在启动阶段创建指定数量的Idle连接,并长期维持idle状态的连接数不少于指定数量;。
//超时
DialTimeout: 5 * time.Second, //连接建立超时时间,默认5秒。
ReadTimeout: 3 * time.Second, //读超时,默认3秒, -1表示取消读超时
WriteTimeout: 3 * time.Second, //写超时,默认等于读超时
PoolTimeout: 4 * time.Second, //当所有连接都处在繁忙状态时,客户端等待可用连接的最大等待时长,默认为读超时+1秒
//闲置连接检查包括IdleTimeout,MaxConnAge
//闲置连接检查的周期,默认为1分钟,-1表示不做周期性检查,只在客户端获取连接时对闲置连接进行处理。
IdleCheckFrequency: 60 * time.Second,
IdleTimeout: 5 * time.Minute, //闲置超时,默认5分钟,-1表示取消闲置超时检查
//连接存活时长,从创建开始计时,超过指定时长则关闭连接,默认为0,即不关闭存活时长较长的连接
MaxConnAge: 0 * time.Second,
//命令执行失败时的重试策略
MaxRetries: 0, // 命令执行失败时,最多重试多少次,默认为0即不重试
MinRetryBackoff: 8 * time.Millisecond, //每次计算重试间隔时间的下限,默认8毫秒,-1表示取消间隔
MaxRetryBackoff: 512 * time.Millisecond, //每次计算重试间隔时间的上限,默认512毫秒,-1表示取消间隔
//可自定义连接函数
Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
netDialer := &net.Dialer{
Timeout: 5 * time.Second,
KeepAlive: 5 * time.Minute,
}
return netDialer.Dial("tcp", "127.0.0.1:6379")
},
//钩子函数
//仅当客户端执行命令时需要从连接池获取连接时,如果连接池需要新建连接时则会调用此钩子函数
OnConnect: func(ctx context.Context, cn *redis.Conn) error {
fmt.Printf("conn=%v\n", cn)
return nil
},
})
}
func TestRedis1(t *testing.T) {
//1.设置key和value,以及key的过期时间expiration
err := rdb.Set("key", "val", 0).Err()
if nil != err {
fmt.Println("插入失败")
return
}
//2.获取key的值
valStr, err := rdb.Get("key").Result()
if nil != err {
if redis.Nil == err {
fmt.Println("不存在")
return
}
fmt.Println("插入数据异常err:" + err.Error())
return
}
fmt.Println(valStr)
//3.设置一个key的值,并且返回这个key的旧值
rdb.GetSet("key", "val").Result()
//4.key不存在,则设置这个key,存在则失败, 成功返回1,失败返回0
rdb.SetNX("", "", 0).Err()
//5.批量设置key的值
rdb.MSet("").Err()
//6.批量查询key值
rdb.MGet("").Result()
//7.删除单个或多个key
rdb.Del("key1", "key2").Err()
//8.设置指定key的过期时间
rdb.Expire("", 0).Err()
//9.针对一个key进行递增操作
rdb.Incr("key").Result()
//10.针对一个key进行递减操作
rdb.Decr("key").Result()
//11.查看键的类型
str, err := rdb.Type("key").Result()
if err == nil {
fmt.Println(str)
}
//12.查看指定key的过期时间
time, err := rdb.TTL("key").Result()
if err == nil {
fmt.Println(time)
}
//13.执行自定义命令
rdb.Do("set", "key", 10, "EX", 3600).Err()
//14.根据前缀获取Key
rdb.Keys("key*")
//15.通过游标迭代器查询,非阻塞式查询key,与keys命令的区别:
// > scan时间复杂度虽然也是O(N),但是分次进行的,不会阻塞线程。
// > scan命令提供了limit参数,可以控制每次返回结果的最大条数。
//当Scan方法需要的cursor也就是游标为0时,表示一个新的完整迭代
//当执行完Scan方法后,返回的数据实际包含两部分一个是游标,一个是元素值
//如果下次想再此处继续迭代,则将cursor设置为上次返回的
//count: 指定返回结果的最大条数
iter := rdb.Scan(0, "k*", 0).Iterator()
for iter.Next() {
err := rdb.Del(iter.Val()).Err()
if err != nil {
panic(err)
}
}
}
func TestRedisHash(t *testing.T) {
//1.添加
err := rdb.HSet("key", "field", "val").Err()
if nil != err {
fmt.Println("插入失败")
return
}
//2.获取指定key的指定field值
rdb.HGet("key", "field").Result()
//3.批量设置
fieldMap := make(map[string]interface{})
fieldMap["field1"] = "val1"
fieldMap["field2"] = "val2"
rdb.HMSet("key", fieldMap)
//4.获取key下所有field值,或者获取key下一个或多个field值
rdb.HMGet("key", "field1", "field2")
//5.获取指定key下所有field与对应的值
rdb.HGetAll("key")
//6.在指定key的指定field上进行累加
rdb.HIncrBy("key", "field", 1)
//7.获取指定key下的所有field
rdb.HKeys("key")
//8.获取指定key下字段数量
rdb.HLen("key")
//9.向指定key中添加field如果不存在添加成功返回1
//如果存在添加无效返回0
rdb.HSetNX("key", "field", "val")
//10.删除指定key,或删除key下的指定一个或多个field
rdb.HDel("key", "field1", "field2")
//11.判断指定key,或key下的指定field是否存在
rdb.HExists("", "")
//12.获取指定key下所有value
rdb.HVals("key")
}
func TestRedisList(t *testing.T) {
//1.添加
rdb.LPush("name", "marry").Err()
rdb.LPush("name", "tom").Err()
//2.将一个或多个值插入到列表头部,如果key不存在,创建一个空列表并执行LPUSH操作
rdb.LPush("key", "val").Err()
//将一个值插入到已存在的列表头部,列表不存在时操作无效
rdb.LPushX("key", "val")
//3.将一个或多个值插入到列表的尾部(最右边)如果列表不存在,创建一个空列表并执行RPUSH操作
rdb.RPush("", "")
//将一个值插入到已存在的列表尾部,列表不存在时操作无效
rdb.RPushX("", "")
//4.移除并返回列表的第一个元素
rdb.LPop("key").Result()
//5.移除并返回列表的最后一个元素
rdb.RPop("key").Result()
//6.返回列表的长度,如果列表key不存在,则key被解释为一个空列表,返回 0
rdb.LLen("")
//7.返回列表中指定区间内的元素
// 0表示第一个,1表示第二个,-1表示最后一个,-2表示倒数第二个
rdb.LRange("key", 0, 2)
//8.根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素
//count>0: 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为COUNT
//count<0: 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为COUNT的绝对值
//count=0 : 移除表中所有与 VALUE 相等的值
rdb.LRem("key ", 22, "")
//9.通过索引获取列表中的元素,其中 -1表示最后一个,-2表示倒数第二个
rdb.LIndex("key", 3)
//10.在列表的元素前或者后插入元素,当指定元素不存在时,返回 -1
rdb.LInsert("key", "BEFORD|AFTER", "元素", "插入val")
}
func TestRedisSet(t *testing.T) {
//1.添加
rdb.SAdd("name", "xh")
//2.获取key下元素个数
rdb.SCard("key")
//3.判断集合中是否有该值
rdb.SIsMember("key", "val")
//4.获取
rdb.SMembers("key")
//5.删除key或删除该key下的指定元素
rdb.SRem("key", "val")
//6.随机弹出key下的一个元素
rdb.SPop("key")
//7.随机弹出key下的指定个数元素
rdb.SPopN("key", 2)
}
func TestRedisZSet(t *testing.T) {
//1.添加
z := redis.Z{
Score: 2,
Member: "kevin",
}
rdb.ZAdd("key", z)
//2.获取集合中元素个数
rdb.ZCard("key")
//3.获取集合中指定分数区间的个数
rdb.ZCount("key", "1", "20")
//4.对集合中指定成员的分数加上增量 increment
rdb.ZIncrBy("key", 20, "val")
//5.获取指定区间内的成员,返回时按分数从小到大排序,相同则按照元素排序
rdb.ZRange("key", 1, 10)
//6.获取指定区间内的成员,返回时按分数从大到小排序,相同则按照元素排序
rdb.ZRevRange("key", 1, 10)
//7.获取指定分数区间的元素,返回时按分数从小到大排序
zRange := redis.ZRangeBy{
Min: "0",
Max: "3",
}
rdb.ZRangeByScore("key", zRange)
//8.获取指定分数区间的元素,返回时按分数从大到小排序
rdb.ZRevRangeByScore("key", zRange)
//9.返回在分数之类的所有的成员以及分数??
rdb.ZRangeByScoreWithScores("", zRange)
//10.删除指定key,或key下的指定一个或多个元素
rdb.ZRem("key", "val1", "")
//11.移除有序集中,指定排名(rank)区间内的所有成员
rdb.ZRemRangeByRank("key", 10, 11)
//12.移除有序集中,指定分数区间内的所有成员
rdb.ZRemRangeByScore("key", "1", "10")
//13.返回指定元素分数
rdb.ZScore("key", "val")
//14.返回元素在集合中的排名
rdb.ZRank("key", "val")
}
func TestRedisPublishAndSubscribe(t *testing.T) {
//发布
rdb.Publish("频道1", "数据")
//订阅,监听指定频道获取数据
pubsub := rdb.Subscribe("频道")
// 第一种接收消息方法
// ch := pubsub.Channel()
// for msg := range ch
// fmt.Println(msg.Channel, msg.Payload)
// 第二种接收消息方法
for {
msg, err := pubsub.ReceiveMessage()
if err != nil {
panic(err)
}
fmt.Println(msg.Channel, msg.Payload)
}
//匹配模式订阅
pubsub = rdb.PSubscribe("mychannel*")
defer pubsub.Close()
//取消订阅
pubsub.Unsubscribe("mychannel1", "mychannel2")
}
func TestRedisTx(t *testing.T) {
//1.执行TxPipeline()开启事物,底层会执行MULTI命令开启事物块
pipe := rdb.TxPipeline()
//2.执行业务操作
incr := pipe.Incr("tx_pipeline_counter")
pipe.Expire("tx_pipeline_counter", time.Hour)
//3.如果业务正常执行完毕,调用Exec()提交事物,底层会执行"EXEC"执行事物块中的所有命令
_, err := pipe.Exec()
fmt.Println(incr.Val(), err)
//4.如果中间有异常可以执行Discard(),底层会执行"DISCARD"取消事务,放弃执行事务块内的所有命令
pipe.Discard()
//5.有些场景下需要配合WATCH
//使用WATCH命令监视某个键,事物执行EXEC命令的这段时间内
//如果有其他用户抢先对被监视的键进行了替换、更新、删除等操作,
//可以选择提交或放弃事物
key := "watch_count"
err = rdb.Watch(func(tx *redis.Tx) error {
n, err := tx.Get(key).Int()
if err != nil && err != redis.Nil {
return err
}
_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Set(key, n+1, 0)
return nil
})
return err
}, key)
}
- eval: 代表执行lua语言的命令
- script:一段可以在redis服务器运行的 lua5.1的脚本
- numkeys:用于指定健名参数的个数
- key [key …]:从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key)。这些键可以通过lua的全局变量KEYS数组获取,用 1 访问形式 KEYS[1],2 是 KEYS[2]
- arg [arg …]:附加参数,可以在lua中用全局变量 ARGV 数组访问,访问形式 ARGV[1],ARGC[2] 等
//示例: luaStr:= eval "return KEYS[1],KEYS[2],ARGV[1],ARGV[2]" 2 key1 key2 first second
// eval lua脚本 参数个数(假设参数个数=2) 参数1 参数2 参数1值 参数2值
var incrBy = redis.NewScript(`
local key = KEYS[1]
local change = ARGV[1]
local value = redis.call("GET", key)
if not value then
value = 0
end
value = value + change
redis.call("SET", key, value)
return value
`)
var sum = redis.NewScript(`
local key = KEYS[1]
local sum = redis.call("GET", key)
if not sum then
sum = 0
end
local num_arg = #ARGV
for i = 1, num_arg do
sum = sum + ARGV[i]
end
redis.call("SET", key, sum)
return sum
`)
func TestRedisLua(t *testing.T) {
fmt.Printf("# INCR BY\n")
for _, change := range []int{+1, +5, 0} {
num, err := incrBy.Run(rdb, []string{"my_counter"}, change).Int()
if err != nil {
panic(err)
}
fmt.Printf("incr by %d: %d\n", change, num)
}
sum, err := sum.Run(rdb, []string{"my_sum"}, 1, 2, 3).Int()
if err != nil {
panic(err)
}
fmt.Printf("sum is: %d\n", sum)
}
- 基于redis实现分布式锁,其原理是依赖redis单线程,原子性存储一个key
- 封装一个代表redis锁核心的上下文结构体,内部添加一个context.Context,通过context.Context解决锁超时问题
- 考虑锁重入,与释放错乱问题,锁key 对应的value是代表每一把锁得唯一编号
- 为保证原子性提供获取锁与释放锁的lua脚本
type RedisLock struct {
ctx context.Context //用来解决锁超时问题
timeoutMs int //锁超时时间
key string //锁key,也就是redis存储时的key
id string //锁编号,也就是redis存储时的val
}
1.提供获取锁脚本, 释放锁脚本,
const (
//加锁时执行lua脚本:
//考虑锁的可重入性: 加锁时除了保存锁key,还需要保存代表每一把锁的id
//在加锁时先get获取,如果该锁key以存在,说明是锁重入刷新ttl,
//没有则set, 这种是可重入锁,防止在同一线程中多次获取锁而导致死锁发生
//KEYS[1]: 表示锁key
//ARGV[1]: 表示value
//ARGV[2]: 表示过期时间
lockCommand = `if redis.call("GET", KEYS[1]) == ARGV[1] then
redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2])
return "OK"
else
--注意:这里执行的是setnx,如果已存在插入失败返回0
return redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2])
end`
//解锁时执行lua脚本
//也就是删除指定锁key,必须先匹配id值,防止A超时后,B马上获取到锁,A的解锁把B的锁删了
delCommand = `if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end`
)
var (
// 默认超时时间
defaultTimeout = 500 * time.Millisecond
// 重试间隔
retryInterval = 10 * time.Millisecond
// 上下文取消
ErrContextCancel = errors.New("context cancel")
)
//1.初始化锁
func NewRedisLock(ctx context.Context, key string) *RedisLock {
timeout := defaultTimeout
if deadline, ok := ctx.Deadline(); ok {
timeout = deadline.Sub(time.Now())
}
rl := &RedisLock{
ctx: ctx,
timeoutMs: int(timeout.Milliseconds()),
key: key,
id: randomStr(),
}
return rl
}
//2.尝试加锁方法
func (rl *RedisLock) TryLock() (bool, error) {
t := strconv.Itoa(rl.timeoutMs)
resp, err := rdb.Eval(lockCommand, []string{rl.key}, []string{rl.id, t}).Result()
if err != nil || resp == nil {
return false, nil
}
reply, ok := resp.(string)
return ok && reply == "OK", nil
}
//3.加锁方法
func (rl *RedisLock) Lock() error {
for {
select {
case <-rl.ctx.Done():
return ErrContextCancel
default:
//续时
b, err := rl.TryLock()
if err != nil {
return err
}
if b {
return nil
}
time.Sleep(retryInterval)
}
}
}
//4.是否锁方法
func (rl *RedisLock) Unlock() {
rdb.Eval(delCommand, []string{rl.key}, []string{rl.id}).Result()
}
//5.生成锁编号
func randomStr() string {
letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
b := make([]byte, 10)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
func BenchmarkRedisLock1(t *testing.B) {
x := t.N
wg := sync.WaitGroup{}
wg.Add(x)
now := time.Now()
var n int64
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
for i := 0; i < x; i++ {
go func() {
defer wg.Done()
rl := NewRedisLock(ctx, "testLock")
if err := rl.Lock(); err != nil {
cancel() // 这里cancel,可以在第一个超时或者发生错误后,后边的不再尝试加锁。
return
}
defer rl.Unlock()
atomic.AddInt64(&n, 1)
}()
}
wg.Wait()
fmt.Printf("测试数:%d\t成功数:%d\t结果:%t\t耗时: %d \n", x, n, x == int(n), time.Since(now).Milliseconds())
}
import (
"fmt"
"github.com/go-redis/redis"
"os"
"testing"
"time"
)
// 声明一个全局的rdb变量
var rdb *redis.Client
func initClient() *redis.Client {
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
_, err := rdb.Ping().Result()
if err != nil {
fmt.Println("连接redis失败")
os.Exit(1)
}
return rdb
}
//基于redis的bitMap,记录用户一个月内访问uv(既一天访问一次统计)
func TestRedisSetBitMap(t *testing.T) {
//获取redis句柄
redisDb := initClient()
//1.组装key: 酒店编码:时间:用户id
key := "IHOTEL:2023-03-27:111222AAASSS"
now := time.Now()
//2.存储为bitmap类型,key,所在月的天数为偏移量,value为1
err := redisDb.Do("setbit", key, now.Day(), 1).Err()
if nil != err {
fmt.Printf("插入失败 err:%v", err)
}
//3.获取当天开始时间戳与本月最后一天的结束时间戳,计算实现时间
startTime, _ := GetTimeStartAndEnd(now)
lastTime := now.AddDate(0, 1, -1)
_, endTime := GetTimeStartAndEnd(lastTime)
var duration_Seconds time.Duration = time.Duration(endTime-startTime) * time.Second
//4.设置失效时间
err = redisDb.Expire(key, duration_Seconds).Err()
if nil != err {
fmt.Printf("设置失效时间异常 err: %v", err)
}
//5.统计指定key的访问量(bitcount也就是统计指定key的valu为1的数量)
r, err := redisDb.Do("bitcount", key).Result()
if nil != err {
fmt.Printf("查询失败 err:%v", err)
return
}
fmt.Printf("查询结果为 result:%v", r)
}
//获取指定time的开始时间戳与结束时间戳
func GetTimeStartAndEnd(NowTime time.Time) (int64, int64) {
var startTime time.Time
//NowTime := time.Date(2022, 9, 15, 0, 0, 0, 0, time.Local)
if NowTime.Hour() == 0 && NowTime.Minute() == 0 && NowTime.Second() == 0 {
startTime = time.Unix(NowTime.Unix()-86399, 0) //当天的最后一秒
} else {
startTime = time.Unix(NowTime.Unix()-86400, 0)
}
currentYear := startTime.Year()
currentMonth := startTime.Month()
currentDay := startTime.Day()
yesterdayStartTime := time.Date(currentYear, currentMonth, currentDay, 0, 0, 0, 0, time.Local).Unix()
yesterdayEndTime := time.Date(currentYear, currentMonth, currentDay, 23, 59, 59, 0, time.Local).Unix()
return yesterdayStartTime, yesterdayEndTime
}
- 执行go get 命令拉取go-redis
- 读取配置参数调用NewClient()获取redis连接
import (
"lmcd_siteserver/config"
"lmcd_siteserver/log"
"os"
"github.com/go-redis/redis"
)
var (
rClient = New()
)
func New() *redis.Client {
log.TraceLog("Redis", "connect redis ...")
address, err := config.Conf.GetValue("redis", "address")
if err != nil {
log.ErrorLog("Redis", "Load redis Error: read config value failed redis address ")
return nil
}
password, err := config.Conf.GetValue("redis", "password")
if err != nil {
log.ErrorLog("Redis", "Load redis Error: read config value failed redis password ")
return nil
}
database, err := config.Conf.Int("redis", "database")
if err != nil {
log.ErrorLog("Redis", "Load redis Error: read config value failed redis password ")
return nil
}
client := redis.NewClient(&redis.Options{
Addr: address,
Password: password,
DB: database,
})
if client == nil {
log.ErrorLog("Redis", "open redis failed %s ", address)
os.Exit(0)
}
pong, err := client.Ping().Result()
if pong == "PONG" {
log.TraceLog("Redis", "connect redis OK")
} else {
log.ErrorLog("Redis", "connect redis failed :"+err.Error())
//os.Exit(0)
return nil
}
return client
}
import (
"lmcd_siteserver/lmerror"
"time"
)
func Set(key, value string, timeout int64) (lmerr *lmerror.LmError) {
client := rClient
if client == nil {
return lmerror.NewLmError(lmerror.REDIS_CONNECT_ERROR, "redisdatabase:Set Error "+key+" | "+value)
}
var duration_Seconds time.Duration = time.Duration(timeout) * time.Second
err := client.Set(key, value, duration_Seconds).Err()
if err != nil {
return lmerror.NewLmError(lmerror.REDIS_SET_ERROR, "redisdatabase:Set Error "+key+" | "+value+" | "+err.Error())
}
return nil
}
func Get(key string) (value string, lmerr *lmerror.LmError) {
client := rClient
if client == nil {
return "", lmerror.NewLmError(lmerror.REDIS_CONNECT_ERROR, "redisdatabase:Set Error "+key)
}
searchvalue, err := client.Get(key).Result()
if err != nil {
return "", lmerror.NewLmError(lmerror.REDIS_GET_ERROR, "redisdatabase:Get Error "+key+" | "+err.Error())
}
return searchvalue, nil
}
func HGet(key string, index string) (value string, lmerr *lmerror.LmError) {
client := rClient
if client == nil {
return "", lmerror.NewLmError(lmerror.REDIS_CONNECT_ERROR, "redisdatabase:Set Error "+key)
}
result, err := client.HGet(key, index).Result()
if err != nil {
return "", lmerror.NewLmError(lmerror.REDIS_HGET_ERROR, "redisdatabase:Get Error "+key+" | "+err.Error())
}
return result, nil
}
func HSet(key string, index string, value string, timeout int) (ret int, lmerr *lmerror.LmError) {
client := rClient
if client == nil {
return 0, lmerror.NewLmError(lmerror.REDIS_CONNECT_ERROR, "redisdatabase:HSet Error "+key)
}
n, err := client.Do("hset", key, index, value).Result()
if err != nil {
return 0, lmerror.NewLmError(lmerror.REDIS_HGET_ERROR, "redisdatabase:HSet Error "+key+" | "+err.Error())
}
var duration_Seconds time.Duration = time.Duration(timeout) * time.Second
err = client.Expire(key, duration_Seconds).Err()
if err != nil {
return 0, lmerror.NewLmError(lmerror.REDIS_SETTIME_ERROR, "redisdatabase:Expire Error "+key)
}
return int(n.(int64)), nil
}
func HGetAll(key string) (ret map[string]string, lmerr *lmerror.LmError) {
client := rClient
var result map[string]string
if client == nil {
return result, lmerror.NewLmError(lmerror.REDIS_CONNECT_ERROR, "redisdatabase:Set Error "+key)
}
result, err := client.HGetAll(key).Result()
if err != nil {
return result, lmerror.NewLmError(lmerror.REDIS_HGET_ERROR, "redisdatabase:Get Error "+key+" | "+err.Error())
}
return result, nil
}
func HMSet(key string, data map[string]interface{}, timeout int) (lmerr *lmerror.LmError) {
client := rClient
if client == nil {
return lmerror.NewLmError(lmerror.REDIS_CONNECT_ERROR, "redisdatabase:Set Error "+key)
}
err := client.HMSet(key, data).Err()
if err != nil {
return lmerror.NewLmError(lmerror.REDIS_HSET_ERROR, "redisdatabase:Get Error "+key+" | "+err.Error())
}
var duration_Seconds time.Duration = time.Duration(timeout) * time.Second
err = client.Expire(key, duration_Seconds).Err()
if err != nil {
return lmerror.NewLmError(lmerror.REDIS_SETTIME_ERROR, "redisdatabase:Expire Error "+key)
}
return nil
}
func Incr(key string) (int, *lmerror.LmError) {
client := rClient
if client == nil {
return 0, lmerror.NewLmError(lmerror.REDIS_CONNECT_ERROR, "redisdatabase:Incr Error "+key)
}
ret, err := client.Incr(key).Result()
if err != nil {
client.Set(key, 10000, 0)
return 0, nil
}
return int(ret), nil
}