zookeeper实现分布式锁

仓库地址:https://gitee.com/J_look/ssm-zookeeper/blob/master/README.md

  • 锁:我们在多线程中接触过,作用就是让当前的资源不会被其他线程访问!

    我的日记本,不可以被别人看到。所以要锁在保险柜中

    当我打开锁,将日记本拿走了,别人才能使用这个保险柜
  • 在zookeeper中使用传统的锁引发的 “羊群效应” :1000个人创建节点,只有一个人能成功,999

    人需要等待!
  • 羊群是一种很散乱的组织,平时在一起也是盲目地左冲右撞,但一旦有一只头羊动起来,其他的羊

    也会不假思索地一哄而上,全然不顾旁边可能有的狼和不远处更好的草。羊群效应就是比喻人都有

    一种从众心理,从众心理很容易导致盲从,而盲从往往会陷入骗局或遭到失败。

实现分布式锁大致流程

整体思路

  1. 所有请求进来,在/lock下创建 临时顺序节点 ,放心,zookeeper会帮你编号排序

  2. 判断自己是不是/lock下最小的节点

  3. 是,获得锁(创建节点)

  4. 否,对前面小我一级的节点进行监听

  5. 获得锁请求,处理完业务逻辑,释放锁(删除节点),后一个节点得到通知(比你年轻的死了,你

    成为最嫩的了)

  6. 重复步骤2

安装nginx

安装nginx运行所需的库

  1. //一键安装上面四个依赖
  2. yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel

下载nginx

在那个目录下执行这个命令 就会下载到哪个目录下

  1. //下载tar包
  2. wget http://nginx.org/download/nginx-1.13.7.tar.gz

解压

注意哦 解压出来的文件 我们还需要安装哦

下面所有的命令 都是在nginx-1.13.7文件夹里面进行哦

  1. tar -zxvf nginx-1.13.7.tar.gz
  • 查看解压出来的文件

    1. ll ./nginx-1.13.7

安装

创建一个文件夹,也就是nginx需要安装到的位置

  1. mkdir /usr/local/nginx

执行命令 考虑到后续安装ssl证书 添加两个模块

  1. ./configure --with-http_stub_status_module --with-http_ssl_module

执行make install命令

  1. make install
  • 我们可以来到nginx安装到的目录下查看
  • 你们没有我这么多目录 conf 配置 sbin 启动nginx
  • 博主技术有限,还没有深入去学习nginx的 大致这样介绍吧

启动nginx服务

我这个是在/ 目录底下执行的 你们可以根据 自己所在的目录去执行

  1. /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf

访问nginx

nginx的默认端口是80

配置nginx

我们所做的配置大概就是

  • 当有请求去访问我们服务器,
  • 然后负载到我们处理请求的服务器 我这里是两台
  • 为了方便 处理请求的这两台服务器 是在我Windows上

打开配置文件

  1. # 打开配置文件
  2. vim /usr/local/nginx/conf/nginx.conf
  • 图中 红框的位置 是需要添加的内容
  • 配置含义: 我们的nginx监听的是服务器的80端口 当有请求访问时 会负载到 look代理里面 server是处理请求的两台服务器
  • 查看本机ip Windows ==>ipconfig linux ==> ip a(ip address)

  1. upstream look{
  2. server 192.168.204.1:8001; //192.168.204.1是我本机的ip地址,8001是tomcat的端口号
  3. server 192.168.204.1:8002; //8002是另外一个工程的tomcat端口号
  4. }
  5. server {
  6. listen 80;
  7. server_name localhost;
  8. #charset koi8-r;
  9. #access_log logs/host.access.log main;
  10. location / {
  11. proxy_pass http://look;
  12. root html;
  13. index index.html index.htm;
  14. }

工程的搭建

搭建ssm框架 有时间推出springboot的版本

  • 创建一个maven项目(普通maven项目即可)

创建数据库:

  1. -- 商品表
  2. create table product(
  3. id int primary key auto_increment, -- 商品编号
  4. product_name varchar(20) not null, -- 商品名称
  5. stock int not null, -- 库存
  6. version int not null -- 版本
  7. )
  8. insert into product (product_name,stock,version) values('锦鲤-清空购物车-大奖',5,0)
  1. -- 订单表
  2. create table `order`(
  3. id varchar(100) primary key, -- 订单编号
  4. pid int not null, -- 商品编号
  5. userid int not null -- 用户编号
  6. )
  • 项目目录结构

添加依赖

简单解释一下build

  • 我们引入的是tomcat7的插件
  • configuration 配置的是端口 和根目录
  • 注意哦 记得刷新pom文件 build里面会有爆红 不要紧张 不用管他 后面的配置他会自己消失
  1. <properties>
  2. <maven.compiler.source>8</maven.compiler.source>
  3. <maven.compiler.target>8</maven.compiler.target>
  4. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  5. <spring.version>5.2.7.RELEASE</spring.version>
  6. </properties>
  7. <packaging>war</packaging>
  8. <dependencies>
  9. <!-- Spring -->
  10. <dependency>
  11. <groupId>org.springframework</groupId>
  12. <artifactId>spring-context</artifactId>
  13. <version>${spring.version}</version>
  14. </dependency>
  15. <dependency>
  16. <groupId>org.springframework</groupId>
  17. <artifactId>spring-beans</artifactId>
  18. <version>${spring.version}</version>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.springframework</groupId>
  22. <artifactId>spring-webmvc</artifactId>
  23. <version>${spring.version}</version>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework</groupId>
  27. <artifactId>spring-jdbc</artifactId>
  28. <version>${spring.version}</version>
  29. </dependency>
  30. <!-- Mybatis -->
  31. <dependency>
  32. <groupId>org.mybatis</groupId>
  33. <artifactId>mybatis</artifactId>
  34. <version>3.5.10</version>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.mybatis</groupId>
  38. <artifactId>mybatis-spring</artifactId>
  39. <version>2.0.7</version>
  40. </dependency>
  41. <!-- 连接池 -->
  42. <dependency>
  43. <groupId>com.alibaba</groupId>
  44. <artifactId>druid</artifactId>
  45. <version>1.2.11</version>
  46. </dependency>
  47. <!-- 数据库 -->
  48. <dependency>
  49. <groupId>mysql</groupId>
  50. <artifactId>mysql-connector-java</artifactId>
  51. <version>8.0.29</version>
  52. </dependency>
  53. <!-- junit -->
  54. <dependency>
  55. <groupId>junit</groupId>
  56. <artifactId>junit</artifactId>
  57. <version>4.13.2</version>
  58. <scope>test</scope>
  59. </dependency>
  60. <dependency>
  61. <groupId>org.projectlombok</groupId>
  62. <artifactId>lombok</artifactId>
  63. <version>1.18.24</version>
  64. </dependency>
  65. </dependencies>
  66. <build>
  67. <plugins>
  68. <!-- maven内嵌的tomcat插件 -->
  69. <plugin>
  70. <groupId>org.apache.tomcat.maven</groupId>
  71. <!-- 目前apache只提供了tomcat6和tomcat7两个插件 -->
  72. <artifactId>tomcat7-maven-plugin</artifactId>
  73. <configuration>
  74. <port>8002</port>
  75. <path>/</path>
  76. </configuration>
  77. <executions>
  78. <execution>
  79. <!-- 打包完成后,运行服务 -->
  80. <phase>package</phase>
  81. <goals>
  82. <goal>run</goal>
  83. </goals>
  84. </execution>
  85. </executions>
  86. </plugin>
  87. </plugins>
  88. </build>

mybatis.xml

注意哦 :仔细查看上面的项目结构 创建相应的文件夹

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  3. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  4. <configuration>
  5. <!-- 后台的日志输出 输出到控制台-->
  6. <settings>
  7. <setting name="logImpl" value="STDOUT_LOGGING"/>
  8. </settings>
  9. </configuration>

spring.xml

注意哦 :仔细查看上面的项目结构 创建相应的文件夹

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:mvc="http://www.springframework.org/schema/mvc"
  5. xmlns:context="http://www.springframework.org/schema/context"
  6. xmlns:tx="http://www.springframework.org/schema/tx"
  7. xsi:schemaLocation="http://www.springframework.org/schema/beans
  8. http://www.springframework.org/schema/beans/spring-beans.xsd
  9. http://www.springframework.org/schema/context
  10. http://www.springframework.org/schema/context/spring-context.xsd
  11. http://www.springframework.org/schema/tx
  12. http://www.springframework.org/schema/tx/spring-tx.xsd">
  13. <!-- 1.扫描包下的注解 -->
  14. <context:component-scan base-package="controller,service,mapper"/>
  15. <!-- 2.创建数据连接池对象 -->
  16. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
  17. destroy-method="close">
  18. <property name="url" value="jdbc:mysql://localhost:3306/2022_zkproduct?serverTimezone=GMT"/>
  19. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  20. <property name="username" value="root"/>
  21. <property name="password" value="317311"/>
  22. <property name="maxActive" value="10"/>
  23. <property name="minIdle" value="5"/>
  24. </bean>
  25. <!-- 3.创建SqlSessionFactory,并引入数据源对象 -->
  26. <bean id="sqlSessionFactory"
  27. class="org.mybatis.spring.SqlSessionFactoryBean">
  28. <property name="dataSource" ref="dataSource"></property>
  29. <property name="configLocation" value="classpath:mybatis/mybatis.xml"></property>
  30. </bean>
  31. <!-- 4.告诉spring容器,数据库语句代码在哪个文件中-->
  32. <!-- mapper.xDao接口对应resources/mapper/xDao.xml-->
  33. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  34. <property name="basePackage" value="mapper"></property>
  35. </bean>
  36. <!-- 5.将数据源关联到事务 -->
  37. <bean id="transactionManager"
  38. class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  39. <property name="dataSource" ref="dataSource"></property>
  40. </bean>
  41. <!-- 6.开启事务 -->
  42. <tx:annotation-driven/>
  43. </beans>

web.xml

注意哦 :仔细查看上面的项目结构 创建相应的文件夹

这里也会出现爆红,后面会自己消失

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
  5. http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  6. version="3.1">
  7. <servlet>
  8. <servlet-name>springMVC</servlet-name>
  9. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  10. <init-param>
  11. <param-name>contextConfigLocation</param-name>
  12. <param-value>classpath:spring/spring.xml</param-value>
  13. </init-param>
  14. <load-on-startup>1</load-on-startup>
  15. <async-supported>true</async-supported>
  16. </servlet>
  17. <servlet-mapping>
  18. <servlet-name>springMVC</servlet-name>
  19. <url-pattern>/</url-pattern>
  20. </servlet-mapping>
  21. </web-app>

实体类

  • @Data 是lombok的注解

Product

  1. /**
  2. * @author : look-word
  3. * 2022-07-17 10:12
  4. **/
  5. @Data
  6. public class Product implements Serializable {
  7. private Integer id;
  8. private String product_name;
  9. private Integer stock;
  10. private Integer version;
  11. }

Order

  1. /**
  2. * @author : look-word
  3. * 2022-07-17 10:12
  4. **/
  5. @Data
  6. public class Order implements Serializable {
  7. private String id;
  8. private Integer pid;
  9. private Integer userid;
  10. }

持久层

ProductMapper

  1. @Mapper
  2. @Component
  3. public interface ProductMapper {
  4. // 查询商品(目的查库存)
  5. @Select("select * from product where id = #{id}")
  6. Product getProduct(@Param("id") int id);
  7. // 减库存
  8. @Update("update product set stock = stock-1 where id = #{id}")
  9. int reduceStock(@Param("id") int id);
  10. }

OrderMapper

  1. @Mapper
  2. @Component
  3. public interface OrderMapper {
  4. // 生成订单
  5. @Insert("insert into `order` (id,pid,userid) values (#{id},#{pid},#{userid})")
  6. int insert(Order order);
  7. }

service

ProductService

  1. /**
  2. * @author : look-word
  3. * 2022-07-17 10:28
  4. **/
  5. public interface ProductService {
  6. // 扣除库存
  7. void reduceStock(Integer id) throws Exception;
  8. }

ProductServiceImpl

  1. /**
  2. * @author : look-word
  3. * 2022-07-17 10:29
  4. **/
  5. @Transactional
  6. @Service
  7. public class ProductServiceImpl implements ProductService {
  8. @Resource
  9. private ProductMapper productMapper;
  10. @Resource
  11. private OrderMapper orderMapper;
  12. @Override
  13. public void reduceStock(Integer id) throws Exception {
  14. // 查询商品库存
  15. Product product = productMapper.getProduct(id);
  16. if (product.getStock() <= 0) {
  17. throw new RuntimeException("库存不足");
  18. }
  19. // 减库存
  20. int i = productMapper.reduceStock(id);
  21. if (i == 1) {
  22. Order order = new Order();
  23. order.setId(UUID.randomUUID().toString());
  24. order.setUserid(1);
  25. order.setPid(id);
  26. Thread.sleep(500);
  27. orderMapper.insert(order);
  28. } else {
  29. throw new RuntimeException("扣除库存失败");
  30. }
  31. }
  32. }

controller

  1. /**
  2. * @author : look-word
  3. * 2022-07-17 10:12
  4. **/
  5. @RestController
  6. public class ProductAction {
  7. @Resource
  8. private ProductService productService;
  9. @GetMapping("product/reduce/{id}")
  10. private Object reduce(@PathVariable Integer id) throws Exception {
  11. productService.reduceStock(id);
  12. return "ok";
  13. }
  14. }

启动测试

  • 点击右侧的maven

还记得我们在pom.xml配置的tomcat的插件吗,我们配置的意思是打包(package)之后会自动运行

  • 在执行打包命令之前,先执行clean命令

  • 执行package命令

测试

  • 浏览器访问
  1. http://localhost:8001/product/reduce/1

访问流程

**注意**

在使用jmeter测试的时候 需要启动两个服务

  • 在启动第一个之后 去修改pom里面的build里面tomcat插件的端口 8002
  • 记得要刷新pom文件,然后再打包启动即可

启动jmeter测试

简单阐述一下:我们会模拟高并发场景下对这个商品的库存进行扣减

  • 这也就会导致一个问题,会出现商品超卖(出现负的库存)
  • 出现的原因: 在同一时间,访问的请求很多。

下载地址

解压双击jmeter.bat启动

创建线程组

  • 这里的线程数量根据自己电脑去设置

创建请求

我们填写红框的内容即可就是访问的地址

  • 我们还需要查看请求的结果 创建结果树 右击会出现

配置好这些之后,点击菜单栏绿色启动标志

  • 会出现弹窗 第一个点yes 第二个点cancel(取消)

去数据库查看

  • 没有启动前数据库的库存

  • 可以看到 出现了 超卖

解决超卖

需要用到 zookeeper集群,搭建的文章

zookeeper分布式锁不需要我们手写去实现,有封装好的依赖,引入即可

  1. <dependency>
  2. <groupId>org.apache.curator</groupId>
  3. <artifactId>curator-recipes</artifactId>
  4. <version>4.2.0</version> <!-- 网友投票最牛逼版本 -->
  5. </dependency>

在控制层中加入分布式锁的逻辑代码

  • 添加了集群的ip
  1. /**
  2. * @author : look-word
  3. * 2022-07-17 10:12
  4. **/
  5. @RestController
  6. public class ProductAction {
  7. @Resource
  8. private ProductService productService;
  9. // 集群ip
  10. private String connectString = "192.168.77.132,192.168.77.131,192.168.77.130";
  11. @GetMapping("product/reduce/{id}")
  12. private Object reduce(@PathVariable Integer id) throws Exception {
  13. // 重试策略 (1000毫秒试1次,最多试3次)
  14. RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
  15. //1.创建curator工具对象
  16. CuratorFramework client = CuratorFrameworkFactory.newClient(connectString, retryPolicy);
  17. client.start();
  18. //2.根据工具对象创建“内部互斥锁”
  19. InterProcessMutex lock = new InterProcessMutex(client, "/product_" + id);
  20. try {
  21. //3.加锁
  22. lock.acquire();
  23. productService.reduceStock(id);
  24. } catch (Exception e) {
  25. if (e instanceof RuntimeException) {
  26. throw e;
  27. }
  28. } finally {
  29. //4.释放锁
  30. lock.release();
  31. }
  32. return "ok";
  33. }
  34. }

启动jmeter去测试,会发现,请求就像排队一样,一个一个出现,数据库也没有超卖现象

  • 可以看到 只有前面的5课请求成功了,我们的库存只有5个
  • 说明我们的分布式锁,已经实现了

springboot版本后续会退出

6 zookeeper实现分布式锁的更多相关文章

  1. zookeeper实现分布式锁服务

    A distributed lock base on zookeeper. zookeeper是hadoop下面的一个子项目, 用来协调跟hadoop相关的一些分布式的框架, 如hadoop, hiv ...

  2. [ZooKeeper.net] 3 ZooKeeper的分布式锁

    基于ZooKeeper的分布式锁 ZooKeeper 里实现分布式锁的基本逻辑: 1.zookeeper中创建一个根节点(Locks),用于后续各个客户端的锁操作. 2.想要获取锁的client都在L ...

  3. 基于 Zookeeper 的分布式锁实现

    1. 背景 最近在学习 Zookeeper,在刚开始接触 Zookeeper 的时候,完全不知道 Zookeeper 有什么用.且很多资料都是将 Zookeeper 描述成一个“类 Unix/Linu ...

  4. zookeeper的分布式锁

    实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...

  5. zookeeper 实现分布式锁安全用法

    zookeeper 实现分布式锁安全用法 标签: zookeeper sessionExpire connectionLoss 分布式锁 背景 ConnectionLoss 链接丢失 SessionE ...

  6. 基于Zookeeper的分布式锁

    实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...

  7. 转载 [ZooKeeper.net] 3 ZooKeeper的分布式锁

    [ZooKeeper.net] 3 ZooKeeper的分布式锁   基于ZooKeeper的分布式锁  源码分享:http://pan.baidu.com/s/1miQCDKk ZooKeeper ...

  8. Redis与Zookeeper实现分布式锁的区别

    Redis实现分布式锁 1.根据lockKey区进行setnx(set not exist,如果key值为空,则正常设置,返回1,否则不会进行设置并返回0)操作,如果设置成功,表示已经获得锁,否则并没 ...

  9. Zookeeper系列四:Zookeeper实现分布式锁、Zookeeper实现配置中心

    一.Zookeeper实现分布式锁 分布式锁主要用于在分布式环境中保证数据的一致性. 包括跨进程.跨机器.跨网络导致共享资源不一致的问题. 1. 分布式锁的实现思路 说明: 这种实现会有一个缺点,即当 ...

  10. 10分钟看懂!基于Zookeeper的分布式锁

    实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...

随机推荐

  1. 斯坦福NLP课程 | 第1讲 - NLP介绍与词向量初步

    作者:韩信子@ShowMeAI,路遥@ShowMeAI,奇异果@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/36 本文地址:http://www. ...

  2. 10个最危险的Linux命令,希望你牢记在心

    点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! 来源:Linux迷链接:https://www.linu ...

  3. Linux进程总结

    一个执着于技术的公众号 进程 进程,是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.它的执行需要系统分配资源创建实体之后,才能进行.举个例子: ...

  4. RestFul和控制器

    RestFul和控制器 控制器Controller 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现. 控制器负责解析用户的请求并将其转换为一个模型. 在Spring MVC中 ...

  5. 1.Docker简介

    Docker是个什么东西 假定您在开发一个项目,您使用的是一台笔记本电脑而且您的开发环境具有特定的配置.其他开发人员身处的环境配置也各有不同.您正在开发的应用依赖于您当前的配置且还要依赖于某些配置文件 ...

  6. GO 语言入门(一)

    GO 语言入门(一) 本文写于 2020 年 1 月 18 日 Go 由 Google 工程师 Robert Griesemer,Rob Pike 和 Ken Thompson 设计的一门编程语言,第 ...

  7. 项目实战:Qt+OpenCV大家来找茬(Qt抓图,穿透应用,识别左右图区别,框选区别,微调位置)

    前言   本项目的出现理由只是笔者的一个念头,于是利用专业Qt和Opencv相关的知识开发一个辅助工具,本文章仅用于Qt和Opencv结合的学习.   Demo演示效果          运行包下载地 ...

  8. maven install resources failed: newPosition < 0: (-1 < 0)

    添加以下代码在 pom.xml 中,具体参阅这里 <build> <plugins> <plugin> <groupId>org.apache.mave ...

  9. 137_Power BI 自定义矩阵复刻Beyondsoft Calendar

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.背景 前两天我们用PBI原生的视觉制作了自定义的热力图,今天我们来复刻一个Beyondsoft Calendar 1. ...

  10. sqlserver 插入 更新 删除 语句中的 output子句

    官方文档镇楼: https://docs.microsoft.com/zh-cn/previous-versions/sql/sql-server-2008/ms177564(v=sql.100) 从 ...