Redis:抢单预热

前言

在当今的互联网时代,抢单活动已经成为了电商平台、外卖平台等各种电子商务平台中常见的营销手段。通过抢单活动,商家可以吸引大量用户参与,从而提高销量和知名度。然而,抢单活动所带来的高并发请求往往会给系统带来巨大的压力,如何在抢单活动开始前进行预热,以确保系统能够稳定运行,成为了技术人员需要解决的重要问题。

在这篇博客中,我们将深入探讨如何利用Redis技术来进行抢单预热,以应对抢单活动带来的高并发访问压力。我们将介绍Redis的基本概念和特点,以及如何利用Redis来进行缓存预热、数据预加载等操作,从而提高系统的并发处理能力和稳定性。同时,我们也将分享一些实际案例和经验,帮助读者更好地理解和应用Redis技术解决抢单预热的挑战。

通过本文的学习,读者将能够深入了解抢单预热的必要性和原理,掌握利用Redis进行抢单预热的具体方法和技巧,从而为自己的系统应对抢单活动带来的高并发访问压力提供有效的解决方案。让我们一起深入探讨Redis在抢单预热中的应用吧!

一、前期准备

1、新建项目,结构如下

 2、添加依赖
<dependencies><!-- 放在最前面依赖,依据依赖的最短路径原则,将不在使用spring-data中的slf4j,否则会引发冲突--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.3.8</version></dependency><!-- spring data框架,提供了对redis的整合支持,内部支持lettuce以及Jedis客户端--><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>2.5.6</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.29</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.29</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.3.29</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.3</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.32</version></dependency><dependency><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId><version>6.2.2.RELEASE</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.11.2</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.6</version></dependency></dependencies>

这个Maven项目中包含了多个依赖,以下是每个依赖的作用:

  1. logback-classic: 这是logback日志框架的经典模块,用于在应用程序中进行日志记录和管理。

  2. spring-data-redis: 提供了Spring Data框架对Redis的整合支持,包括对lettuce和Jedis客户端的支持,可以方便地使用Redis进行数据操作。

  3. javax.servlet-api: 这是Java Servlet API的依赖,提供了对Servlet的支持,通常在Java Web应用中使用。

  4. spring-webmvc: Spring框架的Web MVC模块,提供了基于MVC架构的Web应用程序开发支持。

  5. spring-jdbc: Spring框架的JDBC模块,提供了对JDBC的封装和支持,用于在Spring应用中进行数据库操作。

  6. spring-tx: Spring框架的事务管理模块,提供了声明式事务管理的支持。

  7. druid: 阿里巴巴开源的数据库连接池,在应用中用于管理数据库连接。

  8. mysql-connector-java: MySQL数据库的JDBC驱动,用于连接MySQL数据库。

  9. lettuce-core: Lettuce是一个高性能的开源Java Redis客户端,用于与Redis进行交互。

  10. lombok: Lombok是一个Java库,可以通过注解的方式来简化Java代码的编写,提高开发效率。

  11. junit: JUnit是一个Java单元测试框架,用于编写和运行自动化的单元测试。

  12. jackson-databind: Jackson是一个流行的Java JSON处理库,jackson-databind模块提供了数据绑定功能,用于将Java对象和JSON数据进行相互转换。

  13. mybatis: MyBatis是一个持久层框架,用于在Java应用中进行数据库操作。

  14. mybatis-spring: MyBatis与Spring框架的整合模块,提供了MyBatis和Spring框架的无缝集成支持。

这些依赖项涵盖了日志记录、Web开发、数据库操作、缓存操作、测试等多个方面,可以满足一个典型的Java应用程序的开发需求。

二、编写 dao

由于代码量太多了,就不一一讲解了,本次案例只是讲重要的怎么预热和减库存。 

1、GoodsDao
public interface GoodsDao {/*** 查询参与活动的商品* @return*/List<Goods> listProduct();/*** 减库存* @param id* @return*/void decrStock(int id);
}

首先要有一个查询库存的方法和一个删减库存的方法。 

三、编写 service

1、OrderService
public interface OrderService {/*** 下单* @param id*/void placeOrder(int id);
}

 当库存放生改变时,我们需要为这写下单的用户添加订单记录。

2、GoodsService
public interface GoodsService {/*** 扣减库存* @param id*/void decrStock(int id);
}

当下单成功后,需要扣减数据库的库存数量。

3、GoodsServiceImpl
@Service
@Slf4j
@RequiredArgsConstructor
@Transactional(rollbackFor = RuntimeException.class)
public class GoodsServiceImpl implements GoodsService {private final GoodsDao goodsDao;private final RedisTemplate<String, Object> redisTemplate;/*** 缓存预热*/@PostConstructpublic void initProductCache() {goodsDao.listProduct().forEach(goods -> {//将商品数量加入redis缓存String key = GoodsEnum.PREFIX.value() + goods.getId();redisTemplate.opsForValue().set(key, goods.getStock(), Duration.ofMinutes(60));});}@Overridepublic void decrStock(int id) {goodsDao.decrStock(id);}
}

这段代码是一个名为GoodsServiceImpl的服务类,使用了Lombok的@RequiredArgsConstructor注解来自动生成构造函数,并且使用了Slf4j来实现日志记录。同时,@Service注解表明这是一个Spring的服务类,@Transactional注解表明这个类中的方法将进行事务管理,并且在遇到RuntimeException时进行回滚。

这个类中有两个成员变量:GoodsDao和RedisTemplate。GoodsDao是一个数据访问对象,用于对商品数据进行持久化操作;而RedisTemplate是Spring提供的用于操作Redis的模板类。

在这个类中,有一个@PostConstruct注解的方法initProductCache(),它在类实例化后会被自动调用。这个方法通过goodsDao.listProduct()获取所有商品,并将它们的库存数量加入到Redis缓存中,以实现商品的缓存预热。对于每个商品,它会将商品的id作为key,库存数量作为value存入Redis,并设置了缓存的有效期为60分钟。

另外,这个类还实现了GoodsService接口,其中包含了decrStock(int id)方法,用于减少商品库存。在这个方法中,它调用了goodsDao.decrStock(id)来实现对商品库存的减少操作。

总的来说,这个类主要负责商品库存的管理,通过缓存预热来提高系统性能,并且在减少商品库存时进行事务管理。

4、OrderServiceImpl

@Service
@Slf4j
@RequiredArgsConstructor
@Transactional
public class OrderServiceImpl implements OrderService {private final RedisTemplate<String, Object> redisTemplate;private final OrderDao orderDao;private final GoodsDao goodsDao;/*** 下单* @param id*/@Overridepublic void placeOrder(int id) {//扣减库存decrCacheStock(id);//生成订单createOrder(id);//同步数据库的库存goodsDao.decrStock(id);}/*** 在缓存中扣减库存* @param id*/private void decrCacheStock(int id) {//扣减库存(原子减),并返回剩余库存量long stock = redisTemplate.opsForValue().decrement(GoodsEnum.PREFIX.value() + id);//如果redis中库存为0,则抛出异常告诉用户已经售罄if(stock < 0) {//在并发时redis扣减后的库存为负数,因此要将redis自增回来redisTemplate.opsForValue().increment(GoodsEnum.PREFIX.value() + id);throw new OrderException(ErrorMessageEnum.SELL_OUT);}}/*** 生成订单* @param gid*/private Order createOrder(int gid) {try {Order order = new Order();//用户IDorder.setUserId(1);//商品IDorder.setGoodsId(gid);//0表示未支付order.setStatus(0);orderDao.save(order);return order;} catch (Exception e) {log.error(e.getMessage(), e);throw new RuntimeException(e);}}
}

 这段代码是一个名为OrderServiceImpl的服务类,同样使用了Lombok的@RequiredArgsConstructor注解来自动生成构造函数,并且使用了@Slf4j来实现日志记录。同时,@Service注解表明这是一个Spring的服务类,@Transactional注解表明这个类中的方法将进行事务管理。

这个类中有三个成员变量:RedisTemplate用于操作Redis缓存,OrderDao用于对订单数据进行持久化操作,GoodsDao用于对商品数据进行持久化操作。

在这个类中,有一个placeOrder(int id)方法,用于处理下单操作。在这个方法中,首先调用了decrCacheStock(int id)方法来扣减商品的库存,然后调用了createOrder(int id)方法来生成订单,最后调用了goodsDao.decrStock(id)方法来同步数据库中的库存信息。

在decrCacheStock(int id)方法中,它使用了RedisTemplate来实现对Redis缓存中商品库存的扣减操作,并且通过判断库存是否小于0来判断商品是否售罄,如果售罄则抛出OrderException异常。

在createOrder(int gid)方法中,它创建了一个订单对象,并将订单信息存入数据库中。如果在存入数据库时出现异常,它会记录错误日志并抛出RuntimeException异常。

总的来说,这个类主要负责处理订单的生成和库存的扣减操作,通过调用RedisTemplate来实现对Redis缓存的操作,并且在数据库操作时进行事务管理。

 

四、编写controller

@RestController
@RequiredArgsConstructor
public class OrderController extends BaseController{private final OrderService orderService;@PostMapping("/seckill")public ResultVO placeOrder(Integer gid) {orderService.placeOrder(2);return success();}
}

这段代码是一个名为OrderController的控制器类,使用了Lombok的@RequiredArgsConstructor注解来自动生成构造函数,并且继承了BaseController。同时,@RestController注解表明这是一个Spring的RESTful控制器类。

在这个类中,有一个成员变量OrderService,用于处理订单相关的业务逻辑。在控制器中,有一个@PostMapping注解的方法placeOrder(Integer gid),用于处理秒杀下单的请求。在这个方法中,它调用了orderService.placeOrder(2)来处理下单操作,并且返回了一个ResultVO对象,通过success()方法来表示操作成功。

总的来说,这个控制器类主要用于处理秒杀下单的请求,通过调用OrderService来实现下单操作,并返回相应的结果。

 五、使用jmeter测试

官网网址:Apache JMeter - Apache JMeter™

去官网下载下来,我们用 jmeter 来测试我们的controller。

 1、jmeter有什么用

JMeter是一个用于进行性能测试的开源工具,它最初是为测试Web应用程序而设计的,但后来扩展到其他测试领域。JMeter的主要用途包括:

  1. 性能测试:JMeter可以模拟多个并发用户对目标系统(如Web服务器、数据库、FTP服务器等)发起请求,以评估系统的性能和稳定性。它可以测量系统在不同负载下的响应时间、吞吐量和并发用户数等指标,帮助开发人员和测试人员发现系统性能方面的问题。

  2. 负载测试:通过模拟大量用户请求,JMeter可以测试系统在高负载情况下的表现,评估系统的承载能力和性能瓶颈,以便确定系统是否能够满足预期的用户需求。负载测试也可以用于验证系统的可伸缩性和稳定性。

  3. 压力测试:JMeter可以模拟系统在正常或异常负载下的表现,以便评估系统在不同压力下的稳定性和可靠性。通过压力测试,可以发现系统在极端情况下可能出现的问题,如内存泄漏、资源竞争等。

  4. 功能测试:除了性能测试,JMeter也可以用于进行功能测试,例如测试网站的登录、注册、搜索等功能,以及测试API的响应等。

总的来说,JMeter是一个功能强大的测试工具,可以帮助开发人员和测试人员进行性能、负载、压力和功能测试,以确保系统能够稳定、高效地运行。

2、测试
1)打开 jmeter ,bin目录下双击ApacheJMeter.jar 运行

运行:

 

 2)添加线程组

3)添加HTTP请求
 
4)添加汇总报告
 
5)填写信息

 添加循环数量,我们的库存中有100个库存,我们执行150次,看会不会出现超卖的情况。还是售完了直接就抛异常。

填写 HTTP 协议

 

请求路径不要写错了,还有就是请求的方式是什么就选择什么。

6)测试结果
 

当运行测试后,售完100个数量之后并没有出现超卖的现象,证明我们的代码就没有写错,并且在售完之后直接提示用户商品已售完。

六、gitee 案例

地址:ch02 · qiuqiu/Redis-study - 码云 - 开源中国 (gitee.com)

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

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

相关文章

linux:查看文件前100行和后100行

查看文件中的前100行 head -n 100 文件名查看文件中的后100行 tail -n 100 文件名

Fiddler抓包看这篇就够了:fiddler设置弱网测试

弱网测试 概念&#xff1a;弱网看字面意思就是网络比较弱&#xff0c;我们通称为信号差&#xff0c;网速慢。 意义&#xff1a;模拟在地铁、隧道、电梯和车库等场景下使用APP &#xff0c;网络会出现延时、中断和超时等情况。 自动化测试相关教程推荐&#xff1a; 2023最新自…

【LeetCode刷题-数组】--18.四数之和

18.四数之和 方法&#xff1a;排序双指针 先对数组进行排序&#xff0c;使用两重循环分别枚举前两个数&#xff0c;然后在两重循环枚举到的数之后使用双指针枚举剩下的两个数 class Solution {public List<List<Integer>> fourSum(int[] nums, int target) {List…

python实战—核心基础3(RGB模式颜色转换器) lv1

目录 一、核心代码解释 二、代码 三、运行截图 一、核心代码解释 1、hex() 函数 参数说明&#xff1a; x -- 10进制整数 返回值&#xff1a; 返回16进制数&#xff0c;以字符串形式表示。 实例&#xff1a; 以下实例展示了 hex 的使用方法&#xff1a; >>>h…

第一次性能测试懵逼了

最近&#xff0c;公司领导让我做下性能方面的竞品对比&#xff0c;作为一个性能测试小白的我&#xff0c;突然接到这样的任务&#xff0c;下意识发出大大的疑问。 整理好心情&#xff0c;内心想着“领导一定是为了考验我&#xff0c;才给我这个任务的”&#xff0c;开始了这一次…

Mysql之聚合函数

Mysql之聚合函数 什么是聚合函数常见的聚合函数GROUP BYWITH ROLLUPHAVINGHAVING与WHERE的对比 总结SQL底层原理 什么是聚合函数 对一组数据进行汇总的函数&#xff0c;但是还是返回一个结果 聚合函数也叫聚集&#xff0c;分组函数 常见的聚合函数 1.AVG(): 求平均值 2.SUM() :…

每日一练 | 华为认证真题练习Day134

1、开启标准STP协议的交换机可能存在哪些端口状态&#xff1f;&#xff08;多选&#xff09; A. Discarding B. Listening C. Disabled D. Forwarding 2、下列路由协议中优先级最高的是&#xff1f; A. Direct B. RIP C. OSPF D. Static 3、参考如图所示的输出结果&…

【双指针】盛水最多的容器

盛水最多的容器 文章目录 盛水最多的容器题目描述算法原理思路一思路二 代码实现Java代码实现C代码实现 题目描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与…

Python+Qt虹膜检测识别

程序示例精选 PythonQt虹膜检测识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonQt虹膜检测识别》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应用推…

C 语言获取文件绝对路径

示例代码 1&#xff0c;不包含根目录绝对路径&#xff1a; #include <stdlib.h> #include <stdio.h>int main(void) {char *fileName "/Dev/test.txt";char *abs_path _fullpath(NULL, fileName, 0);printf("The absolute path is: %s\n", a…

【藏经阁一起读】(77)__《Apache Dubbo3 云原生升级与企业最佳实践》

【藏经阁一起读】&#xff08;77&#xff09; __《Apache Dubbo3 云原生升级与企业最佳实践》 目录 一、Dubbo是什么 二、Dubbo具体提供了哪些核心能力&#xff1f; 三、构建企业级Dubbo微服务 &#xff08;一&#xff09;、创建项目模板 &#xff08;二&#xff09;、将…

【Qt一坑】qt编译出现“常量中有换行符”

在qt编译过程中出现“常量中有换行符”&#xff0c;原因有以下几点&#xff08;qt版本5.14.2&#xff09;&#xff1a; 1.中文编码格式问题&#xff0c;将UTF-8编码格式改成 UTF-8 BOM。 或者使用QtCreator 进行如下设置&#xff08;找到Qt的左边列表里的项目&#xff0c;下的…

​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第12章 信息系统架构设计理论与实践&#xff08;P420~465&#xff09;-思维导图】 课本里章节里所有蓝色字体的思维导图

nginx快速入门及配置文件结构

文章目录 Nginx 简介Nginx 特性Nginx 架构Nginx 相比Apache的优点Nginx 的安装启动、停止和重新加载 Nginx 配置Nginx 配置文件结构Nginx 工作流程 Nginx 简介 Nginx是 HTTP 和反向代理服务器&#xff0c;邮件代理服务器&#xff0c;以及 Igor Sysoev 最初编写的通用 TCP/UDP …

解锁潜力:创建支持Actions接口调用的高级GPTs

如何创建带有Actions接口调用的GPTs 在本篇博客中&#xff0c;我们将介绍如何创建一个带有Actions接口调用的GPTs &#xff0c;以及如何进行配置和使用。我们将以 https://chat.openai.com/g/g-GMrQhe7ka-gptssearch 为例&#xff0c;演示整个过程。 Ps: 数据来源&#xff1a…

Linux:wget后台下载/查看后台任务进度

1. 后台下载 使用wget -b url&#xff1a; wget -b http://cn.wordpress.org/wordpress-3.1-zh_CN.zip后台任务启动后&#xff0c;会返回两段话&#xff0c;第一段返回一个pid&#xff0c;代表这个后台任务的进程&#xff0c;并且我们可以kill掉这个id来终止此次下载&#x…

Vue2系列 -- 组件自动化全局注册(require.context)

参考官网&#xff1a;https://v2.cn.vuejs.org/v2/guide/components-registration.html 1 作用 省略 import 引入组件 省略 在main.js 中注册 实现自动化引入组件 2 自定义文件夹 在 src 下新建一个 components/base 文件夹&#xff0c;用于存放要自动注册的组件 3 在 base…

【Docker】从零开始:1.Docker概述

【Docker】从零开始&#xff1a;1.Docker概述 1.什么是Docker2.为什么要使用Docker3.传统虚拟机技术与Linux容器技术的区别(1).传统虚拟机技术(2).Linux容器 4.Docker的特点一次构建、随处运行a.更快速的应用交付和部署b.更便捷的升级和扩缩容&#xff1a;c.更简单的系统运维d.…

边缘计算是如何为元宇宙提供动力的?

构建元宇宙虚拟世界并不简单&#xff0c;也并不便宜&#xff0c;但是还是有许多大型公司正在转移大量资源来开发他们的元宇宙业务&#xff0c;当然大部分企业注意力都围绕着 VR 耳机、AR 眼镜、触觉手套和其他沉浸式虚拟现实体验所需的可穿戴硬件。虽然这种沉浸式的体验是最终结…

2023.11.20 关于 Spring MVC 详解

目录 MVC 工作流程 Spring MVC 掌握三个功能 创建 Spring MVC 项目 推荐安装插件 EditStarters 安装步骤 使用方法 实现连接功能 基础注解 RequestMapping 指定 GET 和 POST 方法类型 ResponseBody 获取参数 传递 单个 或 多个参数 参数重命名 RequestParam …