学习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 协议》,转载必须注明作者和本文链接