- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章SpringBoot中使用redis做分布式锁的方法由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
一.模拟问题 。
最近在公司遇到一个问题,挂号系统是做的集群,比如启动了两个相同的服务,病人挂号的时候可能会出现同号的情况,比如两个病人挂出来的号都是上午2号.这就出现了问题,由于是集群部署的,所以单纯在代码中的方法中加锁是不能解决这种情况的.下面我将模拟这种情况,用redis做分布式锁来解决这个问题. 。
1.新建挂号明细表 。
2.在idea上新建项目 。
下图是创建好的项目结构,上面那个parent项目是其他项目不用管它,和新建的没有关系 。
3.开始创建controller,service,dao(mapper),写好后整体结构如下 。
这里贴上service实现类的代码,主要代码就是这一块:
package com.zk.service.impl; import com.zk.mapper.MzMapper;import com.zk.service.MzService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.HashMap;import java.util.Map; /** * 门诊操作service实现类 * * @author zk * @date 2020-9-9 */@Servicepublic class MzServiceImpl implements MzService { @Autowired private MzMapper mzMapper; @Override public Map<String, Object> gh(String ksdm, String ysdm,String brid) { Map<String,Object> resultMap = new HashMap<>(); int ghxh = 0; //获取当前的挂号序号 Map<String, Object> ghxhMap = mzMapper.getGhxh(ksdm,ysdm); //如果为空,说明还没有人挂这个医生的号,当前是一号 if(ghxhMap == null){ ghxh = 1; }else{ ghxh = (int)ghxhMap.get("GHXH"); ghxh++; } //实际场景中,先获取到ghxh后,还会进行收费等其他操作,这里模拟一下需要耗费时间,为了方便测试出现问题,这里时间设置稍微长一点 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //新增挂号明细记录 mzMapper.addGhmx(ksdm,ysdm,ghxh,brid); resultMap.put("code","200"); resultMap.put("msg","success"); return resultMap; }}
4.进行测试 。
1)清空数据库表 。
2)使用postman发送post请求,假设ksdm=1表示皮肤科,ysdm=1表示医生华佗,brbh=1表示张三,现在张三去医院挂皮肤科华佗医生的号,收费员就会操作系统调用上面写的挂号接口. 。
调用成功后,看看数据库里的数据 。
可以看到张三挂到了华佗医生的第一个号,接着把请求参数的brbh改成2表示李四,李四也去挂华佗医生的号 。
请求成功后查看数据库 。
可以看到李四挂了华佗医生的第二个号.现在就是正常的挂号,没有出现问题. 。
3)postman开第二个请求窗口,两个窗口同时去掉接口进行挂号 。
窗口一模拟张三挂号 。
窗口二模拟李四挂号 。
操作成功后看看数据库 。
结果是张三和李四都挂到了三号,这就出现了线程安全问题. 。
3)使用加锁的方式解决问题 。
方法加上锁之后,测试确实没有问题了,但是实际情况是集群部署的,并不是只有一个服务 。
4)启动两个服务 。
idea中设置允许启动多个实例 。
修改端口号,第一个启动的端口号是8080,这里改成8081 。
5)启动两个服务后再用postman去请求,张三请求8080服务接口 。
李四请求8081接口 。
两个窗口同时请求的时候,再次出现了ghxh相同的情况,在方法上加同步锁不能解决这个问题. 。
二.使用redis做分布式锁解决问题 。
1.首先需要启动一个redis,如果不知道redis怎么安装使用的,可以看我之前写的"redis的安装和使用".因为之前我在虚拟机上安装过了redis,这里直接启动一个单节点redis 。
2.项目的pom.xml文件引入redis依赖 。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>
3.在application.yml中配置redis 。
spring: redis: host: 192.168.1.6 port: 6379
4.新建redis锁操作类 。
package com.zk.util; import org.apache.commons.lang3.StringUtils;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Repository; import java.util.UUID;import java.util.concurrent.TimeUnit; /** * redis锁操作类 * * @author zk * @date 2020-9-10 */@Repositorypublic class RedisLock { private StringRedisTemplate stringredisTemplate; public RedisLock(StringRedisTemplate stringredisTemplate) { this.stringredisTemplate = stringredisTemplate; } /** * 加锁,无阻塞 * 加锁过程必须设置过期时间 * 如果没有设置过期时间,手动释放锁的操作出现问题,那么就发生死锁,锁永远不能被释放. * 加锁和设置过期时间过程必须是原子操作 * 如果加锁后服务宕机或程序崩溃,来不及设置过期时间,同样会发生死锁. * * @param key 锁id * @param expire 过期时间 * @return */ public String tryLock(String key, long expire) { String token = UUID.randomUUID().toString(); //setIfAbsent方法:当key不存在的时候,设置成功并返回true,当key存在的时候,设置失败并返回false //token是对应的value,expire是缓存过期时间 Boolean isSuccess = stringredisTemplate.opsForValue().setIfAbsent(key, token, expire, TimeUnit.MILLISECONDS); if (isSuccess) { return token; } return null; } /** * 加锁,有阻塞 * * @param name 锁名称 * @param expire 锁过期时间 * @param timeout 请求超时时间 * @return */ public String lock(String name, long expire, long timeout) { long startTime = System.currentTimeMillis(); String token; do { token = tryLock(name, expire); if (token == null) { if ((System.currentTimeMillis() - startTime) > (timeout - 50)) { break; } try { //try 50 per sec Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); return null; } } } while (token == null); return token; } /** * 解锁操作 * 解锁必须是解除自己加上的锁 * 试想一个这样的场景,服务A加锁,但执行效率非常慢,导致锁失效后还未执行完,但这时候服务B已经拿到锁了,这时候服务A执行完毕了去解锁, * 把服务B的锁给解掉了,其他服务C、D、E...都可以拿到锁了,这就有问题了. * 加锁的时候我们可以设置唯一value,解锁时判断是不是自己先前的value就行了. * * @param key * @param token * @return */ public boolean unlock(String key, String token) { //解锁时需要先取出key对应的value进行判断是否相等,这也是为什么加锁的时候需要放不重复的值作为value String value = stringredisTemplate.opsForValue().get("name"); if (StringUtils.equals(value, token)) { stringredisTemplate.delete(key); return true; } return false; }}
5.修改业务操作类,用上RedisLock 。
package com.zk.service.impl; import com.zk.mapper.MzMapper;import com.zk.service.MzService;import com.zk.util.RedisLock;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service; import java.util.HashMap;import java.util.Map; /** * 门诊操作service实现类 * * @author zk * @date 2020-9-9 */@Servicepublic class MzServiceImpl implements MzService { @Autowired private MzMapper mzMapper; @Autowired private RedisLock redisLock; @Override public Map<String, Object> gh(String ksdm, String ysdm, String brid) { Map<String, Object> resultMap = new HashMap<>(); int ghxh = 0; //加锁操作 String token = null; token = redisLock.lock("gh", 3000,3500); try { //获取到了锁,执行正常业务 if (token != null) { //获取当前的挂号序号 Map<String, Object> ghxhMap = mzMapper.getGhxh(ksdm, ysdm); //如果为空,说明还没有人挂这个医生的号,当前是一号 if (ghxhMap == null) { ghxh = 1; } else { ghxh = (int) ghxhMap.get("GHXH"); ghxh++; } //实际场景中,先获取到ghxh后,还会进行收费等其他操作,这里模拟一下需要耗费时间,为了方便测试出现问题,这里时间设置稍微长一点 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //新增挂号明细记录 mzMapper.addGhmx(ksdm, ysdm, ghxh, brid); } else { resultMap.put("code", "401"); resultMap.put("msg", "其他窗口正在操作,请稍后再试"); return resultMap; } } finally { //解锁 if (token != null) { boolean gh = redisLock.unlock("gh", token); } } resultMap.put("code", "200"); resultMap.put("msg", "success"); return resultMap; }}
6.再用postman开两个窗口去请求,和上面的操作一样,然后再看数据库的数据 。
问题解决,不会再出现重复的ghxh. 。
总结 。
到此这篇关于SpringBoot中使用redis做分布式锁的方法的文章就介绍到这了,更多相关SpringBoot redis分布式锁内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://blog.csdn.net/hchvhg/article/details/108511239 。
最后此篇关于SpringBoot中使用redis做分布式锁的方法的文章就讲到这里了,如果你想了解更多关于SpringBoot中使用redis做分布式锁的方法的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我在网上搜索但没有找到任何合适的文章解释如何使用 javascript 使用 WCF 服务,尤其是 WebScriptEndpoint。 任何人都可以对此给出任何指导吗? 谢谢 最佳答案 这是一篇关于
我正在编写一个将运行 Linux 命令的 C 程序,例如: cat/etc/passwd | grep 列表 |剪切-c 1-5 我没有任何结果 *这里 parent 等待第一个 child (chi
所以我正在尝试处理文件上传,然后将该文件作为二进制文件存储到数据库中。在我存储它之后,我尝试在给定的 URL 上提供文件。我似乎找不到适合这里的方法。我需要使用数据库,因为我使用 Google 应用引
我正在尝试制作一个宏,将下面的公式添加到单元格中,然后将其拖到整个列中并在 H 列中复制相同的公式 我想在 F 和 H 列中输入公式的数据 Range("F1").formula = "=IF(ISE
问题类似于this one ,但我想使用 OperatorPrecedenceParser 解析带有函数应用程序的表达式在 FParsec . 这是我的 AST: type Expression =
我想通过使用 sequelize 和 node.js 将这个查询更改为代码取决于在哪里 select COUNT(gender) as genderCount from customers where
我正在使用GNU bash,版本5.0.3(1)-发行版(x86_64-pc-linux-gnu),我想知道为什么简单的赋值语句会出现语法错误: #/bin/bash var1=/tmp
这里,为什么我的代码在 IE 中不起作用。我的代码适用于所有浏览器。没有问题。但是当我在 IE 上运行我的项目时,它发现错误。 而且我的 jquery 类和 insertadjacentHTMl 也不
我正在尝试更改标签的innerHTML。我无权访问该表单,因此无法编辑 HTML。标签具有的唯一标识符是“for”属性。 这是输入和标签的结构:
我有一个页面,我可以在其中返回用户帖子,可以使用一些 jquery 代码对这些帖子进行即时评论,在发布新评论后,我在帖子下插入新评论以及删除 按钮。问题是 Delete 按钮在新插入的元素上不起作用,
我有一个大约有 20 列的“管道分隔”文件。我只想使用 sha1sum 散列第一列,它是一个数字,如帐号,并按原样返回其余列。 使用 awk 或 sed 执行此操作的最佳方法是什么? Accounti
我需要将以下内容插入到我的表中...我的用户表有五列 id、用户名、密码、名称、条目。 (我还没有提交任何东西到条目中,我稍后会使用 php 来做)但由于某种原因我不断收到这个错误:#1054 - U
所以我试图有一个输入字段,我可以在其中输入任何字符,但然后将输入的值小写,删除任何非字母数字字符,留下“。”而不是空格。 例如,如果我输入: 地球的 70% 是水,-!*#$^^ & 30% 土地 输
我正在尝试做一些我认为非常简单的事情,但出于某种原因我没有得到想要的结果?我是 javascript 的新手,但对 java 有经验,所以我相信我没有使用某种正确的规则。 这是一个获取输入值、检查选择
我想使用 angularjs 从 mysql 数据库加载数据。 这就是应用程序的工作原理;用户登录,他们的用户名存储在 cookie 中。该用户名显示在主页上 我想获取这个值并通过 angularjs
我正在使用 autoLayout,我想在 UITableViewCell 上放置一个 UIlabel,它应该始终位于单元格的右侧和右侧的中心。 这就是我想要实现的目标 所以在这里你可以看到我正在谈论的
我需要与 MySql 等效的 elasticsearch 查询。我的 sql 查询: SELECT DISTINCT t.product_id AS id FROM tbl_sup_price t
我正在实现代码以使用 JSON。 func setup() { if let flickrURL = NSURL(string: "https://api.flickr.com/
我尝试使用for循环声明变量,然后测试cols和rols是否相同。如果是,它将运行递归函数。但是,我在 javascript 中执行 do 时遇到问题。有人可以帮忙吗? 现在,在比较 col.1 和
我举了一个我正在处理的问题的简短示例。 HTML代码: 1 2 3 CSS 代码: .BB a:hover{ color: #000; } .BB > li:after {
我是一名优秀的程序员,十分优秀!