Golang基於Redis的分佈式鎖

Golang基於Redis的分佈式鎖


我理解的分佈式鎖:

一、爲了不單持有鎖的進程奔潰而沒法釋放鎖,因此必須可以爲鎖設定過時時間自動釋放鎖資源 二、使用TTL檢查鎖是否成功的被設置過時時間,若是返回-1(未被設置)的話,使用Expire爲其設定過時時間 三、在釋放鎖的時候須要使用Watch命令,確保監測的值在事務執行時時未被改變,若是其餘進程修改了鎖,會觸發事務異常,而後從新執行Watch。git


分享一個我寫的Redis封裝類,僅實現了鏈接和鎖github

package dao

import (
	"time"

	"github.com/go-redis/redis/v7"
	uuid "github.com/satori/go.uuid"
)

type Rds struct {
	Client         *redis.Client
	AcquireTimeout int32
	LockTimeout    int32
}

func NewRds(host, passwd string, db int) (rds *Rds, err error) {
	rdsOpt := &redis.Options{
		Addr:     host,
		Password: passwd,
		DB:       db,
	}
	client := redis.NewClient(rdsOpt)
	_, err = client.Ping().Result()
	if err != nil {
		return
	}
	rds = &Rds{Client: client, AcquireTimeout: 10, LockTimeout: 10}
	return
}

func (r *Rds) AcquireLockWithTimeout(key string) (identifier string, b bool) {
	identifier = uuid.NewV4().String()
	lockname := "lock:" + key
	end := time.Now().Add(time.Second * time.Duration(r.AcquireTimeout))
	for time.Now().Before(end) {
		if r.Client.SetNX(lockname, identifier, time.Second*time.Duration(r.LockTimeout)).Val() {
			// 若是key不存在,併成功設置了key
			b = true
			return
		} else if r.Client.TTL(lockname).Val() == -1 {
			// 若是key存在,可是沒有剩餘時間
			r.Client.Expire(lockname, time.Second*time.Duration(r.LockTimeout))
		}
		time.Sleep(time.Microsecond)
	}
	return
}

func (r *Rds) ReleaseLock(key, identifier string) (b bool) {
	lockname := "lock:" + key
	txf := func(tx *redis.Tx) error {
		v, err := tx.Get(lockname).Result()
		if err != nil {
			return err
		}
		_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
			if v == identifier {
				pipe.Del(lockname)
				b = true
			}
			return nil
		})
		return err
	}
	for {
		err := r.Client.Watch(txf, lockname)
		if err == nil {
			break
		} else if err == redis.TxFailedErr {
			glog.Error(err)
		}
	}
	return
}

https://bbs.guaik.org/topic/132/golang%E5%9F%BA%E4%BA%8Eredis%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81golang

相關文章
相關標籤/搜索