Spring Boot整合Zookeeper实现分布式锁的场景分析

目录
  • 一、Java当中关于锁的概念
    • 1.1.什么是锁
    • 1.2.锁的使用场景
    • 1.3.什么是分布式锁
    • 1.4.分布式锁的使用场景
  • 二、zk实现分布式锁
    • 2.1.zk中锁的种类:
    • 2.2.zk如何上读锁
    • 2.3.zk如何上写锁
    • 2.4.⽺群效应
  • 三、springboot整合分布式锁

温馨提示本篇文章要求掌握zk的数据结构,以及临时序号节点!

zk实现分布式锁完全是依靠zk节点类型当中的临时序号节点来实现的

一、Java当中关于锁的概念

1.1.什么是锁

锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源。

1.2.锁的使用场景

以减库存为例,库存这时候就剩1个,那么我们得保证只会有1个请求真正的完成减1操作,假如代码逻辑是,先从库里查库存,通过if条件判断,如果有就减,没有就返回购买失败。

这时候并发访问减库存接口,可能这时候我们代码当中的if判断已经失效了,多个请求同时查到还有一个,并且已经进入了if判断,没有加锁的话,这时候事情就比较严重了,库存一下子就成了负数。

对于这块代码我们不希望同时有人减库存,这时候就需要加锁来控制,加完锁之后,就是将并发请求改成了串行,也就是不管并发了多少个请求,我通过加锁,只让1个请求进行减库存,你们去争抢锁资源吧,谁先抢到了就是谁的。

1.3.什么是分布式锁

分布式锁是控制 微服务集群 之间同步访问共享资源的一种方式。

1.4.分布式锁的使用场景

使用分布式锁前提:微服务一定是集群,这里的微服务不是指的zk,而是指的我们的业务模块,在项目当中一般的,由于并发量较高,往往会将业务拆分为一个模块一个模块,例如订单模块,库存模块,拆模块其中一个目的就是为了针对于模块的并发性进行集群部署,比如订单模块用的比较多,我可以搭建多个订单模块,但是尽管他们的模块是多个,围绕的数据还是共通的(一个数据库)。

集群情况下,我们在代码加普通锁已经解决不了问题,假如现在有三个库存微服务,设置了负载均衡的方式访问,普通锁只能控制自己的服务减库存不会出现负数,但是他控制不了其他两个服务,数据是共通的,所以这时候只能使用分布式锁,通过分布式锁来控制三个服务当库存只有一个商品的时候,只能有一个服务访问的请求可以减库存成功。

二、zk实现分布式锁

2.1.zk中锁的种类:

  • 读锁:创建⼀个临时序号节点,节点的名称会包含READ的字母,表示是读锁,⼤家都可以读,要想上读锁的前提:之前的锁没有写锁
  • 写锁:创建⼀个临时序号节点,节点的名称会包含WRIT的字母,表示是写锁,只有得到写锁的才能写。要想上写锁的前提是,之前没有任何锁

读锁和写锁完全是按照创建的临时序号节点的名称来区分的!

  • 序号节点创建出的节点,根据先后顺序,会在节点之后带上⼀个数值,越往后执⾏数值越⼤,类似于mysql的主键自增
  • 临时节点 :临时节点是在会话结束后,⾃动被删除的,通过这个特性,zk可以实现服务注册与发现的效果。假如会话关掉后大概10s左右,创建的临时节点就会消失。这个会话就是指的连接zk的客户端。
  • 临时序号节点:就是上面两个的结合体

当需要上锁的时候,就进行创建临时序号节点,释放锁的时候就删除节点。

2.2.zk如何上读锁

  • 创建一个临时序号节点
  • 获取当前zk中序号比自己小的所有节点
  • 判断最小节点是否是读锁:
  • 如果不是读锁的话,则上锁失败,为最小节点设置监听。阻塞等待,zk的watch机制会当最小节点发生变化时通知当前节点,于是再执行第二步的流程
  • 如果是读锁的话,则上锁成功

想要上读锁,主要就是需要看比他小的节点当中是否有写锁。如果有写锁,就需要等他用完之后删除节点,通过watch机制来通知他,写锁已经释放,然后他再进行第二步判断。

2.3.zk如何上写锁

  • 创建一个临时序号节点
  • 获取zk中所有的子节点
  • 判断自己是否是最小的节点:
  • 如果是,则上写锁成功
  • 如果不是,说明前面还有锁,则上锁失败,监听最小的节点,如果最小节点有变化,则回到第二步。

2.4.⽺群效应

如果⽤上述的上锁⽅式,只要有节点发⽣变化,就会触发其他节点的监听事件,这样的话对zk的压⼒⾮常⼤,——⽺群效应。可以调整成链式监听。解决这个问题。

假如并发了100个请求都需要获取写锁,这时候创建了100个节点来监听最小节点,当最小节点发生变化的时候,意味着他一下子要进行通知100个节点,zk瞬间会压力非常大。

所以这时候可以采用链式监听,链式监听仍然是依靠的序号节点的特点。就好比mysql设置自增后,不管多少并发请求,他仍然能保证id的唯一性,zk的序号节点同样也是。让他们都不再监听最小节点,而是监听他的上一个节点。当上一个节点释放锁后,那当前节点就可以创建写锁了。

这里还会遇到一个问题,假如他的上一个节点意外删除了,但是并不是等着拿到锁后释放锁,而是单纯的不想等了,所以删除了节点,而上面还有很多节点加着锁呢,所以我们不能单纯的靠上一个节点删除后当前节点就进行加锁。我们加写锁要保证的是,他上面没有任何节点加锁。这时候让他进行监听上上个节点即可。假如上上个节点仍然有问题了,那就监听上上上个节点。总之一点就是尽量避免不让多个节点同时去监听一个节点

三、springboot整合分布式锁

springboot整合curator客户端:https://www.jb51.net/article/181082.htm

我直接是基于上一篇文章当中的项目进行 分布式锁 练习的!

根据上面提到的zk分布式锁实现思路,我们其实并不用去自己写,在curator客户端已经给我们提供了现成的方法,我们只需要简单的调用客户端提供的方法,就可以实现分布式锁功能!

@Autowired
CuratorFramework curatorFramework;

/**
 * 获取读锁的条件是前面没有写锁
 * 当/lock1节点不存在的时候,我们不需要手动去创建,获取锁的时候会自动创建
 * 自动创建的是临时节点,用完之后释放锁的时候会删除掉的
 * 获取到锁之后会在/lock1节点下创建一个临时序号节点
 * 然后没有获取到锁的线程也会创建一个节点,这时候处于等待期间
 * 释放锁的时候,首先会删除掉自己的序号节点,然后假如没有人在排队用锁,这时候会把/lock1节点也删除掉
 *
 * @throws Exception
 */
@Test
void testGetReadLock() throws Exception {
    // 读写锁
    InterProcessReadWriteLock interProcessReadWriteLock = new
            InterProcessReadWriteLock(curatorFramework, "/lock1");
    // 获取读锁对象(创建对象耗时也就18毫秒)
    InterProcessLock interProcessLock = interProcessReadWriteLock.readLock();
    System.out.println("等待获取读锁对象!");

    // 获取锁(假如一直没拿到锁这个方法一直会是阻塞的,就算不阻塞的情况下,这个方法耗时也是特别长,高达17秒)
    interProcessLock.acquire();

    // 正常的我们代码假如走到了这一步,说明已经获取到锁了,这里写相关的业务代码即可,执行完记住释放锁
    System.out.println("获取到了锁!");
    for (int i = 1; i <= 100; i++) {
        Thread.sleep(3000);
        System.out.println(i);
    }
    // 释放锁(方法耗时13毫秒)
    interProcessLock.release();
    // 走到这一步的时候节点已经被删除了
    System.out.println("等待释放锁!");
}

/**
 * 获取写锁的条件是前面没有任何的锁
 *
 * @throws Exception
 */
@Test
void testGetWriteLock() throws Exception {
    // 读写锁
    InterProcessReadWriteLock interProcessReadWriteLock = new
            InterProcessReadWriteLock(curatorFramework, "/lock1");
    // 获取写锁对象
    InterProcessLock
            interProcessLock = interProcessReadWriteLock.writeLock();
    System.out.println("等待获取写锁对象!");
    // 获取锁(假如一直没拿到锁这个方法一直会是阻塞的)
    interProcessLock.acquire();
    for (int i = 1; i <= 100; i++) {
        Thread.sleep(3000);
        System.out.println(i);
    }
    // 释放锁
    interProcessLock.release();
    System.out.println("等待释放锁!");
}

/**
 * 方便测试多个线程获取读锁
 *
 * @throws Exception
 */
@Test
void testGetReadLock1() throws Exception {
    testGetReadLock();
}

这个是两个获取读锁的时候的节点场景:

这个是一个获取读锁,一个获取写锁的场景,然后读锁节点是0004,所以是后创建的,他只有等待0003释放写锁,才能获取到读锁。

zk分布式锁会产生死锁吗?

这个肯定是不会的,因为假如有客户端拿到了锁,还没释放,服务挂了,这时候依据临时节点的特性,当临时节点和客户端断开连接几秒后会自动删除的,删除节点也就意味着自动释放锁!

正常情况不建议使用zk作为分布式锁,效率属实太慢。

到此这篇关于Spring Boot整合Zookeeper实现分布式锁的文章就介绍到这了,更多相关Spring Boot整合Zookeeper分布式锁内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot之使用Redis实现分布式锁(秒杀系统)

    一.Redis分布式锁概念篇 建议直接采用Redis的官方推荐的Redisson作为redis的分布式锁 1.1.为什么要使用分布式锁 我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,可以使用我们学到的Java多线程的18般武艺进行处理,并且可以完美的运行,毫无Bug! 注意这是单机应用,也就是所有的请求都会分配到当前服务器的JVM内部,然后映射为操作系统的线程进行处理!而这个共享变量只是在这个JVM内部的一块内存空间! 后来业务发展,需要做集群,一个应用需要部署到几台机

  • springboot+zookeeper实现分布式锁的示例代码

    目录 依赖 本地封装 配置 测试代码 JMeter测试 InterProcessMutex内部实现了zookeeper分布式锁的机制,所以接下来我们尝试使用这个工具来为我们的业务加上分布式锁处理的功能 zookeeper分布式锁的特点:1.分布式 2.公平锁 3.可重入 依赖 <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId>

  • 浅谈Java(SpringBoot)基于zookeeper的分布式锁实现

    通过zookeeper实现分布式锁 1.创建zookeeper的client 首先通过CuratorFrameworkFactory创建一个连接zookeeper的连接CuratorFramework client public class CuratorFactoryBean implements FactoryBean<CuratorFramework>, InitializingBean, DisposableBean { private static final Logger LOGG

  • SpringBoot中dubbo+zookeeper实现分布式开发的应用详解

    总体实现思路是启动一个生产者项目注册, 将所含服务注册到zookeeper的注册中心, 然后在启动一个消费者项目,将所需服务向zookeeper注册中心进行订阅, 等待注册中心的通知 注册中心基于负载均衡算法给消费者匹配到合适的生产者主机,然后通知消费者可以使用 实现生产者 导入zookeeper依赖包 <!-- Dubbo Spring Boot Starter --> <dependency> <groupId>org.apache.dubbo</groupI

  • 使用dubbo+zookeeper+spring boot构建服务的方法详解

    前言 互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,Dubbo是一个分布式服务框架,在这种情况下诞生的.现在核心业务抽取出来,作为独立的服务,使前端应用能更快速和稳定的响应. Dubbo是什么 Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合).从服务模型的角度来看,Dubbo采用的是一种非常简单的模型,要么是提供方提供服务,要么是消费方消费服

  • IDEA整合Dubbo+Zookeeper+SpringBoot实现

    本文主要介绍了IDEA整合Dubbo+Zookeeper+SpringBoot实现,分享给大家,具体如下: 放上一张Dubbo的架构图 刚刚学过SpringCloud,Dubbo虽然没有Spring家族那么强悍,但始终不能违背Dubbo是一款高性能优秀的RPC框架. click官网地址了解更多 自己也是初学者,想自己动手做一个简单的demo,于是上网查资料,发现针对那种入门的案例,网上主要有两个比较经典的,一个是尚硅谷雷神版,还有一个是官网推荐的入门 案例. 雷神的Dubbo视频教程大神讲解 视

  • Spring Boot整合Zookeeper实现分布式锁的场景分析

    目录 一.Java当中关于锁的概念 1.1.什么是锁 1.2.锁的使用场景 1.3.什么是分布式锁 1.4.分布式锁的使用场景 二.zk实现分布式锁 2.1.zk中锁的种类: 2.2.zk如何上读锁 2.3.zk如何上写锁 2.4.⽺群效应 三.springboot整合分布式锁 温馨提示:本篇文章要求掌握zk的数据结构,以及临时序号节点! zk实现分布式锁完全是依靠zk节点类型当中的临时序号节点来实现的 一.Java当中关于锁的概念 1.1.什么是锁 锁是用来控制多个线程访问共享资源的方式,一般

  • Spring boot 整合 Redisson实现分布式锁并验证功能

    目录 简述 1. 在idea中新建spring boot工程并引入所需依赖 2. 编写相关代码实现 3. 模拟实际环境验证 3.1 下载idea的docker插件并配置相关镜像信息 3.2 将spring boot打包的jar构建为docker镜像 3.2 配置nginx 3.3 下载安装Jmeter进行测试 简述 整篇文章写的比较粗糙,大佬看了轻喷.前半部分 是整合spring boot和redisson, 后半部分是验证分布式锁.在整个过程中遇见了不少的问题,在此做个记录少走弯路 redis

  • Spring Boot 集成Redisson实现分布式锁详细案例

    目录 前言 分布式锁实现 引入jar包 Redisson的配置 application.yml中引入redisson.yml配置 redisson.yml配置 封装Redisson工具类 模拟秒杀扣减库存 测试代码 总结 前言 Spring Boot集成Redis实现单机分布式锁针对单机分布式锁还是存在锁定续期.可重入的问题,本文将采用Spring Boot 集成Ression实现分布式锁进行详细讲解. 分布式锁实现 引入jar包 <dependency> <groupId>org

  • Spring Boot整合流控组件Sentinel的场景分析

    目录 一.百度百科 1.Sentinel 特性 2.Sentinel 的开源生态 二.Sentinel 的历史 三.Sentinel 基本概念 1.资源 2.规则 四.sentinel的优势 五.Sentinel 功能和设计理念 1.什么是流量控制 2.流量控制设计理念 3.什么是熔断降级 4.熔断降级设计理念 5.系统自适应保护 6.Sentinel 是如何工作的 7.竞品对比 六.SpringBoot整合sentinel 1.加入pom 2.编写sentinel规则 3.测试 七.sprin

  • Spring Boot配置线程池拒绝策略的场景分析(妥善处理好溢出的任务)

    目录 场景重现 配置拒绝策略 代码示例 通过之前三篇关于Spring Boot异步任务实现的博文,我们分别学会了用@Async创建异步任务.为异步任务配置线程池.使用多个线程池隔离不同的异步任务.今天这篇,我们继续对上面的知识进行完善和优化! 如果你已经看过上面几篇内容并已经掌握之后,一起来思考下面这个问题: 假设,线程池配置为核心线程数2.最大线程数2.缓冲队列长度2.此时,有5个异步任务同时开始,会发生什么? 场景重现 我们先来把上面的假设用代码实现一下: 第一步:创建Spring Boot

  • spring boot 动态生成接口实现类的场景分析

    目录 一: 定义注解 二: 建立动态代理类 三: 注入spring容器 四: 编写拦截器 五: 新建测试类 在某些业务场景中,我们只需要业务代码中定义相应的接口或者相应的注解,并不需要实现对应的逻辑. 比如 mybatis和feign: 在 mybatis 中,我们只需要定义对应的mapper接口:在 feign 中,我们只需要定义对应业务系统中的接口即可. 那么在这种场景下,具体的业务逻辑时怎么执行的呢,其实原理都是动态代理. 我们这里不具体介绍动态代理,主要看一下它在springboot项目

  • SpringBoot整合Redisson实现分布式锁

    目录 一.添加依赖 二.redis配置文件 三.新建配置类 四.使用分布式锁 可重入锁 读写锁 信号量(Semaphore) 闭锁(CountDownLatch) Redisson是架设在redis基础上的一个Java驻内存数据网格(In-Memory Data Grid).充分的利用了Redis键值数据库提供的一系列优势,基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类.使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低

  • Java Spring Boot 集成Zookeeper

    目录 集成步骤 1.pom.xml文件配置,引入相关jar包 2. 核心配置类 3.常用API功能 4.基本使用 常见错误和解决办法 问题1:调用api创建zookeeper节点时,报KeeperErrorCode = Unimplemented for /test错误. 问题2:启动项目的日志中会有Will not attempt to authenticate using SASL错误 集成步骤 1.pom.xml文件配置,引入相关jar包 Curator是Netflix公司开源的一套zoo

  • Spring Boot整合Elasticsearch实现全文搜索引擎案例解析

    简单说,ElasticSearch(简称 ES)是搜索引擎,是结构化数据的分布式搜索引擎.Elastic Search是一个开源的,分布式,实时搜索和分析引擎.Spring Boot为Elasticsearch及Spring Data Elasticsearch提供的基于它的抽象提供了基本的配置.Spring Boot提供了一个用于聚集依赖的spring-boot-starter-data-elasticsearch 'StarterPOM'. 引入spring-boot-starter-dat

随机推荐