文章目录

  • 什么是分布式锁
  • 分布式死锁
  • 分类
    • 排他锁
    • 共享锁
  • 实现
    • 创建锁
    • 获取锁
    • 释放锁
  • Demo
  • Jmeter配置
  • 方案零 缺陷版本
  • 方案一 非公平锁方案
    • 缺陷 (羊群效应)
  • 方案二 公平锁方案
  • 方案三 共享锁方案


什么是分布式锁

什么是分布式锁,以及分布式锁在日常工作的使用场景。明确了这些,我们才能设计出一个安全稳定的分布式锁。

在日常开发中,我们最熟悉也常用的分布式锁场景是在开发多线程的时候。为了协调本地应用上多个线程对某一资源的访问,就要对该资源或数值变量进行加锁,以保证在多线程环境下系统能够正确地运行。在一台服务器上的程序内部,线程可以通过系统进行线程之间的通信,实现加锁等操作。而在分布式环境下,执行事务的线程存在于不同的网络服务器中,要想实现在分布式网络下的线程协同操作,就要用到分布式锁。


分布式死锁

在单机环境下,多线程之间会产生死锁问题。同样,在分布式系统环境下,也会产生分布式死锁的问题。

当死锁发生时,系统资源会一直被某一个线程占用,从而导致其他线程无法访问到该资源,最终使整个系统的业务处理或运行性能受到影响,严重的甚至可能导致服务器无法对外提供服务。

所以当我们在设计开发分布式系统的时候,要准备一些方案来面对可能会出现的死锁问题,当问题发生时,系统会根据我们预先设计的方案,避免死锁对整个系统的影响。常用的解决死锁问题的方法有超时方法和死锁检测。

超时方法

在解决死锁问题时,超时方法可能是最简单的处理方式了。超时方式是在创建分布式线程的时候,对每个线程都设置一个超时时间。当该线程的超时时间到期后,无论该线程是否执行完毕,都要关闭该线程并释放该线程所占用的系统资源。之后其他线程就可以访问该线程释放的资源,这样就不会造成分布式死锁问题。但是这种设置超时时间的方法也有很多缺点,最主要的就是很难设置一个合适的超时时间。如果时间设置过短,可能造成线程未执行完相关的处理逻辑,就因为超时时间到期就被迫关闭,最终导致程序执行出错。

死锁检测

死锁检测是处理死锁问题的另一种方法,它解决了超时方法的缺陷。与超时方法相比,死锁检测方法主动检测发现线程死锁,在控制死锁问题上更加灵活准确。你可以把死锁检测理解为一个运行在各个服务器系统上的线程或方法,该方法专门用来探索发现应用服务上的线程是否发生了死锁。如果发生死锁,就会触发相应的预设处理方案。


分类

在介绍完分布式锁的基本性质和潜在问题后,接下来我们就通过 ZooKeeper 来实现两种比较常用的分布式锁。

排他锁

排他锁也叫作独占锁,从名字上就可以看出它的实现原理。当我们给某一个数据对象设置了排他锁后,只有具有该锁的事务线程可以访问该条数据对象,直到该条事务主动释放锁。否则,在这期间其他事务不能对该数据对象进行任何操作。


共享锁

另一种分布式锁的类型是共享锁。它在性能上要优于排他锁,这是因为在共享锁的实现中,只对数据对象的写操作加锁,而不为对象的读操作进行加锁。这样既保证了数据对象的完整性,也兼顾了多事务情况下的读取操作。可以说,共享锁是写入排他,而读取操作则没有限制。


实现

接下来就通过 ZooKeeper 来实现一个排他锁。

创建锁

首先,我们通过在 ZooKeeper 服务器上创建数据节点的方式来创建一个共享锁。其实无论是共享锁还是排他锁,在锁的实现方式上都是一样的。唯一的区别在于,共享锁为一个数据事务创建两个数据节点,来区分是写入操作还是读取操作。

在 ZooKeeper 数据模型上的 Locks_shared 节点下创建临时顺序节点,临时顺序节点的名称中带有请求的操作类型分别是 R 读取操作、W 写入操作。

获取锁

当某一个事务在访问共享数据时,首先需要获取锁。ZooKeeper 中的所有客户端会在 Locks_shared 节点下创建一个临时顺序节点。根据对数据对象的操作类型创建不同的数据节点,如果是读操作,就创建名称中带有 R 标志的顺序节点,如果是写入操作就创建带有 W 标志的顺序节点。


释放锁

事务逻辑执行完毕后,需要对事物线程占有的共享锁进行释放。我们可以利用 ZooKeeper 中数据节点的性质来实现主动释放锁和被动释放锁两种方式。

主动释放锁是当客户端的逻辑执行完毕,主动调用 delete 函数删除ZooKeeper 服务上的数据节点。

而被动释放锁则利用临时节点的性质,在客户端因异常而退出时,ZooKeeper 服务端会直接删除该临时节点,即释放该共享锁。

这种实现方式正好和上面介绍的死锁的两种处理方式相对应。到目前为止,我们就利用 ZooKeeper 实现了一个比较完整的共享锁。

如下图所示,在这个实现逻辑中,首先通过创建数据临时数据节点的方式实现获取锁的操作。创建数据节点分为两种,分别是读操作的数据节点和写操作的数据节点。当锁节点删除时,注册了该 Watch 监控的其他客户端也会收到通知,重新发起创建临时节点尝试获取锁。当事务逻辑执行完成,客户端会主动删除该临时节点释放锁。


Demo

NG 负载, 后端两个节点

NG配置如下


Jmeter配置


方案零 缺陷版本

不加锁的情况下,我们来压一下

这是老板打来电话了。。。。。。


方案一 非公平锁方案


缺陷 (羊群效应)

上实现方式在并发问题比较严重的情况下,性能较低,主要原因是,所有的连接都在对同一个节点进行监听,当服务器检测到删除事件时,要通知所有的连接,所有的连接同时收到事件,再次并发竞争,这就是羊群效应。

如何避免呢,我们看下面这种方式


方案二 公平锁方案

上述方案是一个公平锁的实现,通过zk提供的临时顺序节点,可以避免同时多个节点的并发竞争锁,缓解了服务端压力,避免羊群效应。

代码实现,Curator框架提供了InterProcessMutex

https://curator.apache.org/getting-started.html

  @PostMapping("/stock/deductWithLock")public Object deductWithLock(Integer id) throws Exception {InterProcessMutex interProcessMutex = new InterProcessMutex(curatorFramework, "/product_" + id);try {interProcessMutex.acquire();orderService.reduceStock(id);} catch (Exception e) {if (e instanceof RuntimeException) {throw e;}}finally {interProcessMutex.release();}return "visit:" + port;}

方案三 共享锁方案

实现:InterProcessReadWriteLock


https://curator.apache.org/curator-recipes/shared-reentrant-read-write-lock.html

用法都很简单,主要是搞清楚实现原理

行了,到这儿吧先

Apache ZooKeeper - 使用ZK实现分布式锁(非公平锁/公平锁/共享锁 )相关推荐

  1. php zookeeper分布式事务,ZK实现分布式事务锁代码及原理验证

    先复习一下ZK实现分布式锁的原理: 每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点. 判断是否获取锁的方式很简单,只需要判断有序节点中序 ...

  2. 关于Zookeeper和Redis实现分布式锁的异同

    本文来说下Zookeeper和Redis实现分布式锁的异同 文章目录 概述 Redis单机实现分布式锁 Redis加锁 Redis解锁 Redis加锁过期时间设置问题 Zookeeper单机实现分布式 ...

  3. Zookeeper和Redis实现分布式锁,附我的可靠性分析

    作者:今天你敲代码了吗 链接:https://www.jianshu.com/p/b6953745e341 在分布式系统中,为保证同一时间只有一个客户端可以对共享资源进行操作,需要对共享资源加锁来实现 ...

  4. Apache ZooKeeper - 使用Apache Curator操作ZK

    文章目录 原生ZK API VS Curator Curator 概述 Maven依赖 会话创建 静态工厂方式创建会话 使用 fluent 风格创建会话 创建节点 protection 模式 ,规避僵 ...

  5. Apache ZooKeeper - ZK的基本特性与节点应用场景一览

    文章目录 概述 ZK产生的背景 安装 JDK依赖 下载 解压 修改配置文件 启动 / 停止 连接服务器 配置文件说明 ZK 核心概念 (文件系统数据结构+监听通知机制) 文件系统数据结构 6种Node ...

  6. Apache ZooKeeper - 使用源码启动ZK集群模式

    文章目录 Pre 配置总览 端口说明 Node 1 [zoo1.cfg ] [myid] Node 2 [zoo2.cfg ] [myid] Node 3 [zoo3.cfg ] [myid] 启动集 ...

  7. Apache ZooKeeper - 使用原生的API操作ZK

    文章目录 概述 maven依赖 验证 测试基类 ZK构造函数参数 connectString:ZooKeeper服务器列表 sessionTimeout:会话的超时时间, "毫秒" ...

  8. Apache ZooKeeper - ZK的内存数据 + 持久化事务日志 + 数据快照 初探

    文章目录 内存数据 源码实现 事务日志 配置项 查看事务日志数据 LogFormatter 写入日志的优化 (预分配) 数据快照 查看数据快照数据 SnapshotFormatter 事务日志 VS ...

  9. redis实现轮询算法_基于zookeeper或redis实现分布式锁

    前言 在分布式系统中,分布式锁是为了解决多实例之间的同步问题.例如master选举,能够获取分布式锁的就是master,获取失败的就是slave.又或者能够获取锁的实例能够完成特定的操作. 目前比较常 ...

最新文章

  1. 关于 HTML5 的 11 个让人难以接受的事实
  2. 写给笨蛋徒弟的学习手册(1)——完整C#项目中各个文件含义
  3. kubeadm集群修改k8s证书时间到99年
  4. Linux 命令之 sudo -- 以其他用户身份来执行命令
  5. ajax工作中使用模板
  6. 12345组成三个不重复数java,求大神帮忙!五子棋!只能识别按顺序识别!例如 12345 不能...
  7. 如何使用python装饰器_Python学习之如何使用装饰器 @decorator
  8. 高可用性及容灾的几个衡量指标
  9. dz mysql导出shell_mysql数据备份并导入数据库shell脚本
  10. SpringBoot 的属性配置文件
  11. lu分解法matlab_MIT 18.065—机器学习中的矩阵方法02 矩阵乘法与矩阵分解
  12. ASP.NET中WEB上弹出消息框的N种方法(为了以后方便,转了很多网友的文章!希望不会介意)...
  13. apache ii评分和死亡率_危重病人APACHE II评分表
  14. 中文字体的FontMetrics解析
  15. 项目管理的方法论 一
  16. Sonic云真机测试平台在windows的部署及使用
  17. stc89c52 单片机模块
  18. 读书笔记 | 4.3 基于征信系统的征信基础产品
  19. python用余弦相似度计算英文文本相似度
  20. keras使用VGG19网络模型实现风格迁移

热门文章

  1. 计算机动画制作 实验要求,有关计算机动画设计课程教学的对比试验
  2. tf.lookup.StaticHashTable 用法
  3. windows10上为jupyter notebook切换指定conda环境
  4. 机器学习算法源码全解析(四)-人工神经网络关键核心知识点汇总
  5. Matplotlib实例教程(三)折线图 plt.plot()
  6. 深度学习100例-卷积神经网络(LeNet-5)深度学习里的“Hello Word” | 第22天
  7. 虚拟机安装多了,怎么删除?
  8. arcgis-shp文件属性表导出为dbf或txt
  9. 详解TF-Ranking:Google开源的排序框架,应用于邮件检索、推荐系统等场景
  10. gradle本地、远程仓库配置--转