首页 > 编程语言 >ETCD源码阅读(四)

ETCD源码阅读(四)

时间:2023-04-02 15:45:58浏览次数:52  
标签:ETCD lock storage client 源码 阅读 租约 分布式

DAY3 :ETCD分布式锁: etcd/contrib/lock

这一部分代码主要是为了展示ETCD实现分布式锁的原理(Lease),并且贴出了 DDIA作者的一篇博文作为应用场景建模。那么我们就先来读这篇博文吧。

为什么要使用分布式锁

  1. 防止数据竞争:多个分布式下节点可能会同时修改同一份数据,如果不加锁,会导致数据出现错误或不一致的情况。

  2. 避免重复操作:某些操作只需要在分布式系统中执行一次,如果不使用分布式锁来控制并发访问,就有可能导致操作被重复执行,导致资源浪费甚至严重后果。

  3. 保证顺序访问:某些操作必须按照特定的顺序执行,例如在订单系统中,生成订单和扣款必须按照先后顺序执行。使用分布式锁可以确保这些操作的顺序性。

使用分布式锁来保护资源

设想这样一个场景:某一个应用需要修改一个HDFS中的文件,那么某一个client就会试着去获得一个锁;然后读取文件,将文件进行修改后上传回HDFS;最后释放锁。

下面便是一个示例代码。

    func writeData(filePath string) error {
        lock, err := lockService.acquireLock()
        if err != nil {
            // ...
            return err
        }
        defer lock.release()

        file, _ := storage.readFile(filePath)
        // update file
        // ...
        storage.writeFile(filePath, file) 
        return nil
    }

但实际上,这样的设计并不能正常工作,下面这张图展示了一种出错场景。
Alt text
假设client获取锁后,由于GC等问题,产生了锁过期(锁对应着一个租约)的情况(这是一个必要的设计,避免client永久性获得一个锁),而client并不知道锁已经过期。这种情况下,多个client会产生数据竞争,搞乱数据。这样的bug在老版本的Hbase中存在过。

在往HDFS写数据前再检查一次锁过期情况可以吗?不行,因为GC随时有可能发生,检查锁状态与回写数据这个两个操作不可能是原子的。

解决方案:fencing token( version number validation)

fencing token是一个单调递增的数字,当client成功获取锁的时候,将这个token与锁一起返回给client。而client访问共享资源的时候,需要带着这个fencing token
Alt text
至此,关于这篇博文的内容已经结束,我们已经学习到了一种分布式锁的设计方法。值得一提的是,在这篇博文中,作者对于Redis的Redlock机制提出的一些批判,Redlock的发明者也针对这些批判发起了回应,参见这篇文章

ETCD的分布式锁设计

ETCD在很多场景下被用作一个分布式协调服务,ETCD提供了event watch、lease、选主、共享分布式锁(并不是严格的分布式锁,持有锁的用户并不拥有资源的独占访问)等机制。

那么如何使用ETCD实现一个分布式锁呢?ETCD的文档种给出了详细介绍以及注意事项Notes on the usage of lock and lease

ETCD提供了一个基于租约机制(lease)的lock API:服务器发放一个租约给客户端,并且给这个租约设置TTL,当服务器检查到TTL过期后,就会收回这个租约。持有合法租约的客户端,可以访问与这个租约关联的资源(比如ETCD内的某个Key)。但是 ETCD的lock API并不能保障资源的互斥访问 我们还需要在ETCD的lock机制之上再进行一些优化。(既然这个lock API不是一个分布式锁,为什么还叫这个名字呢?ETCD官方解释说是 历史原因

ETCD租约机制最大的问题在于TTL。TTl是通过一个物理时钟来进行定义,客户端与服务端都用自己本地的时钟来监测TTl是否失效,而他们的时钟不一定同步。这就有可能产生“客户端认为自己还持有租约,而服务端已经将租约收回”的场景。甚至会有“服务端认为租约还有1s,但是100ms后网络授时服务器对其时钟进行调整,使得租约立马失效”的场景。

因此,ETCD在租约机制之上,还采用了版本号校验机制来实现分布式锁(在其他的系统种,可能被称作cas,compare and swap)。这是一种乐观锁,并不需要真的对数据进行加锁,只会在数据读取时获得一个版本号,写入时检查版本号是否被更新。在ETCD种,我们执行 PutTxn等操作时,可以验证版本号与租约ID,来作为操作条件,如果无法达成条件,则认为操作失败。

Run the Demo

etcd/contrib/lock目录下包含两个程序:client和storage。 用于演示分布式锁的典型场景,比如租约过期的问题。storage是一个非常简单的内存k-v存储库,通过json提供对外的HTTP访问。client根据ETCD分布式锁提供的协调机制,往ETCD里写数据。

编译client与storage

$ cd client
$ go build
$ cd storage 
$ go build 

启动ETCD集群

# ETCD源码根目录
$ ./build
$ goreman start

启动storage以及两个client

$ ./storage

Alt text
根据上图,我们启动两个client,分别代表client1和client2

# 启动client1,会模拟长时间GC
$ GODEBUG=gcstoptheworld=2 ./client 1
client 1 starts
creted etcd client
acquired lock, version: 1029195466614598192
took 6.771998255s for allocation, took 36.217205ms for GC
emulated stop the world GC, make sure the /lock/* key disappeared and hit any key after executing client 2:
# 启动client2
$ ./client 2
client 2 starts
creted etcd client
acquired lock, version: 4703569812595502727
this is client 2, continuing

如果一切顺利,client2将会成功往storage中写入一个数据,而client1会失败:

resuming client 1
failed to write to storage: error: given version (4703569812595502721) differ from the existing version (4703569812595502727)

标签:ETCD,lock,storage,client,源码,阅读,租约,分布式
From: https://www.cnblogs.com/chnjm/p/17280596.html

相关文章

  • rocketmq-spring : 实战与源码解析一网打尽
    RocketMQ是大家耳熟能详的消息队列,开源项目rocketmq-spring可以帮助开发者在SpringBoot项目中快速整合RocketMQ。这篇文章会介绍SpringBoot项目使用rocketmq-springSDK实现消息收发的操作流程,同时笔者会从开发者的角度解读SDK的设计逻辑。1SDK简介项目地址:......
  • rocketmq-spring : 实战与源码解析一网打尽
    RocketMQ是大家耳熟能详的消息队列,开源项目rocketmq-spring可以帮助开发者在SpringBoot项目中快速整合RocketMQ。这篇文章会介绍SpringBoot项目使用rocketmq-springSDK实现消息收发的操作流程,同时笔者会从开发者的角度解读SDK的设计逻辑。1SDK简介项目地址:......
  • clang操作源码
    生成注释假设有下面的源码:structVec3{floatx,y,z;};structVec4{floatx,y,z,w;};生成这样的代码://[[CLASSINFO]]class:Vec3,ispod:true,isaggregate:truestructVec3{floatx,y,z;};//[[CLASSINFO]]usestruct-bindingmethod://c......
  • Linux下编译Sqlite源码
    1.下载wgethttps://www.sqlite.org/2023/sqlite-autoconf-3410200.tar.gz--no-check-certificate2.解压tarzxvfsqlite-autoconf-3410200.tar.gz 3.配置路径cdsqlite-autoconf-3410200/./configure--prefix=/data/sqlite#先建立该路径 4.编译make&&ma......
  • 使用 IntelliJ IDEA 构建 Spring Framework 5.3.21 源码问题解决
    源码版本1、下载地址:https://github.com/spring-projects/spring-framework/tags2、选择要构建的源码版本并下载,例如:5.3.21相关环境1、操作系统:Windows102、JDK版本:Jdk173、IDE工具:IntelliJIDEA2021.3.34、项目构建工具:Gradle7.3.3使用IntelliJIDEA构建Spring......
  • 鸿蒙开发学习笔记-UIAbility-Router页面跳转接口源码分析
    在鸿蒙开发中,UIAbility的跳转使用router方法.在使用的时候需导入importrouterfrom'@ohos.router';该方法接口成员如下:1.interfaceRouterOptionsinterfaceRouterOptions{url:string;//跳转页面的Urlparams?:Object;//传给跳转页面的参数params......
  • java高精度定位系统源码 工厂人员定位系统源码
    这是一套java定位系统源码,工厂人员定位系统源码,UWB高精度定位系统源码,前后端分离架构,源码有演示。工厂人员定位系统,高精度的位置数据作为智能工厂数据流的重要组成部分,可实现对工厂内的人,车、物的精确定位,无缝追踪,智能调配与高效协同,可大幅提升工厂的精益生产及精细化管理水平,我们......
  • 代码大全 阅读笔记03
    复杂数据类型恰当地对数据进行结构化,可以使程序更简单、更容易理解也更容易维护。可以用表来代替复杂的逻辑结构。当你被程序的复杂逻辑迷惑时,应考虑是否可用查寻表来简化程序。抽象数据类型是降低复杂性的有力武器。它使你可以分层编写程序,而且是从问题域,而不是程序语言细节来编......
  • 人月神话阅读笔记01
    由于该书所描述的内容比较庞杂,本人预计将分为三篇文章对于相关内容和感想进行阐述。作为开章第一篇,就先来说说为什么“人月”是“神话”。小学的时候我们都做过这样的应用题:“工厂需要加工一批零件,安排5名工人的话需要10小时完成,那么安排25名工人加工,多少小时可以完成”之类的。......
  • 开源优先队列FastPriorityQueue源码阅读
    FastPriorityQueue  源码连接:https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp  大致结构:  1节点在内存中的结构还是数组,且首节点为无意义节点,有效节点从索引1开始。(见FastPriorityQueue的T[]_nodes)  2维护的节点必须时固定的继承。(见FastPri......