本文是“Java秒杀系统实战系列文章”的第十六篇,本文我们将继续秒杀系统的优化之路,采用统一协调调度中心中间件ZooKeeper控制秒杀系统中高并发多线程对于共享资源~代码块的并发访问所出现的并发安全问题,即用ZooKeeper实现一种分布式锁!

ZooKeeper,看到其名字,不由得联想至 Zoo + Keeper,即动物园的看管所!这个寓意用以表达的是一种统一协调管理思想,动物园有很多动物,这些动物就类似于分布式系统架构时代所部署的不同系统服务节点,而这些动物~服务节点偶尔可能需要打交道,相互之间可能需要进行相应的问候,这个时候得需要有一个“看管者”,其职责除了需要管理动物园里的这些动物的行为之外(即这些系统服务的行为),还需要统一协调管理这些动物之间的“问候”、“打交道”(系统服务之间的调用)!

ZooKeeper对外会提供一个多层级的节点命名空间(节点称为ZNode),每个节点都用一个以斜杠(/)分隔的路径表示,而且每个节点都有父节点(根节点除外)。ZooKeeper的相关功能特性在实际使用过程中,其底层可能需要动态的添加、删减相应的节点,此时zk会提供一个Watcher监听器,用以监听那些动态新增、删减的节点,即ZooKeeper会在某些业务场景对一些节点使用上Watcher机制,监听相应的节点的动态。

我们即将要在下面介绍的“分布式锁”功能组件即为ZooKeeper提供给开发者的一大利器,其底层的实现原理正是基于Watcher机制 + 动态创建、删减临时顺序节点 所实现的,值得一提的是,一个ZNode节点将代表一个路径。

以下为ZooKeeper实现(获取)分布式锁的原理:

(1)当前线程在获取分布式锁的时候,会在ZNode节点(ZNode节点是Zookeeper的指定节点)下创建临时顺序节点,释放锁的时候将删除该临时节点。

(2)客户端/服务调用createNode方法在 ZNode节点 下创建临时顺序节点,然后调用getChildren(“ZNode”)来获取ZNode下面的所有子节点,注意此时不用设置任何Watcher。

(3)客户端/服务获取到所有的子节点path之后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁,即当前线程获取到了分布式锁。

(4)如果发现自己创建的节点并非ZNode所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,然后对其调用exist()方法,同时对其注册事件监听器。

之后,让这个被关注的节点删除(核心业务逻辑执行完,释放锁的时候,就是删除该节点),则客户端的Watcher会收到相应的通知,此时再次判断自己创建的节点是否是ZNode子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。

以上为ZooKeeper的基本介绍以及关于其底层实现分布式锁的原理的介绍,但是,Debug想说的是“理论再好,如果不会转化为实际的代码或者输出,那只能称之为泛泛而谈、吹牛逼” !

下面,我们将基于SpringBoot搭建的秒杀系统整合ZooKeeper,并基于ZooKeeper实现一种分布式锁,以此解决秒杀系统中高并发多线程并发产生的诸多问题。

(1)首先,当然是引入ZooKeeper的依赖啦,其中zk的版本在这里我们采用3.4.10,zk客户端操作实例curator的版本为2.12.0.

org.apache.zookeeper zookeeper 3.4.10org.slf4j slf4j-log4j12 log4j log4j org.apache.curator curator-framework 2.12.0org.apache.curator curator-recipes 2.12.0

紧接着,是在配置文件application.properties中加入ZooKeeper的配置,包括其服务所在的Host、端口Port、命名空间等等:

#zookeeperzk.host=127.0.0.1:2181zk.namespace=kill

(2)然后,跟Redis、Redisson一样,我们需要基于Spring Boot自定义注入ZooKeeper的相关操作Bean组件,即CuratorFramework实例的自定义配置,如下所示:

//ZooKeeper组件自定义配置@Configurationpublic class ZooKeeperConfig { @Autowired private Environment env; //自定义注入ZooKeeper客户端操作实例 @Bean public CuratorFramework curatorFramework(){ CuratorFramework curatorFramework=CuratorFrameworkFactory.builder() .connectString(env.getProperty("zk.host")) .namespace(env.getProperty("zk.namespace")) //重试策略 .retryPolicy(new RetryNTimes(5,1000)) .build(); curatorFramework.start(); return curatorFramework; }}

(3)接着,我们就可以拿来使用了,在KillService秒杀服务类中,我们创建了一个新的秒杀处理方法killItemV5,表示借助ZooKeeper中间件解决高并发多线程并发访问共享资源~共享代码块出现的并发安全问题!

@Autowiredprivate CuratorFramework curatorFramework;//TODO:路径就相当于一个ZNodeprivate static final String pathPrefix="/kill/zkLock/";//商品秒杀核心业务逻辑的处理-基于ZooKeeper的分布式锁@Overridepublic Boolean killItemV5(Integer killId, Integer userId) throws Exception { Boolean result=false; //定义获取分布式锁的操作组件实例 InterProcessMutex mutex=new InterProcessMutex(curatorFramework,pathPrefix+killId+userId+"-lock"); try { //尝试获取分布式锁 if (mutex.acquire(10L,TimeUnit.SECONDS)){ //TODO:核心业务逻辑 if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){ ItemKill itemKill=itemKillMapper.selectByIdV2(killId); if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){ int res=itemKillMapper.updateKillItemV2(killId); if (res>0){ commonRecordKillSuccessInfo(itemKill,userId); result=true; } } }else{ throw new Exception("zookeeper-您已经抢购过该商品了!"); } } }catch (Exception e){ throw new Exception("还没到抢购日期、已过了抢购时间或已被抢购完毕!"); }finally { //释放分布式锁 if (mutex!=null){ mutex.release(); } } return result;}

从上述该源代码中可以看出其核心的处理逻辑在于“定义操作组件实例”、“获取锁”以及“释放锁”的实现上,如下所示:

//定义获取分布式锁的操作组件实例InterProcessMutex mutex=new InterProcessMutex(curatorFramework,pathPrefix+killId+userId+"-lock");//尝试获取分布式锁mutex.acquire(10L,TimeUnit.SECONDS)//释放锁mutex.release();

(4)至此,基于统一协调调度中心中间件ZooKeeper实现的分布式锁的代码我们已经实战完毕了,下面我们按照惯例,进入压测环节,数据用例以及压测的线程组的线程数我们仍旧跟以前一样,total=6本书,用户Id为10040~10049即10个用户,线程数为1w。

点击JMeter的启动按钮,即可发起秒级并发1w个线程的请求,稍等片刻(因为ZooKeeper需要不断的在当前设定的节点创建、删除临时节点,故而耗时还是比较长的),观察控制台的输出以及数据库表item_kill、item_kill_success表最终的数据记录结果,如下图所示:

对于这样的结果,可谓是皆大欢喜吧!至此,本文关于ZooKeeper的应用实战我们就介绍到这里了!

相关视频教程可私信咨询。

推荐阅读:

Java商城秒杀系统的设计与实战教程(SpringBoot版)

Java秒杀系统实战系列-构建SpringBoot多模块项目

Java秒杀系统实战系列-基于Redis的原子操作优化秒杀逻辑

ieee39节点系统介绍_Java秒杀系统实战系列-基于ZooKeeper的分布式锁优化秒杀逻辑...相关推荐

  1. java设计前期工作基础和存在的困难_Java秒杀系统实战系列-基于Redisson的分布式锁优化秒杀逻辑...

    本文是"Java秒杀系统实战系列文章"的第十五篇,本文我们将借助综合中间件Redisson优化"秒杀系统中秒杀的核心业务逻辑",解决Redis的原子操作在优化秒 ...

  2. Java秒杀系统实战系列~基于Redisson的分布式锁优化秒杀逻辑

    摘要: 本篇博文是"Java秒杀系统实战系列文章"的第十五篇,本文我们将借助综合中间件Redisson优化"秒杀系统中秒杀的核心业务逻辑",解决Redis的原子 ...

  3. Java秒杀系统实战系列~基于Redis的原子操作优化秒杀逻辑

    https://blog.csdn.net/u013871100/article/details/99621967

  4. sql判断时间大于0点_Java秒杀系统实战系列-数据库级别Sql的优化与代码的调整

    本文是"Java秒杀系统实战系列文章"的第十三篇,从本篇文章开始我们将进入"秒杀代码优化"环节,本文将首先从数据库级别Sql的优化入手,结合调整秒杀相关的部分核 ...

  5. asp按时间自动递增编号_Java秒杀系统实战系列-分布式唯一ID生成订单编号

    本文是"Java秒杀系统实战系列文章"的第七篇,在本文中我们将重点介绍 "在高并发,如秒杀的业务场景下如何生成全局唯一.趋势递增的订单编号",我们将介绍两种方法 ...

  6. java 模块 分工_Java秒杀系统实战系列~构建SpringBoot多模块项目

    摘要:本篇博文是"Java秒杀系统实战系列文章"的第二篇,主要分享介绍如何采用IDEA,基于SpringBoot+SpringMVC+Mybatis+分布式中间件构建一个多模块的项 ...

  7. sql取系统时间减一小时_Java秒杀系统实战系列-整体业务流程介绍与数据库设计...

    本篇文章是"Java秒杀系统实战系列文章"的第三篇,本文将主要介绍秒杀系统的整体业务流程,并根据相应的业务流程进行数据库设计,最终采用Mybatis逆向工程生成相应的实体类Enti ...

  8. java 唯一编号_Java秒杀系统实战系列~分布式唯一ID生成订单编号

    摘要: 本篇博文是"Java秒杀系统实战系列文章"的第七篇,在本博文中我们将重点介绍 "在高并发,如秒杀的业务场景下如何生成全局唯一.趋势递增的订单编号",我们 ...

  9. java 秒杀代码_Java秒杀系统实战系列~商品秒杀代码实战

    摘要: 本篇博文是"Java秒杀系统实战系列文章"的第六篇,本篇博文我们将进入整个秒杀系统核心功能模块的代码开发,即"商品秒杀"功能模块的代码实战. 内容: & ...

最新文章

  1. 2022-2028年中国软件测试行业市场研究及前瞻分析报告
  2. 关闭Windows 7中的 Program Compatibility Assistant
  3. 面试数百名NLP工程师发现:90%以上是不合格的
  4. boost::python::detail::result相关的测试程序
  5. yjv是电缆还是电线_BVV属于电线还是电缆?
  6. 第二十五期:知乎用Go替代Python,说明了啥
  7. AOP Aspect Oriented Programming 面向切面编程 Spring
  8. 最长公共子序列LCS(动态规划)—详解
  9. dataguard switchover的自动化脚本实现
  10. 微课|《Python编程基础与案例集锦(中学版)》第3章(1)
  11. 【2011.9.29】得到明天的时间,得到明天某时刻和现在的时间差(毫秒)
  12. matlab随机线性微分方程,基于MATLAB的随机线性微分方程的求解
  13. 下载超星或读秀图书时,怎么搞定完整书签?
  14. 郑州学python哪个机构好_郑太高铁线路图_郑太高铁站点_【高铁网】_郑太高铁时刻表_郑太高铁通车时间...
  15. 探索性与验证性因子分析
  16. Vue2.0+组件库总结
  17. 【JVM】运行时数据区概述(程序计数器、虚拟机栈、本地方法栈)
  18. 把阿拉伯数字翻译成中文大写数字
  19. 大学生必须掌握的计算机软件基础
  20. linux 查看硬盘信息

热门文章

  1. 【转载】贝叶斯决策论
  2. C# Obsolete
  3. 员工辞职的十大原因!
  4. 牛客14342 神奇的数字
  5. java下载文件夹_java如何通过共享目录下载文件夹(有子文件夹)到本地目录?...
  6. mysql 启动 修改密码_基础的启动/停止/重启/密码修改MySQL
  7. python文件系统_你应该知道的10个Python文件系统方法
  8. c语言商品货架管理_汽配仓库布局及管理
  9. linux la 的使用方法,Linux简介及常用命令使用4--linux高级命令与技巧(示例代码)
  10. android actionbar 背景,Android应用开发之定制页面背景及Actionbar overflow menu的背景色...