下面描述使用 Etcd 实现分布式锁的业务流程,假设对某个共享资源设置的锁名为:/lock/mylock

步骤 1: 准备

客户端连接 Etcd,以 /lock/mylock 为前缀创建全局唯一的 key,假设第一个客户端对应的 key="/lock/mylock/UUID1",第二个为 key="/lock/mylock/UUID2";客户端分别为自己的 key 创建租约 - Lease,租约的长度根据业务耗时确定,假设为 15s;

步骤 2: 创建定时任务作为租约的“心跳”

当一个客户端持有锁期间,其它客户端只能等待,为了避免等待期间租约失效,客户端需创建一个定时任务作为“心跳”进行续约。此外,如果持有锁期间客户端崩溃,心跳停止,key 将因租约到期而被删除,从而锁释放,避免死锁。

步骤 3: 客户端将自己全局唯一的 key 写入 Etcd

进行 put 操作,将步骤 1 中创建的 key 绑定租约写入 Etcd,根据 Etcd 的 Revision 机制,假设两个客户端 put 操作返回的 Revision 分别为 1、2,客户端需记录 Revision 用以接下来判断自己是否获得锁。

步骤 4: 客户端判断是否获得锁

客户端以前缀 /lock/mylock 读取 keyValue 列表(keyValue 中带有 key 对应的 Revision),判断自己 key 的 Revision 是否为当前列表中最小的,如果是则认为获得锁;否则监听列表中前一个 Revision 比自己小的 key 的删除事件,一旦监听到删除事件或者因租约失效而删除的事件,则自己获得锁。

步骤 5: 执行业务

获得锁后,操作共享资源,执行业务代码。

步骤 6: 释放锁

完成业务流程后,删除对应的key释放锁。

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

etcd分布式锁实现原理:

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

锁结构体:

在使用该锁时,需要传入Ttl,Conf,Key字段来初始化锁

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.Errof("抢锁失败")
}
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{"172.16.196.129:2380", "192.168.50.250:2380"},
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()
if err != nil{
fmt.Println("groutine1抢锁失败")
fmt.Println(err)
return
}
fmt.Println("groutine1抢锁成功")
time.Sleep(10*time.Second)
defer eMutex.UnLock()
}() //groutine2
go func() {
err := eMutex2.Lock()
if err != nil{
fmt.Println("groutine2抢锁失败")
fmt.Println(err)
return
}
fmt.Println("groutine2抢锁成功")
defer eMutex.UnLock()
}()
time.Sleep(30*time.Second)
}

golang基于etcd实现分布式锁(转)的更多相关文章

  1. 一次基于etcd的分布式锁自动延时失败问题的排查

    今天在测试基于etcd的分布式锁过程中,在测试获取锁后,释放之前超出TTL时长的情况下自动延长TTL这部分功能,在延长指定key的TTL时总是返回404错误信息,在对目标KEY更新TTL时目标KEY已 ...

  2. golang使用etcd实现分布式锁

    package main import ( "context" "fmt" "time" "go.etcd.io/etcd/cli ...

  3. 基于 Redis 实现分布式锁

    1.主流分布式锁实现方案 基于数据库实现分布式锁 基于缓存(redis 等) 基于 Zookeeper 2.根据实现方式分类 类 CAS 自旋式分布式锁:询问的方式,类似 java 并发编程中的线程获 ...

  4. 基于redis 实现分布式锁的方案

    在电商项目中,经常有秒杀这样的活动促销,在并发访问下,很容易出现上述问题.如果在库存操作上,加锁就可以避免库存卖超的问题.分布式锁使分布式系统之间同步访问共享资源的一种方式 基于redis实现分布式锁 ...

  5. 基于redis的分布式锁

    <?php /** * 基于redis的分布式锁 * * 参考开源代码: * http://nleach.com/post/31299575840/redis-mutex-in-php * * ...

  6. 基于Redis的分布式锁真的安全吗?

    说明: 我前段时间写了一篇用consul实现分布式锁,感觉理解的也不是很好,直到我看到了这2篇写分布式锁的讨论,真的是很佩服作者严谨的态度, 把这种分布式锁研究的这么透彻,作者这种技术态度真的值得我好 ...

  7. 基于 Redis 的分布式锁

    前言 分布式锁在分布式应用中应用广泛,想要搞懂一个新事物首先得了解它的由来,这样才能更加的理解甚至可以举一反三. 首先谈到分布式锁自然也就联想到分布式应用. 在我们将应用拆分为分布式应用之前的单机系统 ...

  8. 基于 Zookeeper 的分布式锁实现

    1. 背景 最近在学习 Zookeeper,在刚开始接触 Zookeeper 的时候,完全不知道 Zookeeper 有什么用.且很多资料都是将 Zookeeper 描述成一个“类 Unix/Linu ...

  9. 基于redis的分布式锁(转)

    基于redis的分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...

随机推荐

  1. c#死锁示例代码

    void Main() { object obj1 = new object(); object obj2 = new object(); var t1 = new Thread(delegate(o ...

  2. Java多线程学习(总结很详细!!!)

    https://www.cnblogs.com/yjd_hycf_space/p/7526608.html

  3. 使用korofileheader插件vs code添加文件头注释和函数注释

    korofileheadervs code添加文件头注释和函数注释1.extensions搜索fileheader,安装koroFileHeader2.设置:edit=>perference=& ...

  4. Spring Cloud(Dalston.SR5)--Config 集群配置中心-加解密

    实际应用中会涉及很多敏感的数据,这些数据会被加密保存到 SVN 仓库中,最常见的就是数据库密码.Spring Cloud Config 为这类敏感数据提供了加密和解密的功能,加密后的密文在传输给客户端 ...

  5. django template模板 母板 include导入

    一,使用{% block name %}{% endblock %}定义一个模板,在模板页面中它的内容为空,在各页面用{% block name %}自己的标签内容{% endblock %}调用. ...

  6. 在图像中随机更改像素值程序——matlab

    I=imread('C:\Users\wangd\Desktop\result3.png'); % m = rgb2gray(I); % r = unidrnd(,,); %产生一个1*100的数组, ...

  7. Centos7 kernel 内核升级 GPU显卡驱动程序编译安装

    1.NVIDIA官网下载相关显卡驱动 #在服务器上查看网卡型号 lspci -mm | grep NVIDIA   #在NVIDIA官网下载相应型号驱动程序 https://www.geforce.c ...

  8. Mysql中判断一个点是否落在多边形内

    关于地理空间数据,经常需要处理两个空间数据的关联关系.有很多种方法可以处理,通过编写程序算法,或者是调用数据库中对应的function.在mysql数据库中,https://dev.mysql.com ...

  9. windows 命令相关

    脚本执行后的返回值:echo %errorlevel%

  10. Apache Atlas元数据管理从入门到实战(1)

    一.前言   元数据管理是数据治理非常重要的一个方向,元数据的一致性,可追溯性,是实现数据治理非常重要的一个环节.传统数据情况下,有过多种相对成熟的元数据管理工具,而大数据时代,基于hadoop,最为 ...