之前使用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 協議》,轉載必須註明作者和本文連結