微服务架构上篇:5. 基于etcd实现分布式锁

简介: 微服务架构上篇:5. 基于etcd实现分布式锁

1. 前言



通过 etcd 实现分布式锁,同样需要满足一致性互斥性可靠性等要求。etcd 中的事务 txnlease 租约以及 watch 监听特性,能够使得基于 etcd 实现上述要求的分布式锁


2. 思路分析



2.1 正常获取锁(etcd的事务IF-Then-Else)


通过 etcd 的事务特性可以帮助我们实现一致性和互斥性。etcd 的事务特性,使用的 IF-Then-Else 语句,IF 语言判断 etcd 服务端是否存在指定的 key,即该 key 创建版本号 create_revision 是否为 0 来检查 key 是否已存在,因为该 key 已存在的话,它的 create_revision 版本号就不是 0。满足 IF 条件的情况下则使用 then 执行 put 操作,否则 else 语句返回抢锁失败的结果。当然,除了使用 key 是否创建成功作为 IF 的判断依据,还可以创建前缀相同的 key,比较这些 key 的 revision 来判断分布式锁应该属于哪个请求。


2.2 获取锁异常


客户端请求在获取到分布式锁之后,如果发生异常,需要及时将锁给释放掉。因此需要租约,当我们申请分布式锁的时候需要指定租约时间。超过 lease 租期时间将会自动释放锁,保证了业务的可用性。是不是这样就够了呢?在执行业务逻辑时,如果客户端发起的是一个耗时的操作,操作未完成的请情况下,租约时间过期,导致其他请求获取到分布式锁,造成不一致。这种情况下则需要续租,即刷新租约,使得客户端能够和 etcd 服务端保持心跳。


3. 实现分布式锁的流程图



我们基于如上分析的思路,绘制出实现 etcd 分布式锁的流程图,如下所示:

640.png


4. 代码实现



package main
import (
 "context"
 "fmt"
 "github.com/coreos/etcd/clientv3"
 "time"
)
func main() {
 // 客户端配置
 config := clientv3.Config{
  Endpoints:   []string{"localhost:2379"},
  DialTimeout: 5 * time.Second,
 }
 var client *clientv3.Client
 var err error
 // 建立连接
 if client, err = clientv3.New(config); err != nil {
  fmt.Println(err)
  return
 }
 // 1. 上锁并创建租约
 lease := clientv3.NewLease(client)
 var leaseGrantResp *clientv3.LeaseGrantResponse
 if leaseGrantResp, err = lease.Grant(context.TODO(), 5); err != nil {
  panic(err)
 }
 leaseId := leaseGrantResp.ID
 // 2 自动续约
 // 创建一个可取消的租约,主要是为了退出的时候能够释放
 ctx, cancelFunc := context.WithCancel(context.TODO())
 // 3. 释放租约
 defer cancelFunc()
 defer lease.Revoke(context.TODO(), leaseId)
 if keepRespChan, err := lease.KeepAlive(ctx, leaseId); err != nil {
  panic(err)
 } else {
  // 续约应答
  go func() {
   for {
    select {
    case keepResp := <-keepRespChan:
     if keepRespChan == nil {
      fmt.Println("租约已经失效了")
      goto END
     } else { // 每秒会续租一次, 所以就会受到一次应答
      fmt.Println("收到自动续租应答:", keepResp.ID)
     }
    }
   }
  END:
  }()
 }
 // 1.3 在租约时间内去抢锁(etcd 里面的锁就是一个 key)
 kv := clientv3.NewKV(client)
 // 创建事务
 txn := kv.Txn(context.TODO())
 //if 不存在 key,then 设置它,else 抢锁失败
 txn.If(clientv3.Compare(clientv3.CreateRevision("lock"), "=", 0)).
  Then(clientv3.OpPut("lock", "g", clientv3.WithLease(leaseId))).
  Else(clientv3.OpGet("lock"))
 // 提交事务
 if txnResp, err := txn.Commit(); err != nil {
  panic(err)
 } else {
  if !txnResp.Succeeded {
   fmt.Println("锁被占用:", string(txnResp.Responses[0].GetResponseRange().Kvs[0].Value))
   return
  }
  // 抢到锁后执行业务逻辑,没有抢到退出
  fmt.Println("处理任务")
  time.Sleep(5 * time.Second)
 }
}


预期的执行结果如下所示:

收到自动续租应答: 6825622810871743294
处理任务
收到自动续租应答: 6825622810871743294
收到自动续租应答: 6825622810871743294
Process finished with exit code 0


总得来说,如上关于 etcd 分布式锁的实现过程分为四个步骤:

  • 客户端初始化与建立连接;
  • 创建租约,自动续租;
  • 创建事务,获取锁;
  • 执行业务逻辑,最后释放锁。


创建租约的时候,需要创建一个可取消的租约,主要是为了退出的时候能够释放。释放锁对应的步骤,在上面的 defer 语句中。当 defer 租约关掉的时候,分布式锁对应的 key 就会被释放掉了。


5. 小结



本文主要介绍了基于 etcd 实现分布式锁的案例。首先介绍了分布式锁产生的背景以及必要性,分布式架构不同于单体架构,涉及到多服务之间多个实例的调用,跨进程的情况下使用编程语言自带的并发原语没有办法实现数据的一致性,因此分布式锁出现,用来解决分布式环境中的资源互斥操作。


接着本文重点介绍了基于 etcd 实现分布式锁的方案,根据 etcd 的特点,利用事务 txn、lease 租约以及 watch 监测实现分布式锁。


在我们上面的案例中,抢锁失败,客户端就直接返回了。那么当该锁被释放之后,或者持有锁的客户端出现了故障退出了,其他锁如何快速获取锁呢?所以上述代码可以基于 watch 监测特性进行改进,各位同学可以自行试试。(可以参考:https://github.com/zieckey/etcdsync)

相关文章
|
29天前
|
弹性计算 API 持续交付
后端服务架构的微服务化转型
本文旨在探讨后端服务从单体架构向微服务架构转型的过程,分析微服务架构的优势和面临的挑战。文章首先介绍单体架构的局限性,然后详细阐述微服务架构的核心概念及其在现代软件开发中的应用。通过对比两种架构,指出微服务化转型的必要性和实施策略。最后,讨论了微服务架构实施过程中可能遇到的问题及解决方案。
|
29天前
|
Java 开发者 微服务
从单体到微服务:如何借助 Spring Cloud 实现架构转型
**Spring Cloud** 是一套基于 Spring 框架的**微服务架构解决方案**,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。
157 69
从单体到微服务:如何借助 Spring Cloud 实现架构转型
|
3天前
|
存储 Prometheus Cloud Native
分布式系统架构6:链路追踪
本文深入探讨了分布式系统中的链路追踪理论,涵盖追踪与跨度的概念、追踪系统的模块划分及数据收集的三种方式。链路追踪旨在解决复杂分布式系统中请求流转路径不清晰的问题,帮助快速定位故障和性能瓶颈。文中介绍了基于日志、服务探针和边车代理的数据收集方法,并简述了OpenTracing、OpenCensus和OpenTelemetry等链路追踪协议的发展历程及其特点。通过理解这些概念,可以更好地掌握开源链路追踪框架的使用。
54 41
|
28天前
|
运维 监控 持续交付
微服务架构解析:跨越传统架构的技术革命
微服务架构(Microservices Architecture)是一种软件架构风格,它将一个大型的单体应用拆分为多个小而独立的服务,每个服务都可以独立开发、部署和扩展。
171 36
微服务架构解析:跨越传统架构的技术革命
|
1天前
|
Java 关系型数据库 数据库
微服务SpringCloud分布式事务之Seata
SpringCloud+SpringCloudAlibaba的Seata实现分布式事务,步骤超详细,附带视频教程
14 1
|
13天前
|
设计模式 存储 算法
分布式系统架构5:限流设计模式
本文是小卷关于分布式系统架构学习的第5篇,重点介绍限流器及4种常见的限流设计模式:流量计数器、滑动窗口、漏桶和令牌桶。限流旨在保护系统免受超额流量冲击,确保资源合理分配。流量计数器简单但存在边界问题;滑动窗口更精细地控制流量;漏桶平滑流量但配置复杂;令牌桶允许突发流量。此外,还简要介绍了分布式限流的概念及实现方式,强调了限流的代价与收益权衡。
57 11
|
15天前
|
设计模式 监控 Java
分布式系统架构4:容错设计模式
这是小卷对分布式系统架构学习的第4篇文章,重点介绍了三种常见的容错设计模式:断路器模式、舱壁隔离模式和重试模式。断路器模式防止服务故障蔓延,舱壁隔离模式通过资源隔离避免全局影响,重试模式提升短期故障下的调用成功率。文章还对比了这些模式的优缺点及适用场景,并解释了服务熔断与服务降级的区别。尽管技术文章阅读量不高,但小卷坚持每日更新以促进个人成长。
43 11
|
16天前
|
消息中间件 存储 安全
分布式系统架构3:服务容错
分布式系统因其复杂性,故障几乎是必然的。那么如何让系统在不可避免的故障中依然保持稳定?本文详细介绍了分布式架构中7种核心的服务容错策略,包括故障转移、快速失败、安全失败等,以及它们在实际业务场景中的应用。无论是支付场景的快速失败,还是日志采集的安全失败,每种策略都有自己的适用领域和优缺点。此外,文章还为技术面试提供了解题思路,助你在关键时刻脱颖而出。掌握这些策略,不仅能提升系统健壮性,还能让你的技术栈更上一层楼!快来深入学习,走向架构师之路吧!
52 11
|
1月前
|
设计模式 负载均衡 监控
探索微服务架构下的API网关设计
在微服务的大潮中,API网关如同一座桥梁,连接着服务的提供者与消费者。本文将深入探讨API网关的核心功能、设计原则及实现策略,旨在为读者揭示如何构建一个高效、可靠的API网关。通过分析API网关在微服务架构中的作用和挑战,我们将了解到,一个优秀的API网关不仅要处理服务路由、负载均衡、认证授权等基础问题,还需考虑如何提升系统的可扩展性、安全性和可维护性。文章最后将提供实用的代码示例,帮助读者更好地理解和应用API网关的设计概念。
64 8
|
26天前
|
存储 算法 安全
分布式系统架构1:共识算法Paxos
本文介绍了分布式系统中实现数据一致性的重要算法——Paxos及其改进版Multi Paxos。Paxos算法由Leslie Lamport提出,旨在解决分布式环境下的共识问题,通过提案节点、决策节点和记录节点的协作,确保数据在多台机器间的一致性和可用性。Multi Paxos通过引入主节点选举机制,优化了基本Paxos的效率,减少了网络通信次数,提高了系统的性能和可靠性。文中还简要讨论了数据复制的安全性和一致性保障措施。
36 1

热门文章

最新文章