etcd的分布式锁

在分布式系统中,通常需要使用分布式锁解决跨网络的多线程的共享资源竞争问题。
使用etcd实现分布式锁可以确保确保了锁的安全性、互斥性,同时可以通过Lease确保持有锁的client的活性,当client发送宕机或者网络分区的时候,不会出现死锁。并且基于Raft保证了数据的强一致性,不会因为etcd实例宕机出现锁数据丢失的问题。
这里介绍一下clientv3提供的concurrency包里的分布式锁的实现原理和使用示例。

concurrency分布式锁源码介绍

首先concurrency.NewSession方法创建Session对象,本质是创建了一份租约,可以通过续租保证锁的安全性,当持有锁的client宕机不能主动释放锁的时候,锁会因为租约到期而自动释放,避免产生死锁。

type Session struct {client *v3.Clientopts   *sessionOptionsid     v3.LeaseIDcancel context.CancelFuncdonec  <-chan struct{}
}

使用Session对象通过concurrency.NewMutex 创建了一个Mutex对象,这个对象是分布式锁的核心逻辑,咱们通过源码分析下

type Mutex struct {s *Sessionpfx   string //前缀,实际上就是加锁的key + "/"myKey string //在etcd中真实的key,就是pfx/LeaseID的格式myRev int64 //创建key的Revisionhdr   *pb.ResponseHeader
}func (m *Mutex) Lock(ctx context.Context) error {s := m.sclient := m.s.Client()//根据要加锁资源的key和租约ID为这个锁创建一个myKeym.myKey = fmt.Sprintf("%s%x", m.pfx, s.Lease())//使用事务,如果myKey的CreateRevision=0,则代表key不存在,就创建一个,如果存在就获取myKey的信息和同样prefix中Revision最小的key,//同样prefix代表锁的是同一个资源。cmp := v3.Compare(v3.CreateRevision(m.myKey), "=", 0)put := v3.OpPut(m.myKey, "", v3.WithLease(s.Lease()))get := v3.OpGet(m.myKey)getOwner := v3.OpGet(m.pfx, v3.WithFirstCreate()...)resp, err := client.Txn(ctx).If(cmp).Then(put, getOwner).Else(get, getOwner).Commit()if err != nil {return err}//如果myKey已经存在则代表是同一个租约再次请求锁,实现了可重入锁m.myRev = resp.Header.Revisionif !resp.Succeeded {m.myRev = resp.Responses[0].GetResponseRange().Kvs[0].CreateRevision}//如果myKey的Revision是同样prefix的key中最小的,就直接获取到锁。ownerKey := resp.Responses[1].GetResponseRange().Kvsif len(ownerKey) == 0 || ownerKey[0].CreateRevision == m.myRev {m.hdr = resp.Headerreturn nil}//监听等待比自己Revision小的key释放锁hdr, werr := waitDeletes(ctx, client, m.pfx, m.myRev-1)//如果等待失败,释放锁键if werr != nil {m.Unlock(client.Ctx())} else {m.hdr = hdr}return werr
}

分布式锁的demo

下面是ectd分布式锁的应用,通过两个goroutine模拟两个client同时请求锁,可以看到后面的请求是阻塞的,直到持有锁的client释放。


package mainimport ("log""time""github.com/coreos/etcd/clientv3""github.com/coreos/etcd/clientv3/concurrency""context""fmt"
)func main(){cli, err := clientv3.New(clientv3.Config{Endpoints:   []string{"127.0.0.1:2379"},DialTimeout: time.Second * 5,})if err != nil {log.Fatal(err)}defer cli.Close()s1, err := concurrency.NewSession(cli, concurrency.WithTTL(5))if err != nil {log.Fatal(err)}defer s1.Close()//会话1上锁成功,然后开启goroutine去新建一个会话去上锁,5秒钟后会话1解锁。m1 := concurrency.NewMutex(s1, "mylock")if err := m1.Lock(context.TODO()); err != nil {log.Fatal(err)}fmt.Printf("session1 上锁成功。 time:%d \n",time.Now().Unix())g2 := make(chan struct{})go func() {defer close(g2)s2, err := concurrency.NewSession(cli, concurrency.WithTTL(5))if err != nil {log.Fatal(err)}defer s2.Close()m2 := concurrency.NewMutex(s2, "mylock")if err := m2.Lock(context.TODO()); err != nil {log.Fatal(err)}fmt.Printf("session2 上锁成功。 time:%d \n",time.Now().Unix())if err := m2.Unlock(context.TODO()); err != nil {log.Fatal(err)}fmt.Printf("session2 解锁。 time:%d \n",time.Now().Unix())}()time.Sleep(5 * time.Second)if err := m1.Unlock(context.TODO()); err != nil {log.Fatal(err)}fmt.Printf("session1 解锁。 time:%d \n",time.Now().Unix())<- g2
}/**
结果输出:
session1 上锁成功。 time:1641454818
session1 解锁。 time:1641454823
session2 上锁成功。 time:1641454823
session2 解锁。 time:1641454823
*/

go etcd分布式锁的实现和使用相关推荐

  1. 用java实现etcd分布式锁_etcd分布式锁及事务

    前言 分布式锁是控制分布式系统之间同步访问共享资源的一种方式.在分布式系统中,常常需要协调他们的动作.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互 ...

  2. 用java实现etcd分布式锁_etcdsync 一个golang的ectd分布式锁实现

    etcdsync 介绍 etcdsync is a distributed lock library in Go using etcd. It easy to use like sync.Mutex. ...

  3. Java开发高级工程师面试,etcd:一款比Redis更骚的分布式锁的实现方式

    Watch 机制支持 Watch 某个固定的 key,也支持 Watch 一个范围(前缀机制). 当被 Watch 的 key 或范围发生变化,客户端将收到通知:在实现分布式锁时,如果抢锁失败, 可通 ...

  4. 分布式为什么一定要有高可用的分布式锁?一线大厂必看!

    " 现在面试都会聊到分布式系统,其中不免谈到分布式锁这块的知识,今天就来聊聊如何设计高可用的分布式锁. 作者:陈于喆,来自:51CTO技术栈 分布式锁定义 分布式锁在分布式环境下,锁定全局唯 ...

  5. 分布式锁选型背后的架构设计思维【附源码】

    1. 分布式锁本质 提到分布式锁,有很多实现,比如Redis分布式锁.ZooKeeper分布式锁.etcd分布式锁等.但是选择哪个更适合你的项目?在<基于CAP模型设计企业级真正高可用的分布式锁 ...

  6. 还不会使用分布式锁?教你三种分布式锁实现的方式

    摘要:在单进程的系统中,当存在多个线程可以同时改变某个变量时,就需要对变量或代码块做同步,使其在修改这种变量时能够线性执行消除并发修改变量,而同步本质上通过锁来实现. 本文分享自华为云社区<还不 ...

  7. etcd 笔记(08)— 基于 etcd 实现分布式锁

    1. 为什么需要分布式锁? 在分布式环境下,数据一致性问题一直是个难点.分布式与单机环境最大的不同在于它不是多线程而是多进程.由于多线程可以共享堆内存,因此可以简单地采取内存作为标记存储位置.而多进程 ...

  8. go 调用etcd实现分布式锁

    package workerimport ("context""fmt""go.etcd.io/etcd/clientv3""ti ...

  9. Zookeeper,etcd,consul内部机制和分布式锁和选主实现的比较

    我的另外3篇文章分别介绍了Zookeeper,etcd,consul是如何实现分布式锁和选主的.本文想比较一下Zookeeper.etcd.consul内部机制有哪些不同,他们实现锁和选主的方式相同和 ...

最新文章

  1. 聚集索引和非聚集索引- -
  2. Log4j2异步日志背后的数字
  3. Java中的volatile关键字
  4. 【Python】zip函数的使用
  5. 文件设置索引_什么样的网站结构备受搜索引擎喜爱?
  6. 2021高通人工智能应用创新大赛踩坑指南
  7. java泛型函数类型推断_为什么javac可以推断用作参数的函数的泛型类型参数?
  8. 还在纠结是否入手M1 macbook?看完这篇文章再做决定也不迟!
  9. 演化博弈与GAN网络
  10. 【二分法】凸多边形外接圆的半径
  11. flash debug版本
  12. Linux /usr、/usr/share、/etc介绍
  13. 华为服务器不显示u盘启动项,服务器不读u盘启动
  14. 想要写好文案,就要学习这八种动物
  15. CodeTON Round 1 (Div. 1 + Div. 2, Rated, Prizes)
  16. Zigbee协调器主动使终端节点退网
  17. 新一年TurboGate邮件网关再次提醒小心勒索邮件
  18. GoldWave教程分享:混音怎么处理
  19. sourceInsight4.0 安装-pojie
  20. android局域网通信方案,Android基于TCP的局域网聊天通信

热门文章

  1. GPS实时定位、获取基站信息
  2. CCNP基础知识-路由(二)
  3. eclipse连接小米2调试程序的问题.
  4. stream各种类区别
  5. 什么是基于模型设计(MBD)?
  6. 项目总结:vue.js2.5饿了么APP(1)概述+项目准备
  7. bootstrap栅格系统布局原理
  8. 移动端选择插件mobiscroll的使用demo
  9. vue中获取组件的位置
  10. 8个炫酷又好用的 Python 工具界面编程不再复杂,Python工具PyCharm使用技巧