redis分散式鎖實現(golang版)

tsin發表於2021-09-26

程式碼

.
.
.


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

// HandleBalanceDistributedLock 處理餘額更新時獲取鎖和釋放鎖 如果加鎖成功,使用語句 ` defer cb() ` 釋放鎖
func HandleBalanceDistributedLock(paramA,paramB,requestIdPrefix string) (cb func(), err error){

  lockKey := fmt.Sprintf("%s:xxx_lock:%s", paramA, paramB)
  requestId := GetDistributedLockRequestId(requestIdPrefix)
  lockOk := TryGetDistributedLock(lockKey, requestId, true)
  if !balanceLockOk {
  return nil, errors.New("系統繁忙,請稍後再試")
 }
  cb = func() {
  _, _ = ReleaseDistributedLock(lockKey, requestId)
 }
  return cb, nil
}

使用示例

// 獲取鎖
cb, err := HandleBalanceDistributedLock(c.GetString("mid"), utils.IntToStr(user.Info.Uid), "acq_reward")
    if err != nil {
        return err
    }
    // 加鎖成功則最後釋放鎖
    if cb != nil {
        defer cb()
    }

參考

部落格:redis應用系列一:分散式鎖正確實現姿勢

本作品採用《CC 協議》,轉載必須註明作者和本文連結
Was mich nicht umbringt, macht mich stärker

相關文章