Files
2023-09-16 20:14:20 +08:00

95 lines
1.6 KiB
Go

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())
}
}