学习etcd分布式锁

之前使用redis分布式锁,最近看了下etcd的分布式锁,下面是测试demo

package main

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

func main() {
    conf = clientv3.Config{
        Endpoints:            []string{"localhost:2379"},
        DialTimeout:          5 * time.Second,
    }

    go tryLock1()
    go tryLock2()
    go tryLock3()
    go tryLock4()
    time.Sleep(10 * time.Second)

}

var conf clientv3.Config

type EtcdMutex struct {
    Ttl int64
    Conf clientv3.Config
    Key string
    cancel context.CancelFunc
    txn clientv3.Txn
    lease clientv3.Lease
    LeaseID clientv3.LeaseID
}

// 初始化锁
func (em *EtcdMutex) init() error {
    var (
        err error
        ctx context.Context
    )
    client, err := clientv3.New(em.Conf)
    if err != nil {
        return err
    }
    em.txn = clientv3.NewKV(client).Txn(context.TODO())
    em.lease = clientv3.NewLease(client)
    leaseResp, err := em.lease.Grant(context.TODO(), em.Ttl)
    if err != nil {
        return err
    }

    ctx, em.cancel = context.WithCancel(context.TODO())
    em.LeaseID = leaseResp.ID
    _, err = em.lease.KeepAlive(ctx, em.LeaseID)
    return err
}

// 获取锁
func (em *EtcdMutex) Lock() error {
    err := em.init()
    if err != nil {
        return err
    }

    // lock, 当前key版本等于0,则创建空值,并赋上租约ID,并提交
    txnResp, err := em.txn.If(clientv3.Compare(clientv3.CreateRevision(em.Key), "=", 0)).
        Then(clientv3.OpPut(em.Key, "", clientv3.WithLease(em.LeaseID))).
        Else().Commit()

    if err != nil {
        return err
    }

    // 是否创建成功
    if !txnResp.Succeeded {
        return fmt.Errorf("获取锁失败")
    }
    return nil
}

// 释放锁
func (em *EtcdMutex) Unlock(name string) {
    // 租约取消
    em.cancel()
    // 释放租期
    _, _ = em.lease.Revoke(context.TODO(), em.LeaseID)
    fmt.Printf("%s 释放锁", name)
}

func tryLock1() {
    e := &EtcdMutex{
        Conf: conf,
        Ttl: 10,
        Key: "lock",
    }
    err := e.Lock()
    if err != nil {
        fmt.Println("go1 获取锁失败")
        return
    }
    defer e.Unlock("go1")

    fmt.Println("go1 获取锁成功")
    time.Sleep(1 * time.Second)
}

func tryLock2() {
    e := &EtcdMutex{
        Conf: conf,
        Ttl: 10,
        Key: "lock",
    }
    err := e.Lock()
    if err != nil {
        fmt.Println("go2 获取锁失败")
        return
    }
    defer e.Unlock("go2")

    fmt.Println("go2 获取锁成功")
    time.Sleep(1 * time.Second)
}

func tryLock3() {
    e := &EtcdMutex{
        Conf: conf,
        Ttl: 10,
        Key: "lock",
    }
    err := e.Lock()
    if err != nil {
        fmt.Println("go3 获取锁失败")
        return
    }
    defer e.Unlock("go3")

    fmt.Println("go3 获取锁成功")
    time.Sleep(1 * time.Second)
}

func tryLock4() {
    e := &EtcdMutex{
        Conf: conf,
        Ttl: 10,
        Key: "lock",
    }
    err := e.Lock()
    if err != nil {
        fmt.Println("go4 获取锁失败")
        return
    }
    defer e.Unlock("go4")

    fmt.Println("go4 获取锁成功")
    time.Sleep(1 * time.Second)
}

运行结果

go3 获取锁失败
go2 获取锁成功
go4 获取锁失败
go1 获取锁失败
go2 释放锁
进程完成,并显示退出代码 0
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
39
粉丝
9
喜欢
71
收藏
102
排名:461
访问:1.9 万
私信
所有博文
社区赞助商