手把手教媳婦 -ETCD實現(xiàn)分布式鎖

package etcdmux

import (
    "context"
    "fmt"
    "github.com/coreos/etcd/clientv3"
    "time"
)

//分布式鎖
type EtcdMutex struct {
    Ttl int64 //租約時間

    Conf clientv3.Config

    Key string //etcd的key

    cancel context.CancelFunc //關(guān)閉續(xù)約的func

    lease clientv3.Lease //租約

    leaseId clientv3.LeaseID //租約ID

    txn clientv3.Txn //etcd事務(wù)

}

//初始化鎖
func (mutex *EtcdMutex) init() error {

    client, err := clientv3.New(mutex.Conf)

    if err != nil {
        return err
    }
    //初始化txn
    mutex.txn = clientv3.NewKV(client).Txn(context.TODO())
    //初始化租約
    mutex.lease = clientv3.NewLease(client)
    //申請一個租約
    leaseResp, err := mutex.lease.Grant(context.TODO(), mutex.Ttl)

    if err != nil {
        return err
    }

    var ctx context.Context

    //初始化mutex的cancelFunc
    ctx, mutex.cancel = context.WithCancel(context.TODO())
    //初始化leaseId
    mutex.leaseId = leaseResp.ID
    //自動續(xù)約
    _, err = mutex.lease.KeepAlive(ctx, mutex.leaseId)

    return err

}

//獲取鎖
func (mutex *EtcdMutex) Lock() error {

    err := mutex.init()

    if err != nil {
        return err
    }
    //Lock,如果不存在key,那就創(chuàng)建一個key,并且提交事務(wù)
    mutex.txn.If(clientv3.Compare(clientv3.CreateRevision(mutex.Key), "=", 0)).
        Then(clientv3.OpPut(mutex.Key, "", clientv3.WithLease(mutex.leaseId))).
        Else()
    txnResp, err := mutex.txn.Commit()
    if err != nil {
        return err
    }
    if !txnResp.Succeeded { //判斷txn.if條件是否成立
        return fmt.Errorf("槍鎖失敗")
    }
    return nil

}

//釋放鎖
func (mutex *EtcdMutex) UnLock() {
    mutex.cancel()
    //撤銷租約
    mutex.lease.Revoke(context.TODO(), mutex.leaseId)
    fmt.Println("釋放了鎖")
}

測試代碼A-(利用兩個goroutine來測試搶奪鎖)

//測試鎖
func  TestMutex(t *testing.T) {
    var conf = clientv3.Config{
        Endpoints:   []string{"127.0.0.1:2379"},
        DialTimeout: 5 * time.Second,
    }

    //兩把鎖
    eMutex1 := &EtcdMutex{
        Conf: conf,
        Ttl:  10,
        Key:  "lock",
    }
    eMutex2 := &EtcdMutex{
        Conf: conf,
        Ttl:  10,
        Key:  "lock",
    }
    //groutine1
    go func() {
        err := eMutex1.Lock()
        defer eMutex1.UnLock()
        if err != nil {
            fmt.Println("groutine1搶鎖失敗")
            fmt.Println(err)
            return
        }
        fmt.Println("groutine1搶鎖成功")
        time.Sleep(10 * time.Second)

    }()

    //groutine2
    go func() {
        err := eMutex2.Lock()
        defer eMutex2.UnLock()
        if err != nil {
            fmt.Println("groutine2搶鎖失敗")
            fmt.Println(err)
            return
        }
        fmt.Println("groutine2搶鎖成功")

    }()
    time.Sleep(30 * time.Second)
}

啟動etcd,多運行幾次,會隨機出現(xiàn)goroutinne1和goroutine2其中一個搶到鎖另一個搶不到的情況

注意:此為不可重入鎖

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容