Skip to content

Latest commit

 

History

History
286 lines (234 loc) · 6.44 KB

readme.md

File metadata and controls

286 lines (234 loc) · 6.44 KB
permalink sidebarBasedOnContent
/
true

goclub/redis

Go Reference

易用性

go 社区有很多 redis 库, 但是大多数库在 redis 返回 nil 时候用 error 表示.例如使用GET方法:

cmd := client.Get(ctx, "name")
err = cmd.Err()
isNil := false
if err != nil {
    if errors.Is(err, redis.Nil) {
        isNil = true
    } else {
        return err
    }
}
if isNil {
    // do some
} else {
    log.Print(cmd.String())
}

代码写的复杂,心很累.

goclub/redis 的接口风格是

value, isNil, err := red.GET{Key:key}.Do(ctx, client) ; if err != nil {
    return
}
if isNil {
	// do some
} else {
	log.Print(value)
}

自由

redis 的核心是 RESP 协议. goclub/redis 提供以下接口对应 RESP.

// Connecter RESP
type Connecter interface {
	DoStringReply(ctx context.Context, args []string) (reply string, isNil bool, err error)
	DoStringReplyWithoutNil(ctx context.Context, args []string) (reply string, err error)
	DoIntegerReply(ctx context.Context, args []string) (reply int64, isNil bool, err error)
	DoIntegerReplyWithoutNil(ctx context.Context, args []string) (reply int64, err error)
	DoArrayIntegerReply(ctx context.Context, args []string) (reply []OptionInt64, err error)
	DoArrayStringReply(ctx context.Context, args []string) (reply []OptionString, err error)
	Eval(ctx context.Context, script Script) (reply Reply, isNil bool, err error)
	EvalWithoutNil(ctx context.Context, script Script) (reply Reply, err error)
}

你可以自由的使用任何命令.

replyInt64, err = client.DoIntegerReplyWithoutNil(
	ctx, 
	[]string{
		"HSET", key, "name", "goclub", "age", "18",
	})
if err != nil {
    return
}

你也可以查看 red.API 查看 goclub/redis 对哪些方法进行了封装


像是 RESP Simple Strings 这类操作你可以根据 redis 是否会返回 nil 去调用 DoStringReplyDoStringReplyWithoutNil

脚本则使用 Eval 或者EvalWithoutNil

script := `
if redis.call("GET", KEYS[1]) == ARGV[1]
then
	return redis.call("DEL", KEYS[1])
else
	return 0
end
`
reply, err := client.EvalWithoutNil(ctx, Script{
    KEYS: []string{key},
    ARGV: []string{value},
    Script: script,
}) ; if err != nil {
    return
}
/*
你可以根据 script 内容调用一下 reply 方法
reply.Int64()
reply.Uint64()
reply.String()
reply.Int64Slice()
reply.StringSlice()
*/

包容

你可以将 goclub/redis 理解为是一个驱动库,是一个"壳". goclub/redis 能通过接口适配 go 社区的说有 redis 库.

goredis 是非常流行的库, goclub/redis 默认支持了 goredisv8

直接来吧

连接redis | NewClient

字符串增删改查 | StringsCRUD

eval执行脚本 | Eval

直接写命令 |l DoIntegerReply

实用方法

goclub/redis 还基于redis实现了了一些实用的方法,帮助开发人员提高开发效率.防止重复造轮子.

Trigger

触发器

// Trigger 触发器
// 每5分钟出现3次则触发,但是10分钟内只触发一次
func exmaple () {
	triggered, err := red.Trigger{
		Namespace: "pay_fail_alarm",
		Interval: time.Minute*5,
		Threshold: 3,
		Frequency: time.Minute*5,
	}.Do(ctx, client) ; if err != nil {
	    return
	}
	if triggered {
		// do some
	}
}

Mutex

互斥锁

mutex := red.Mutex{
    Key: key,
	// Expire 控制锁定时间
    Expire: time.Millisecond*100,
	// Retry 当锁被占用时进入循环重试(此时会堵塞)
	// Retry.Times 重试上锁多少次
	// Retry.Interval 重试上锁间隔
    Retry: red.Retry{
        Times: 3,
		Interval:time.Millisecond*100,
    },
}
lockSuccess, unlock, err := mutex.Lock(context.TODO(), client) ; if err != nil {
    // 锁失败
    return
}
if lockSuccess == false {
    // 锁被占用
    return
}
// 处理某些业务
err = unlock(context.TODO()) ; if err != nil {
    *mutexCount--
	// 解锁失败
	log.Printf("%+v", err)
    return
}
// 解锁成功

IncrLimiter

递增限制器

alarm_1 := IncrLimiter{
    Namespace: "incr_limiter_alarm_1",
    Expire:    time.Second * 10,
    Maximum:   3,
}
/* 第1次 */
limited, err := alarm_1.Do(ctx, client) ; if err != nil {
    return
} // limited = false

/* 第2次 */
limited, err := alarm_1.Do(ctx, client) ; if err != nil {
    return
} // limited = false

/* 第3次 */
limited, err := alarm_1.Do(ctx, client) ; if err != nil {
    return
} // limited = false

/* 第4次 */
limited, err := alarm_1.Do(ctx, client) ; if err != nil {
    return
} // limited = true

SetLimiter

设值限制器

使用场景: 限制用户每天只能试读3个章节(如果不允许一天内反复试读相同章节则可以使用 IncrLimiter )
注意:
如果 Key = free_trial:{userID} Expire = 24h 是限制24小时
如果 Key = free_trial:2022-01-01:{userID} Expire = 24h 是限制每天

/* 第1次 用户1访问了章节1 */
limited, err := SetLimiter{
    Key:       "free_trial:2022-05-25:userID:1",
    Member:    "1"
    Expire:    time.Hour * 24,
    Maximum:   3,
}.Do(ctx, client) ; if err != nil {
    return
} // limited = false

/* 第2次 用户1重复访问了章节1 */
limited, err := SetLimiter{
    Key:       "free_trial:2022-05-25:userID:1",
    Member:    "1"
    Expire:    time.Hour * 24,
    Maximum:   3,
}.Do(ctx, client) ; if err != nil {
    return
} // limited = false

/* 第3次 用户1访问了章节2 */
limited, err := SetLimiter{
    Key:       "free_trial:2022-05-25:userID:1",
    Member:    "2"
    Expire:    time.Hour * 24,
    Maximum:   3,
}.Do(ctx, client) ; if err != nil {
    return
} // limited = false

/* 第4次 用户1访问了章节3 */
limited, err := SetLimiter{
    Key:       "free_trial:2022-05-25:userID:1",
    Member:    "3"
    Expire:    time.Hour * 24,
    Maximum:   3,
}.Do(ctx, client) ; if err != nil {
    return
} // limited = false

/* 第5次 用户1访问了章节4 */
limited, err := SetLimiter{
    Key:       "free_trial:2022-05-25:userID:1",
    Member:    "4"
    Expire:    time.Hour * 24,
    Maximum:   3,
}.Do(ctx, client) ; if err != nil {
    return
} // limited = true