etcd实现分布式锁

我们希望同一时间只有一个线程能够访问到资源,但是分布式资源点之间的协调会非常麻烦,这个时候我们就需要一个分布式锁。

etcd分布式锁实现原理:
1.利用租约在etcd集群中创建一个key,这个key有两种形态,存在和不存在,而这两种形态就是互斥量。
2.如果这个key不存在,那么线程创建key,成功则获取到锁,该key就为存在状态。
3.如果该key已经存在,那么线程就不能创建key,则获取锁失败。

package main

import (
	"go.etcd.io/etcd/v3/clientv3"
	"context"
	"fmt"
	"time"
)

type EtcdMutex struct {
	Ttl int64  //租约时间
	Conf clientv3.Config  //etcd集群配置
	Key string   //etcd的key
	cancel context.CancelFunc  //关闭续租的func
	lease clientv3.Lease
	leaseID clientv3.LeaseID
	txn clientv3.Txn
}

func(em *EtcdMutex)init()error{
	var err error
	var 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:
	em.txn.If(clientv3.Compare(clientv3.CreateRevision(em.Key),"=",0)).
		Then(clientv3.OpPut(em.Key,"",clientv3.WithLease(em.leaseID))).
		Else()
	txnResp,err := em.txn.Commit()
	if err != nil{
		return err
	}
	if !txnResp.Succeeded{   //判断txn.if条件是否成立
		return fmt.Errorf("抢锁失败")
	}
	return nil
}

func(em *EtcdMutex)UnLock(){
	em.cancel()
	em.lease.Revoke(context.TODO(),em.leaseID)
	fmt.Println("释放了锁")
}

测试分布式锁

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

	eMutex1 := &EtcdMutex{Conf:conf,Ttl:10,Key:"Dlock"}
	eMutex2 := &EtcdMutex{Conf:conf,Ttl:10,Key:"Dlock"}
	go func() {
		AA:err := eMutex1.Lock()
		if err != nil{
			fmt.Println("eMutex1 catch lock failed")
			time.Sleep(time.Second)
			goto AA
		}else {
			fmt.Println("eMutex1 catch lock success")
			time.Sleep(time.Second * 10)
			fmt.Println("eMutex1 release")
			eMutex1.UnLock()
		}
	}()

	go func() {
	BB:err := eMutex2.Lock()
		if err != nil{
			fmt.Println("eMutex2 catch lock failed")
			time.Sleep(time.Second)
			goto BB
		}else {
			fmt.Println("eMutex2 catch lock success")
			time.Sleep(time.Second * 10)
			fmt.Println("eMutex2 release")
			eMutex2.UnLock()
		}
	}()

	time.Sleep(time.Second*30)
}

结果
etcd实现分布式锁_第1张图片

你可能感兴趣的:(问题收集和解决)