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了,不能再减库存了,库存不能为负的。
相关文章:

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) ) ENGINEInnoDB DEF…...
NineAi 新版AI系统网站源码 ChatGPT
简介: Nine AI.ChatGPT是基于ChatGPT开发的一个人工智能技术驱动的自然语言处理工具,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,真正像人类一样来聊天交流,甚至能完成撰写邮件、视频脚本、文案、翻译、代码,写论文等任务。 NineAi 新版A…...
开源监控服务一瞥:Prometheus、Grafana、Zabbix、Nagios、Icinga和Open-Falcon
前言 随着信息技术的发展,监控服务在维护系统稳定性和性能方面变得越来越重要。本文将比较一些流行的开源监控服务,以帮助你选择适合你需求的解决方案。 监控服务对比 监控服务特点优势不足性能扩展性安全性Prometheus- 多维度数据模型- 监控容器化环…...

使用WAF防御网络上的隐蔽威胁之扫描器
在网络安全领域,扫描器是用于侦察和识别网络系统漏洞的工具。 它们可以帮助网络管理员识别安全漏洞,也可能被攻击者用来寻找攻击目标。 扫描器的基本概念 定义:扫描器是一种自动化工具,用于探测网络和服务器中的漏洞、开放端口、…...
企业信息安全管理制度
随着信息化程度的日益推进,企业信息的脆弱性也日益暴露。如何规范日趋复杂的信息安全保障体系建设,如何进行信息风险评估保护企业的信息资产不受侵害,已成为当前行业实现信息化运作亟待解决的问题。 一、企业的信息及其安全隐患 在我公司&am…...
Qt中的线程池
Qt中的线程池 目录 1 为什么需要线程池 2 Qt中有哪些方式实现线程池 3 如何通过QThreadPool类实现线程池 4 如何通过QtConcurrent库实现线程池 5 如何通过自定义的方式实现线程池 5 小结 1 为什么需要线程池 线程池是多线程编程中常用的一种技术,可以帮助管理系统中…...
使用Spring Boot集成中间件:Elasticsearch基础->提高篇
使用Spring Boot集成中间件:Elasticsearch基础->提高篇 导言 Elasticsearch是一个开源的分布式搜索和分析引擎,广泛用于构建实时的搜索和分析应用。在本篇博客中,我们将深入讲解如何使用Spring Boot集成Elasticsearch,实现数…...

【Docker】Dockerfile构建最小镜像
🥳🥳Welcome 的Huihuis Code World ! !🥳🥳 接下来看看由辉辉所写的关于Docker的相关操作吧 目录 🥳🥳Welcome 的Huihuis Code World ! !🥳🥳 前言 一.Dockerfile是什么 二.Dock…...
【严重】GitLab 以其他用户身份执行 Slack 命令
漏洞描述 GitLab 是由GitLab公司开发的、基于Git的集成软件开发平台。使用 Slack 命令在 Slack 聊天环境中运行常见的 GitLab 操作。 GitLab 受影响版本中,由于配置Slack/Mattermost 集成时,未正确验证用户身份信息,导致攻击者可以使用其他…...
【卡梅德生物】纳米抗体文库构建
纳米抗体文库构建服务是一项提供定制化纳米抗体文库的服务,旨在满足研究者和生物制药公司对高质量抗体的需求。这项服务通常包括以下主要步骤: 1.抗原设计和制备: -客户提供目标抗原信息,或由服务提供商协助设计抗原。 -抗原制…...
MySQL修炼手册6:子查询入门:在查询中嵌套查询
目录 写在开头1 子查询基础概念1.1 了解子查询的基本概念1.2 子查询与主查询的关系 2 标量子查询详细展开2.1 学会使用标量子查询2.1.1 在SELECT语句中使用2.1.2 在WHERE子句中使用2.1.3 在ORDER BY子句中使用 2.2 标量子查询在条件判断中的应用2.2.1 使用比较运算符2.2.2 使用…...

01章【JAVA开发入门】
计算机基本概念 计算机组成原理 计算机组装 计算机:电子计算机,俗称电脑。是一种能够按照程序运行,自动、高速处理海量数据的现代化智能电子设备。由硬件和软件所组成,没有安装任何软件的计算机称为裸机。常见的形式有台式计算机、…...

ARM day1
一、概念 ARM可以工作的七种模式用户、系统、快中断、中断、管理、终止、未定义ARM核的寄存器个数 37个32位长的寄存器,当前处理器的模式决定着哪组寄存器可操作,且任何模式都可以存取: PC(program counter程序计数器) CPSR(current program…...

ImageNet Classification with Deep Convolutional 论文笔记
✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。 🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心&…...

Spring Boot中加@Async和不加@Async有什么区别?设置核心线程数、设置最大线程数、设置队列容量是什么意思?直接在yml中配置线程池
在 Spring 中,Async 注解用于将方法标记为异步执行的方法。当使用 Async 注解时,该方法将在单独的线程中执行,而不会阻塞当前线程。这使得方法可以在后台执行,而不会影响主线程的执行。 在您提供的代码示例中,a1() 和…...
自动化理论基础(2)—开发语言之Python
一、知识汇总 掌握 Python 编程语言需要具备一定的基础知识和技能,特别是对于从事自动化测试等领域的工程师。以下是掌握 Python 的一些关键方面: 基本语法: 理解 Python 的基本语法,包括变量、数据类型、运算符、条件语句、循环…...
Spark算子(RDD)超细致讲解
SPARK算子(RDD)超细致讲解 map,flatmap,sortBykey, reduceBykey,groupBykey,Mapvalues,filter,distinct,sortBy,groupBy共10个转换算子 (一)转换算子 1、map from pyspark import SparkContext# 创建SparkContext对象 sc Spark…...
转盘寿司(100%用例)C卷 (JavaPythonC++Node.jsC语言)
寿司店周年庆,正在举办优惠活动回馈新老客户。 寿司转盘上总共有n盘寿司,prices[i]是第i盘寿司的价格,如果客户选择了第i盘寿司,寿司店免费赠送客户距离,第i盘寿司最近的下一盘寿司i,前提是prices[j]< prices[i],如果没有满足条件的j,则不赠送寿司。 每个价格的寿司都…...

【python】搭配Miniconda使用VSCode
现在的spyder总是运行出错,启动不了,尝试使用VSCode。 一、在VSCode中使用Miniconda管理的Python环境,可以按照以下步骤进行: a. 确保Miniconda环境已经安装并且正确配置。 b. 打开VSCode,安装Python扩展。 打开VS…...

从购买服务器到部署前端VUE项目
购买 选择阿里云服务器,地址:https://ecs.console.aliyun.com/home。学生会送一个300的满减券,我买了一个400多一年的,用券之后100多点。 使用SSH连接服务器 我选择的是vscode 中SSH工具。 安装一个插件 找到配置文件配置一下…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...

如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...