golang基於go-redis實現分佈式鎖

項目最近涉及到自動彈縮,因此涉及到分佈式鎖相關的處理;
項目redis採用 go-redis庫(點擊跳轉)進行操作;因此基於此實現了一個分佈式鎖,做一個記錄;
如有缺陷,歡迎指正

  1. redis分佈式鎖的實現是基於 SETNX 命令的特性來完成的, 即:僅首次設置值時才能夠設置成功
    同時做了一定的保護,防止惡意不設置過期時間導致永久佔用鎖的情況。
  2. 釋放鎖時,使用了MULTI/EXEC ,同時 WATCH 鎖保證不會錯誤的釋放鎖;
package fwRedis

import (
	"errors"
	"fmt"
	"github.com/go-redis/redis/v7"
	"github.com/satori/go.uuid"
	"leiax00.com/fxWeb/util"
	"time"
)

func GetLock(lockName string, acquireTimeout, lockTimeOut time.Duration) (string, error) {
	code := uuid.NewV4().String()
	endTime := util.FwTimer.CalcMillis(time.Now().Add(acquireTimeout))
	for util.FwTimer.CalcMillis(time.Now()) <= endTime {
		if success, err := fwRedisClient.SetNX(lockName, code, lockTimeOut).Result(); err != nil && err != redis.Nil {
			return "", err
		} else if success {
			return code, nil
		} else if fwRedisClient.TTL(lockName).Val() == -1 { //-2:失效;-1:無過期;
			fwRedisClient.Expire(lockName, lockTimeOut)
		}
		time.Sleep(time.Millisecond)
	}
	return "", errors.New("timeout")
}

//var count = 0  // test assist
func ReleaseLock(lockName, code string) bool {
	txf := func(tx *redis.Tx) error {
		if v, err := tx.Get(lockName).Result(); err != nil && err != redis.Nil {
			return err
		} else if v == code {
			_, err := tx.Pipelined(func(pipe redis.Pipeliner) error {
				//count++
				//fmt.Println(count)
				pipe.Del(lockName)
				return nil
			})
			return err
		}
		return nil
	}

	for {
		if err := fwRedisClient.Watch(txf, lockName); err == nil {
			return true
		} else if err == redis.TxFailedErr {
			fmt.Println("watch key is modified, retry to release lock. err:", err.Error())
		} else {
			fmt.Println("err:", err.Error())
			return false
		}
	}
}

完整代碼實例 ==> please click me

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章