### 材料准备
```
github.com/go-redis/redis/v8 // 操作redis
github.com/gin-gonic/gin
go v1.17
```
#### 核心封装代码
```
package lib
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
"log"
"time"
)
type Locker struct {
key string
expire time.Duration
unlock bool
incLua *redis.Script
}
const incLua = `if redis.call('get',KEYS[1])==ARGV[1] then
return redis.call('expire',KEYS[1],ARGV[2])
else return '0'
end`
// ==========> option 模式初始化
type CmdOption func(locker *Locker)
type CmdOptions []CmdOption
func (this CmdOptions) Apply(locker *Locker) {
for _, option := range this {
option(locker)
}
}
func New(opts ...CmdOption) *Locker {
locker := &Locker{}
CmdOptions(opts).Apply(locker)
return locker
}
func WithKey(key string) CmdOption {
return func(locker *Locker) {
locker.key = key
}
}
// ==========> option 模式初始化 end
func NewLocker(key string) *Locker {
return &Locker{key: key, expire: time.Second * 30, incLua: redis.NewScript(incLua)}
}
func NewLockerWithTTL(key string, expire time.Duration) *Locker {
if expire.Seconds() <= 0 {
panic("error expire")
}
return &Locker{key: key, expire: expire, incLua: redis.NewScript(incLua)}
}
func (this *Locker) Lock() *Locker {
boolCmd := redisClient.SetNX(context.Background(), this.key, "1", 0)
if ok, err := boolCmd.Result(); err != nil || !ok {
panic(fmt.Sprintf("locker error with key:%s", this.key))
}
this.expandLockTime()
return this
}
func (this *Locker) UnLock() *Locker {
redisClient.Del(context.Background(), this.key)
this.unlock = true
return this
}
func (this *Locker) expandLockTime() {
sleepTime := this.expire.Seconds() * 2 / 3
go func() {
for {
time.Sleep(time.Duration(sleepTime))
if this.unlock {
break
}
this.resetExpire()
}
}()
}
// 重新设置过期时间
func (this *Locker) resetExpire() {
cmd := this.incLua.Run(context.Background(), redisClient, []string{this.key}, 1, this.expire.Seconds())
v, err := cmd.Result()
log.Printf("key=%s,续期结果:%v,%v\n", this.key, err, v)
}
```
#### 调用实例
```
package main
import (
"GoRedis/src/lib"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
var a = 0
func main() {
r := gin.New()
r.Use(func(ctx *gin.Context) {
defer func() {
if err := recover(); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": err})
}
}()
ctx.Next()
})
r.Handle("GET", "/", func(ctx *gin.Context) {
var locker = lib.NewLockerWithTTL("locker1", time.Second*30).Lock()
defer locker.UnLock()
fmt.Println(ctx.Query("t"))
if ctx.Query("t") != "" {
time.Sleep(time.Second * 5)
}
a++
ctx.JSON(http.StatusOK, gin.H{"message": a})
})
r.Run(":9091")
}
```
有疑问加站长微信联系(非本文作者))