首页 > 其他分享 >使用etcd来实现一个简单的分布式锁

使用etcd来实现一个简单的分布式锁

时间:2024-03-24 15:48:58浏览次数:21  
标签:context EtcdLock return etcd key 简单 分布式

使用etcd来实现一个简单的分布式锁

使用etcd来实现一个简单的分布式锁

分布式锁有着极为广泛的使用, 在多节点服务部署中是必不可少的一环.

在本文中, 我们尝试以etcd为基础来实现一个简单的分布式锁.

基本能力

  1. Lock 上锁
  2. Unlock 解锁
  3. 一些额外的设置, 比如Watch-Dog模式/ 设置最长运行时间等等

设计方案

如果获取当前的锁的状态?

我们都知道, 在redis中可以通过SetNx Incr来实现这个方式

归根到底, 判断加锁是否成功只需要确认一点, 那就是锁的首次且唯一性, 我们使用SetNx是通过保证只有第一个操作能得到1来保证的. Incr是通过保证1是首次出现且之后不会在出现来保证的.

那么在etcd这个kv存储中, 有什么是首次唯一的呢?

答案是 key的revision.

我们可以通过键值对的revision来判断这个某个锁是否被创建以及被释放.

一个使用到的知识是: 未被创建的key的revision是 0

被创建到的key的revision, 必不为0

// 代码表述为
if revision != 0{
    // 拿锁失败
    return false
}else{
    // set key
    try:
        lock = 1
        return true
    expect:
        return false
}

实现

一个十分粗糙的实现版本, 仅供参考

package main

import (
  "context"
  "time"

  clientv3 "go.etcd.io/etcd/client/v3"
)

const commonPrefix = "/_locks/"

type EtcdLock struct {
  client  *clientv3.Client
  key     string
  leaseId clientv3.LeaseID
  ctx     context.Context

  innerCtx       context.Context
  innerCtxCancel context.CancelFunc
}

func NewEtcdLock(c *clientv3.Client) *EtcdLock {
  return &EtcdLock{
    client: c,
  }
}

func (e *EtcdLock) WithContext(ctx context.Context) *EtcdLock {
  e.ctx = ctx
  return e
}

func (e *EtcdLock) WithKey(key string) *EtcdLock {
  e.key = key
  return e
}

func (e *EtcdLock) WatchDog() {
  // 定时器
  t := time.NewTimer(4 * time.Second)
  defer t.Stop()

  for {
    select {
    case <-e.innerCtx.Done():
      return
    case <-t.C:
      // 5s refresh lease
      _, err := e.client.KeepAliveOnce(e.ctx, e.leaseId)
      if err != nil {
        return
      }
    }
  }
}

func (e *EtcdLock) Lock() bool {
  grant, err := e.client.Grant(e.ctx, 5)
  if err != nil {
    return false
  }

  e.leaseId = grant.ID
  // 1. get key revision
  cmp := clientv3.Compare(clientv3.CreateRevision(commonPrefix+e.key), "=", 0)
  // 2. create key
  put := clientv3.OpPut(commonPrefix+e.key, "locked", clientv3.WithLease(e.leaseId))
  // 3. transaction
  resp, err := e.client.Txn(e.ctx).If(cmp).Then(put).Commit()
  if err != nil {
    return false
  }
  if !resp.Succeeded {
    return false
  }

  e.innerCtx, e.innerCtxCancel = context.WithCancel(e.ctx)
  go e.WatchDog()

  return true
}

func (e *EtcdLock) Unlock() error {
  // just delete lease
  _, err := e.client.Revoke(e.ctx, e.leaseId)
  e.innerCtxCancel()
  if err != nil {
    return err
  }
  return nil
}

标签:context,EtcdLock,return,etcd,key,简单,分布式
From: https://www.cnblogs.com/pDJJq/p/18092485/use-etcd-to-achieve-a-simple-distributed-lock-z11

相关文章

  • etcd 以及 redis分布式锁的实现优劣比较
    etcd以及redis分布式锁的实现优劣比较背景介绍在学习etcd时,对于使用etcd实现分布式锁(使用etcd来实现一个简单的分布式锁)做了一个简单的示例,同时也能想到和Redis实现的分布式锁相比,基于etcd来做有什么好处呢?技术要点底层技术比较我们必须要明白一件事情,两者的底......
  • 使用etcd来实现一个简单的分布式锁
    使用etcd来实现一个简单的分布式锁使用etcd来实现一个简单的分布式锁分布式锁有着极为广泛的使用,在多节点服务部署中是必不可少的一环.在本文中,我们尝试以etcd为基础来实现一个简单的分布式锁.基本能力Lock上锁Unlock解锁一些额外的设置,比如Watch-Dog模式/设置最......
  • etcd:基本使用
    基本使用基本使用建立连接//initetcdclientclient,err:=clientv3.New(clientv3.Config{Endpoints:[]string{"http://localhost:2379"},DialTimeout:5*time.Second,},)这里需要注意Endpoints,传入的是一个list这样做的原因很简单......
  • raft算法和etcd代码解析-3.网络分区问题及其它
    网络分区问题网络分区导致选举永远无法达成共识,选举不断超时,任期号将不断增加为避免这个问题,candidate会探测网络环境以免发起无意义的竞选集群变更leader收到配置变更要求,会广播配置变更日志,日志包括新结点和老节点,在收到老节点的多数派认可后,leader后提交该请求在处理配置......
  • 大数据分布式事务的深入理解?
        在一个大数据系统内部分布式事务无处不在,但凡一个任务分布到多台机器上执行就会涉及到分布式事务的场景,分布式事务一直以来都是分布式系统比较难以解决的问题。    事务的理解,比如你要将账户A转1块钱到账户B中,那么这个行为在执行时会被拆分成两个步骤,第一......
  • Linux操作系统的简单终端(Terminal)命令
    ls:列出当前目录下的文件和文件夹。你可以使用ls-l来查看详细信息,或者使用ls-a来显示包括隐藏文件在内的所有文件。cd:用于切换目录。例如,cdDocuments会进入Documents文件夹。你还可以使用cd..来返回上一级目录,或者使用cd~来回到你的主目录。pwd:显示当前所在的目录路径。chm......
  • 最快的 Python API 框架之一:简单、现代、高性能 | 开源日报 No.207
    tiangolo/fastapiStars:68.1kLicense:MITfastapi是一个现代、高性能、易学习、快速编码且适用于生产环境的框架。其主要功能和核心优势包括:高性能:与NodeJS和Go相当,是最快的Python框架之一。编码速度快:开发特性的速度提高约200%到300%。减少错误:减少大约......
  • HTML5抽奖转盘-CSS3超简单版本
     网上有很多关于抽奖转盘的代码和实例,有使用JavaScript控制动画的,有使用Canvas实现的,它们思路各不相同,本文将介绍一种采用CSS3的方式来实现转盘动画效果,非常简单,只需几行代码即可实现。 核心思路采用CSS3的transition(过渡效果),给定需要旋转的元素设置transform的rotate属性......
  • .NET分布式Orleans - 2 - Grain的通信原理与定义
    Grain是Orleans框架中的基本单元,代表了应用程序中的一个实体或者一个计算单元。每个Silo都是一个独立的进程,Silo负责加载、管理和执行Grain实例,并处理来自客户端的请求以及与其他Silo之间的通信。通信原理在相同的Silo中,Grain与Grain之间的通信通过直接的方法调用实现。每个......
  • Android studio 简单入门程序
    1.创建一个新的AndroidStudio项目。2.在activity_main.xml布局文件中添加一个TextView控件,用于显示文本。3.在对应的Activity类(例如MainActivity.java)中,获取TextView控件并设置文本内容。以下是示例代码:<!--activity_main.xml--><TextView  android:......