一切福田,不離方寸,從心而覓,感無不通。

【SpringBoot框架篇】5.分布式锁的实现方式

1.简介

为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁。

2.为什么要用分布式锁

​​
​​在这里插入图片描述
假设有3个用户对一个秒杀系统的商品点击购买并且该商品的数量只有1件,如果不设置分布式锁的情况,会出现3个人都可能出去购买成功的情况,这种情况是系统不允许的.

例如下面情况,当库存是100的时候,用jmeter模拟100个用户下单,会显示库存一直只减少了1件.
在这里插入图片描述

3.分布式锁的实现方式

3.1.基于 redis的 单线程原子性

3.1.1.redis实现方式

主要依赖redis 的setnx()、expire() 这2个函数实现

方法 描述
setnx(lockkey, 1) 如果方法返回 0,则说明占位失败;如果返回 1,则说明占位成功
expire() 对 lockkey 设置超时时间,为的是避免死锁问题。

3.1.2.JAVA代码实现

3.1.2.1.引入依赖

 

3.1.2.2.配置文件

 

3.1.2.3.Controller层

 

3.1.2.4.模拟测试

启动2个进程
分别启动8080和8081这两个端口

让idea 一个项目能够启动多次
修改idea启动配置,勾选 Allow parallel run
在这里插入图片描述
按默认配置启动8080端口,然后修改启动配置,启动8081端口
在Program arguments处添加 以下配置,然后点击OK,再启动程序

 

在这里插入图片描述

配置nginx

 

使用java多线程模拟100个用户并行操作

 

8080端口扣减数量
在这里插入图片描述
8081端口扣减数量
在这里插入图片描述
可以看到2个进程的扣商品扣减数都是正常的。
也可以使用jmeter模拟并发,jmeter使用请参考我写的 jmeter入门教程

3.2.基于数据库的排它锁

利用主键唯一的特性,如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,当方法执行完毕之后,想要释放锁的话,删除这条数据库记录即可。

下图是商品库存是100件,用jmeter模拟100个用户请求,重数据库扣减库存出现的情况,为了避免这种情况出现,加上分布式锁解决该问题

3.2.1.引入依赖

 

3.2.2.JAVA代码

3.2.2.1.controller

 

3.2.2.2.model

 

 

3.2.2.3.aop切面

 

3.2.2.3.测试代码

压测的地址用的是nginx代理过的,具体配置请参考上面

 

可以看到,在压测5秒,每秒20个请求的情况下,只有3个线程拿到了锁
在这里插入图片描述

3.基于 ZooKeeper 做分布式锁

每个客户端对某个方法加锁时,在 Zookeeper 上与该方法对应的指定节点的目录下,生成一个唯一的临时有序节点。 判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个临时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。

3.1.引入依赖

 

3.2.配置文件

 

3.3.JAVA代码

下面代码中,库存没有放到数据库或者redis中,用内存放着,
有兴趣的同学可以自己放到redis或数据库然后开多个节点测试.

 

4.项目配套代码

gitee代码地址

创作不易,要是觉得我写的对你有点帮助的话,麻烦在gitee上帮我点下 Star

【SpringBoot框架篇】其它文章如下,后续会继续更新。

from:https://blog.csdn.net/ming19951224/article/details/106205332