package svc import ( "applet/app/md" "applet/app/utils" "applet/app/utils/cache" "errors" "fmt" "math/rand" "reflect" "time" ) const redisMutexLockExpTime = 15 // TryGetDistributedLock 分布式锁获取 // requestId 用于标识请求客户端,可以是随机字符串,需确保唯一 func TryGetDistributedLock(lockKey, requestId string, isNegative bool) bool { if isNegative { // 多次尝试获取 retry := 1 for { ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX") // 获取锁成功 if err == nil && ok == "OK" { return true } // 尝试多次没获取成功 if retry > 10 { return false } time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000))) retry += 1 } } else { // 只尝试一次 ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX") // 获取锁成功 if err == nil && ok == "OK" { return true } return false } } // ReleaseDistributedLock 释放锁,通过比较requestId,用于确保客户端只释放自己的锁,使用lua脚本保证操作的原子型 func ReleaseDistributedLock(lockKey, requestId string) (bool, error) { luaScript := ` if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end` do, err := cache.Do("eval", luaScript, 1, lockKey, requestId) fmt.Println(reflect.TypeOf(do)) fmt.Println(do) if utils.AnyToInt64(do) == 1 { return true, err } else { return false, err } } func GetDistributedLockRequestId(prefix string) string { return prefix + utils.IntToStr(rand.Intn(100000000)) } // HandleBalanceDistributedLock 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁 func HandleBalanceDistributedLock(masterId, uid, requestIdPrefix string) (cb func(), err error) { // 获取余额更新锁 balanceLockKey := fmt.Sprintf(md.UserFinValidUpdateLock, masterId, uid) requestId := GetDistributedLockRequestId(requestIdPrefix) balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true) if !balanceLockOk { return nil, errors.New("系统繁忙,请稍后再试") } cb = func() { _, _ = ReleaseDistributedLock(balanceLockKey, requestId) } return cb, nil }