package uniquex // Version: 2023年6月12日18:59:16 /** 说明: 1. 这是基于redis的全局锁 2. 只要没释放锁会自动的续期,不用考虑锁被提前释放的问题 3. 允许自定义超时时间 **/ import ( "context" "time" uuid "github.com/satori/go.uuid" "github.com/go-redis/redis/v8" ) type uniquex struct { ctx context.Context cancel context.CancelFunc redis *redis.Client uniqueKey string uuid string } const tokenValidTime = time.Second * 5 func NewUniquex(ctx context.Context, redis *redis.Client, uniqueKey string) *uniquex { ctx, cancel := context.WithCancel(ctx) // 获取Token return &uniquex{ ctx: ctx, cancel: cancel, redis: redis, uniqueKey: uniqueKey, uuid: uuid.NewV4().String(), } } func (u *uniquex) Lock() error { // 获取Token b, err := u.redis.SetNX(u.ctx, u.uniqueKey, u.uuid, tokenValidTime).Result() if !b { return err } go u.refreshToken() return nil } func (u *uniquex) Unlock() { u.cancel() // 释放Token script := ` local token = redis.call('get',KEYS[1]); if token == ARGV[1] then redis.call('del',KEYS[1]); return 'SUCCESS'; end; return 'ERROR' ` u.redis.Eval(u.ctx, script, []string{u.uniqueKey}, u.uuid) return } // 刷新Token func (u *uniquex) refreshToken() { script := ` local token = redis.call('get',KEYS[1]) if token == ARGV[1] then redis.call('set',KEYS[1],ARGV[2],ARGV[1]); return 'SUCCESS'; end; return 'ERROR'; ` for { time.Sleep(time.Second) select { case <-u.ctx.Done(): return default: } u.redis.Eval(u.ctx, script, []string{u.uniqueKey}, u.uuid, tokenValidTime.Seconds()) } }