spring boot学习第八篇:通过spring boot、jedis实现秒单

参考:Redis实现分布式锁的7种方案 - 知乎

1、 准备数据库表,如下SQL表示库存表,有主键ID和库存数量字段

CREATE TABLE `t_stock` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`quantity` bigint(20) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

初始数据id           quantity

              1111       9

2、pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.4</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.hmblogs</groupId><artifactId>hmblogs</artifactId><version>0.0.1-SNAPSHOT</version><name>hmblogs</name><description>hmblogs</description><properties><java.version>8</java.version><druid.version>1.2.8</druid.version><log4jdbc.version>1.16</log4jdbc.version></properties><dependencies><!-- druid数据源驱动 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>${druid.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- mybatis --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--Mysql依赖包--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!--lombok插件--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--监控sql日志--><dependency><groupId>org.bgee.log4jdbc-log4j2</groupId><artifactId>log4jdbc-log4j2-jdbc4.1</artifactId><version>${log4jdbc.version}</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.9</version></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

3、应用配置文件

server:port: 8081servlet.context-path: /#配置数据源
spring:datasource:druid:db-type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpyurl: jdbc:log4jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:eladmin}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=falseusername: ${DB_USER:root}password: ${DB_PWD:123456}

4、StockMapper.xml如下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.hmblogs.backend.dao.StockMapper"><resultMap id="BaseResultMap" type="com.hmblogs.backend.entity.Stock"><id column="id" property="id"/><result column="quantity" property="quantity"/></resultMap><sql id="BaseResultMap">id, quantity</sql><select id="findAll" resultMap="BaseResultMap">select<include refid="BaseResultMap"/>from t_stock</select><select id="findById" resultMap="BaseResultMap"  parameterType="com.hmblogs.backend.entity.Stock">select<include refid="BaseResultMap"/>from t_stockwhere id=#{id}</select><update id="updateStockById" parameterType="com.hmblogs.backend.entity.Stock">update t_stock set quantity=quantity-1 where id=#{id}</update>
</mapper>

5、BackendApplication

package com.hmblogs.backend;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class BackendApplication {public static void main(String[] args) {SpringApplication.run(BackendApplication.class, args);}}

6、Stock.java如下

package com.hmblogs.backend.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;@Data
@TableName("t_stock")
public class Stock {@TableId(value="id", type = IdType.AUTO)private Integer id;private Integer quantity;}

7、StockMapper

package com.hmblogs.backend.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hmblogs.backend.entity.Stock;
import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapper
public interface StockMapper extends BaseMapper<Stock> {List<Stock> findAll();Stock findById(Stock stock);Integer updateStockById(Stock stock);
}

8、OrderController

package com.hmblogs.backend.controller;import com.hmblogs.backend.dao.StockMapper;
import com.hmblogs.backend.entity.Stock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;@RestController
@Slf4j
public class OrderController {@Autowiredprivate StockMapper stockMapper;//总库存private long nKuCuen = 0;//商品key名字private String shangpingKey = "computer_key";//获取锁的超时时间 秒private int timeout = 30 * 1000;@GetMapping("/qiangdan")public List<String> qiangdan() {//抢到商品的用户List<String> shopUsers = new ArrayList<>();//构造很多用户List<String> users = new ArrayList<>();IntStream.range(0, 10000).parallel().forEach(b -> {users.add("神牛-" + b);});//初始化库存nKuCuen = 10;//模拟开抢users.parallelStream().forEach(b -> {String shopUser = qiang(b);if (!StringUtils.isEmpty(shopUser)) {shopUsers.add(shopUser);}});return shopUsers;}/*** 模拟抢单动作** @param b* @return*/private String qiang(String b) {//用户开抢时间long startTime = System.currentTimeMillis();//未抢到的情况下,30秒内继续获取锁while ((startTime + timeout) >= System.currentTimeMillis()) {//商品是否剩余if (nKuCuen <= 0) {break;}Jedis jedisCom = new Jedis("localhost",6379);jedisCom.auth("heming");if (jedisCom.setnx(shangpingKey, b)==1) {//用户b拿到锁log.info("用户{}拿到锁...", b);try {//商品是否剩余if (nKuCuen <= 0) {break;}//模拟生成订单耗时操作,方便查看:神牛-50 多次获取锁记录try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}//抢购成功,商品递减,记录用户nKuCuen -= 1;int id = 1111;Stock stock = new Stock();stock.setId(id);Stock stockDo = stockMapper.findById(stock);Integer quantity = stockDo.getQuantity();if(quantity!=null && quantity>0){stockMapper.updateStockById(stock);log.info("update success.");}else{log.info("no update.");}//抢单成功跳出log.info("用户{}抢单成功跳出...所剩库存:{}", b, nKuCuen);return b + "抢单成功,所剩库存:" + nKuCuen;} finally {log.info("用户{}释放锁...", b);//释放锁jedisCom.del(shangpingKey, b);}}}return "";}}

9、验证

浏览器访问http://localhost:8081/qiangdan

查看Idea的console内容,

 将console内容拿到notepad++里面搜索

但是,搜索"update success."内容,预期是9次,实际也是9次,符合我的需要,没有让库存变成负数,

 查看数据库表的库存,id为1111的记录的quantity为0,不是1,也不是负数

10、继续第二种纬度的验证 

StockController的代码如下:

package com.hmblogs.backend.controller;import com.hmblogs.backend.dao.StockMapper;
import com.hmblogs.backend.entity.Stock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
import java.util.Date;@RestController
@Slf4j
public class StockController {@Autowiredprivate StockMapper stockMapper;/*** redis test* @return*/@GetMapping(value = "/reduceStock")public void redisTestLock(){log.info("reduceStock");int id = 1111;String key = "reduceStock"+id;String time = new Date().getTime()+"";Jedis jedisCom = new Jedis("localhost",6379);jedisCom.auth("heming");if (jedisCom.setnx(key, time)==1) {log.info("{}生成锁...", time);try{Stock stock = new Stock();stock.setId(id);Stock stockDo = stockMapper.findById(stock);Integer quantity = stockDo.getQuantity();if(quantity!=null && quantity>0){stockMapper.updateStockById(stock);log.info("update success.");}else{log.info("no update.");}} finally {log.info("{}释放锁...", time);//释放锁jedisCom.del(key, time);}}}
}

将库存改为10,验证,通过压测验证有没有保证锁应该有的作用,控制查库存和当不小于0的时候减少库存这2个逻辑的原子性,预期做到,实际做到了。利用APIPost工具做压测,如下图所示

查看server的控制台

 

 然后把这些内容复制到notepad++里面搜索,

搜索reduceStock,有100个结果

搜索"生成锁..."和"释放锁...",都有14次结果,说明jedisCom.setnx(key, time)==1成立的次数有14次。

 搜索"update success.",有10次结果,说明减少库存减少了10次,

此时查看数据库表的库存,发现为0

 搜索"no update.",发现有4个结果,说明有4次获得锁了,但是库存已经是0了,不能再减库存了,库存不能为负的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/241878.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Transformer详解(附代码实现及翻译任务实现)

一&#xff1a;了解背景和动机 阅读Transformer论文&#xff1a; 阅读原始的Transformer论文&#xff1a;“Attention is All You Need”&#xff0c;由Vaswani等人于2017年提出&#xff0c;是Transformer模型的开创性工作。 二&#xff1a;理解基本构建块 注意力机制&#…

源码:Spring常规Bean创建过程

Bean创建过程&#xff1a; 一、版本 5.3.10二、学习内容 Bean创建过程源码三、Bean生命周期 时间轴地址&#xff1a;点击 四、bean创建过程脑图总结 脑图地址&#xff1a;点击 五、源码过程 说明&#xff1a; bean创建入口一般都是通过getBean(xxx);方法进入的&#xf…

C 语言->编译和链接实现原理

✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;橘橙黄又青-CSDN博客 今天学习&#xff1a;浅学编译和链接内部实现原理 前提&#xff1a;本文是在gcc编译环…

「Kafka」Broker篇

「Kafka」Broker篇 主要讲解的是在 Kafka 中是怎么存储数据的&#xff0c;以及 Kafka 和 Zookeeper 之间如何进行数据沟通的。 Kafka Broker 总体工作流程 Zookeeper 存储的 Kafka 信息 启动 Zookeeper 客户端&#xff1a; [atguiguhadoop102 zookeeper-3.5.7]$ bin/zkCli.sh通…

git 删除 submodule 子模块的步骤

实验有效&#xff0c;这里删除了两个 submodule。 1, 执行删除 submodule mkdir tmp1 && cd tmp1 && git clone --recursive ssh://gitaaa.bbb.ccc.git \ && cd ccc/ && git checkout -b abranch_01 \ && git submodule deinit -f…

如何给新华网投稿发稿?新华网的媒体发稿方法步骤

现如今&#xff0c;互联网已经成为了人们获取信息的主要途径&#xff0c;各大媒体网站也成为了发布自己作品的首选平台。其中&#xff0c;新华网作为中国最具影响力的新闻媒体之一&#xff0c;其内容覆盖面广、触及人群众多&#xff0c;因此&#xff0c;能够在新华网上发表文章…

ASEPRITE使用笔记

aseprite学习笔记 快捷键 新建图层后,按快捷键c可以调出画布属性框放大缩小画布快捷键,鼠标滚轮移动画布快捷键,空格ctr+d,取消选取基本概念 软件五个基本区域:菜单栏、工具属性栏、工具栏、图层栏、颜色栏颜色栏分为色板和调色区域注意事项 创造时,需要把输入法调整成应…

Docker:6种网络配置详解浅介

在Docker中&#xff0c;网络配置是一个重要的主题&#xff0c;因为容器需要与其他容器或外部网络进行通信。Docker提供了多种网络模式和配置选项&#xff0c;以便在不同的场景下满足用户的需求。 本文介绍这些网络模式的区别以及配置&#xff0c;相信看完以后你能够掌握Docker网…

【AI】ChatGPT和文心一言那个更好用

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读文章&#xff01; 此篇是【话题达人】序列文章&#xff0c;这一次的话题是《自然语言处理的发展》 文章将以博主的角度进行讲述&#xff0c;理解和水平有限&#xff0c;不足之处&#xff0c;望指正。 目录 背景自我介绍面试题…

在线扒站网PHP源码-在线扒站工具网站源码

源码介绍 这是一款在线的网站模板下载程序&#xff0c;也就是我们常说的扒站工具&#xff0c;利用它我们可以很轻松的将别人的网站模板样式下载下来&#xff0c;这样就可以大大提高我们编写前端的速度了&#xff01;注&#xff1a;扒取的任何站点不得用于商业、违法用途&#…

day3:基于UDP模型的简单文件下载

思维导图 tftp文件下载客户端实现 #include <head.h> #define SER_PORT 69 #define SER_IP "192.168.125.223" int link_file() {int sfdsocket(AF_INET,SOCK_DGRAM,0);if(sfd-1){perror("socket error");return -1;}return sfd; } int filedownloa…

关于java的继承

关于java的继承 我们在上一篇文章中&#xff0c;了解到了封装&#xff0c;我们本篇文章来介绍一下面向对象的第二大特点&#xff0c;继承&#xff0c;还是遵循结合现实生活中的实际情况&#xff0c;理解着去学习&#xff0c;能更好的加深印象&#x1f600;。 一、继承 继承的…

逻辑运算

目录 AND OR NOT Oracle从入门到总裁:https://blog.csdn.net/weixin_67859959/article/details/135209645 逻辑运算可以保证连接多个条件&#xff0c;连接主要使用 AND、OR 、NOT完成 AND 1.查询职位不是办事员&#xff0c;但是工资低于 300 的员工信息 这个范例可以理…

Windows系统字体尺寸学习

调用GetTextMetrics来获得字体尺寸信息, 函数返回设备描述表中当前选定的字体信息&#xff1b; 返回值到TEXTMETRIC类型的结构中&#xff1b; 返回字段值的单位取决于当前设备描述表映射方式&#xff1b;默认映射方式是MM_TEXT&#xff0c;值的单位是像素&#xff1b; 前7个字…

脱离于ASP.NET 和Visual Studio编辑Razor脚本

Razor Pad是一个编辑Razor脚本的工具&#xff0c;脱离于ASP.NET 和Visual Studio。 github地址&#xff1a;GitHub - RazorPad/RazorPad: RazorPad is a quick and simple stand-alone editing environment that allows anyone (even non-developers) to author Razor templat…

Python项目——搞怪小程序(PySide6+Pyinstaller)

1、介绍 使用python编写一个小程序&#xff0c;回答你是猪吗。 点击“是”提交&#xff0c;弹窗并退出。 点击“不是”提交&#xff0c;等待5秒&#xff0c;重新选择。 并且隐藏了关闭按钮。 2、实现 新建一个项目。 2.1、设计UI 使用Qt designer设计一个UI界面&#xff0c…

Three.JS教程1 环境搭建、场景与相机

Three.JS教程1 环境搭建、场景与相机 一、Three.JS简介二、环境搭建1. 开发准备2. 安装 three.js3. 新建文件index.htmlmain.js 4. 关于附加组件5. 启动 三、创建场景1. 场景的概念2. 相机的概念3. 相机的几个相关概念&#xff08;1&#xff09;视点&#xff08;Position&#…

ctfshow php特性(web89-web101)

目录 web89 web90 web91 web92 web93 web94 web95 web96 web97 web98 web99 web100 web101 php特性(php基础知识) web89 <?php include("flag.php"); highlight_file(_FILE_);if(isset($_GET[num])){$num$_GET[num];if(preg_match("/[0-9]/&…

【征服redis2】redis的事务介绍

目录 目录 1.redis事务介绍 2 事务出错的处理 3.Java如何使用redis事务 1.redis事务介绍 在前面我们介绍了redis的几种典型数据结构和应用&#xff0c;本文我们来看一下redis的事务问题。事务也是数据库的重要主题&#xff0c;熟悉关系型数据库的读者应该对事务比较了解&a…

深度学习记录--偏差/方差(bias/variance)

误差问题 拟合神经网络函数过程中会出现两种误差&#xff1a;偏差(bias)和方差(variance) 偏差和误差的区别 欠拟合(underfitting) 当偏差(bias)过大时&#xff0c;如左图&#xff0c;拟合图像存在部分不符合值&#xff0c;称为欠拟合(underfitting) 过拟合(overfitting) …