使用Redis实现分布式锁

delaywu · · 1005 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

### 材料准备 ``` 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") } ```

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1005 次点击  ∙  1 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传