golang基于etcd实现分布式锁(转)
下面描述使用 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实现分布式锁(转)的更多相关文章
- 一次基于etcd的分布式锁自动延时失败问题的排查
今天在测试基于etcd的分布式锁过程中,在测试获取锁后,释放之前超出TTL时长的情况下自动延长TTL这部分功能,在延长指定key的TTL时总是返回404错误信息,在对目标KEY更新TTL时目标KEY已 ...
- golang使用etcd实现分布式锁
package main import ( "context" "fmt" "time" "go.etcd.io/etcd/cli ...
- 基于 Redis 实现分布式锁
1.主流分布式锁实现方案 基于数据库实现分布式锁 基于缓存(redis 等) 基于 Zookeeper 2.根据实现方式分类 类 CAS 自旋式分布式锁:询问的方式,类似 java 并发编程中的线程获 ...
- 基于redis 实现分布式锁的方案
在电商项目中,经常有秒杀这样的活动促销,在并发访问下,很容易出现上述问题.如果在库存操作上,加锁就可以避免库存卖超的问题.分布式锁使分布式系统之间同步访问共享资源的一种方式 基于redis实现分布式锁 ...
- 基于redis的分布式锁
<?php /** * 基于redis的分布式锁 * * 参考开源代码: * http://nleach.com/post/31299575840/redis-mutex-in-php * * ...
- 基于Redis的分布式锁真的安全吗?
说明: 我前段时间写了一篇用consul实现分布式锁,感觉理解的也不是很好,直到我看到了这2篇写分布式锁的讨论,真的是很佩服作者严谨的态度, 把这种分布式锁研究的这么透彻,作者这种技术态度真的值得我好 ...
- 基于 Redis 的分布式锁
前言 分布式锁在分布式应用中应用广泛,想要搞懂一个新事物首先得了解它的由来,这样才能更加的理解甚至可以举一反三. 首先谈到分布式锁自然也就联想到分布式应用. 在我们将应用拆分为分布式应用之前的单机系统 ...
- 基于 Zookeeper 的分布式锁实现
1. 背景 最近在学习 Zookeeper,在刚开始接触 Zookeeper 的时候,完全不知道 Zookeeper 有什么用.且很多资料都是将 Zookeeper 描述成一个“类 Unix/Linu ...
- 基于redis的分布式锁(转)
基于redis的分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...
随机推荐
- C# 判断文件编码
无耐网上各种方法都有缺陷,此方法为原创,暂问发现问题.如发现请指正 public static Encoding GetFileEncodingByContent(string path) { var ...
- intval — 获取变量的整数值
echo intval ( '-42' ); // -42
- python selenium TouchAction模拟移动端触摸操作(十八)
最近做移动端H5页面的自动化测试时候,需要模拟一些上拉,下滑的操作,最初考虑使用使用selenium ActionChains来模拟操作,但是ActionChains 只是针对PC端程序鼠标模拟的一系 ...
- jquery: 获取当前天加减一天
// 对Date的扩展,将 Date 转化为指定格式的String // 月(M).日(d).小时(h).分(m).秒(s).季度(q) 可以用 1-2 个占位符, // 年(y)可以用 1-4 个占 ...
- EasyTouch和NGUI的使用心得
今天来写一写Unity3D中两个比较常用插件:EasyTouch和NGUI的学习心得.我用的版本分别是EasyTouch 3.1.1和NGUI 3.6.0,下面也是对这两个版本的学习心得. 1. Ea ...
- ZoomIt: 非PPT演示必备辅助软件
下载地址:https://docs.microsoft.com/zh-cn/sysinternals/downloads/zoomit 使用方法: 另一个免费的屏幕标注工具:Pointofix v1. ...
- 【C++】static关键字的总结
转自 https://www.cnblogs.com/BeyondAnyTime/p/2542315.html C++的static有两种用法:面向过程程序设计中的static和面向对象程序设计中的s ...
- VS2015Git 源代码工具使用
1. 首先到源代码托管平台申请个账户:https://git.oschina.net/ 2.创建流程图: 2.1 开始创建项目: 2.2 3. 4. 作者:江宁织造 qq空间:苦心孤诣博客:http: ...
- azkaban执行错误 Job failed, Cannot request memory (Xms 0 kb, Xmx 0 kb) from system for job
azkaban默认需要3G的内存,剩余内存不足则会报异常. 配置文件,设置 azkaban-web-server-2.7.0/plugins/jobtypes/commonprivate.proper ...
- catkin-make: command not found 错误解决
参考网址:https://answers.ros.org/question/212492/catkin_make-command-not-found/ zc@ubuntu:~ $ source /op ...