微服务项目【商品秒杀接口压测及优化】
生成测试用户
将UserUtils工具类导入到zmall-user模块中,运行生成测试用户信息,可根据自身电脑情况来生成用户数量。
UserUtils:
package com.xujie.zmall.utils;import com.alibaba.nacos.common.utils.MD5Utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xujie.zmall.model.User;
import com.xujie.zmall.util.JsonResponseBody;import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;public class UserUtils {private static void createUser(int count) throws Exception {List<User> lst=new ArrayList<User>();//循环添加用户数据for(int i=0;i<count;i++){User user=new User();user.setLoginName("user"+i);user.setUserName("测试用户"+i);user.setPassword(MD5Utils.md5Hex("123456".getBytes()));user.setType(0);user.setMobile((17700000000L+i)+"");user.setEmail("user"+i+"@139.com");user.setIdentityCode((430104199912120000L+i)+"");lst.add(user);}System.out.println("create users");//获取数据库连接Connection conn=getConn();//定义SQLString sql="insert into zmall_user(loginName,userName,password,identityCode,email,mobile,type) values(?,?,?,?,?,?,?)";//执行SQLPreparedStatement ps=conn.prepareStatement(sql);//赋值for (User user : lst){ps.setString(1,user.getLoginName());ps.setString(2,user.getUserName());ps.setString(3,user.getPassword());ps.setString(4,user.getIdentityCode());ps.setString(5,user.getEmail());ps.setString(6,user.getMobile());ps.setInt(7,user.getType());ps.addBatch();}ps.executeBatch();ps.clearParameters();ps.close();conn.close();System.out.println("insert to db");//登录,生成UserTicketString urlString="http://localhost:8010/userLogin";File file=new File("C:\\Users\\xj\\DeskTop\\config.txt");if(file.exists()){file.delete();}RandomAccessFile accessFile=new RandomAccessFile(file,"rw");//设置光标位置accessFile.seek(0);for (User user : lst) {URL url=new URL(urlString);HttpURLConnection co = (HttpURLConnection) url.openConnection();co.setRequestMethod("POST");co.setDoOutput(true);OutputStream out=co.getOutputStream();String params="loginName="+user.getLoginName()+"&password=123456";out.write(params.getBytes());out.flush();InputStream in=co.getInputStream();ByteArrayOutputStream bout=new ByteArrayOutputStream();byte[] buffer=new byte[1024];int len=0;while((len=in.read(buffer))>=0){bout.write(buffer,0,len);}in.close();bout.close();String response=new String(bout.toByteArray());ObjectMapper mapper=new ObjectMapper();JsonResponseBody jsonResponseBody=mapper.readValue(response, JsonResponseBody.class);String token=jsonResponseBody.getData().toString();System.out.println("create token:"+token);accessFile.seek(accessFile.length());accessFile.write(token.getBytes());accessFile.write("\r\n".getBytes());//System.out.println("write to file:"+token);}accessFile.close();System.out.println("over");}private static Connection getConn() throws Exception {String url="jdbc:mysql://localhost:3306/zmall?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&characterEncoding=UTF8";String driver="com.mysql.jdbc.Driver";String username="root";String password="1234";Class.forName(driver);return DriverManager.getConnection(url,username,password);}public static void main(String[] args) throws Exception {createUser(100);}
}
1)必须保证zmall-user模块处于运行状态下,在进行测试用户数据生成操作;
2)注意修改UserUtils中的用户登录接口地址及端口;同时请修改用户登录接口,将生成的token令牌存入响应封装类中;//5.通过UUID生成token令牌并保存到cookie中 String token= UUID.randomUUID().toString().replace("-",""); ... return new JsonResponseBody<>(token);
3)设置生成登录令牌存储位置;
4)修改数据库名、登录账号及密码;
5)设置生成测试用户数量;
jmeter压测
线程组:1000个线程,1秒之内发送,循环1次。测试结果如下:吞吐量为139/s
数据库中的秒杀商品表中的商品出现了库存为负数的问题。
订单表和订单项表中出现了秒杀商品超卖问题。
秒杀接口优化
优化第一步:解决超卖
更新秒杀商品库存的sql语句,只有当库存大于0才能更新库存;修改更新秒杀库存方法updateKillStockById的返回类型为boolean,用于判断是否更新成功。
KillServiceImpl
@Service
public class KillServiceImpl extends ServiceImpl<KillMapper, Kill> implements IKillService {@Transactional@Overridepublic boolean updateKillStockById(Integer pid) {return this.update(new UpdateWrapper<Kill>().setSql("total=total-1").eq("item_id",pid).gt("total",0)); //修改点}
}
OrderServiceImpl
@Transactional
@Override
public JsonResponseBody<?> createKillOrder(User user, Integer pid) {...//4.秒杀商品库存减一boolean flag=killService.updateKillStockById(pid);if(!flag)throw new BusinessException(JsonResponseStatus.STOCK_EMPTY);...//生成秒杀订单等操作return new JsonResponseBody<>();
}
优化第二步:Redis重复抢购
在zmall_order表中将用户ID+商品ID设置为唯一组合索引。
可以再zmall_order表中新增一个商品ID字段,用于与用户ID一起控制用户重复抢购问题。
在RedisService中新增以下两个方法,用于Redis重复抢购的判断操作。
- 根据用户ID和秒杀商品ID为Key,将秒杀订单保存到Redis中;
- 根据用户ID和秒杀商品ID从Redis中获取对应的秒杀商品;
RedisServiceImpl
/*** 将秒杀订单保存到Redis* @param pid 商品ID* @param order 秒杀订单
*/
@Override
public void setKillOrderToRedis(Integer pid, Order order) {redisTemplate.opsForValue().set("order:"+order.getUserId()+":"+pid,order,1800, TimeUnit.SECONDS);
}/*** 根据用户ID和商品ID从Redis中获取秒杀商品,用于重复抢购判断* @param uid 用户ID* @param pid 商品ID* @return 返回Redis中存储的秒杀订单
*/
@Override
public Order getKillOrderByUidAndPid(Integer uid, Integer pid) {return (Order) redisTemplate.opsForValue().get("order:"+uid+":"+pid);
}
这里用户抢购的秒杀订单保存到Redis默认设置是1800秒,即30分钟;可视情况具体调整。
OrderServiceImpl
@Transactional
@Override
public JsonResponseBody<?> createKillOrder(User user, Integer pid) {.../***********在库存判断是否为空之后***********///6.根据秒杀商品ID和用户ID判断是否重复抢购Order order = redisService.getKillOrderByUidAndPid(user.getId(), pid);if(null!=order)throw new BusinessException(JsonResponseStatus.ORDER_REPART);/***********在根据商品ID获取商品之前***********///4.秒杀商品库存减一boolean flag=killService.updateKillStockById(pid);if(!flag)throw new BusinessException(JsonResponseStatus.STOCK_EMPTY);...//生成秒杀订单等操作//重点,重点,重点,在此处将生成的秒杀订单保存到Redis中,用于之后的重复抢购判断redisService.setKillOrderToRedis(pid,order);return new JsonResponseBody<>();
}
此处第二步优化完毕,再次进行JMeter压测,并查看测试情况。
优化第三步:Redis预减库存
商品初始化
将参与秒杀活动且秒杀状态、秒杀活动时间有效的商品推送到Redis中,并对秒杀商品设置超时时间。
超时时间的设定取至于活动结束时间减去活动开始时间的差值,但必须是有效活动时间,也就是当前时间在活动开始时间与结束时间范围之内。
IRedisService
/**
* 设置秒杀商品库存到Redis中
* @param pid 秒杀商品ID
* @param total 秒杀商品数量
* @param expires 秒杀商品存储过期时间
*/
void setKillTotaltoRedis(Integer pid,Integer total,long expires);
RedisServiceImpl
@Override
public void setKillTotaltoRedis(Integer pid, Integer total,long expires) {redisTemplate.opsForValue().set("goods:"+pid,total,expires,TimeUnit.DAYS);
}
OrderController
在zmall-order订单模块中的OrderController类上实现InitializingBean,完成秒杀商品预加载。
@Controller
public class OrderController implements InitializingBean {@Autowiredprivate IRedisService redisService;/*** 秒杀商品初始化* @throws Exception*/@Overridepublic void afterPropertiesSet() throws Exception {List<Kill> list =killService.list(new QueryWrapper<Kill>()//秒杀活动必须是激活状态.eq("is_active", 1)//秒杀活动结束时间必须>=当前时间,小于证明活动已结束.ge("end_time",new Date().toLocaleString()));list.forEach(kill -> {//计算秒杀商品存入Redis的过期时间,此处以天为单位Instant start = kill.getStartTime().toInstant();Instant end = kill.getEndTime().toInstant();long days = Duration.between(start, end).toDays();redisService.setKillTotaltoRedis(kill.getItemId(),kill.getTotal(),days);});}
}
预减库存
第一步:在RedisService中定义库存预减和递增方法。预减方法是在用户抢购商品成功后对商品进行库存预减;递增方法是在高并发情况下Redis库存预减可能会出现负数情况,通过递增方法进行库存回滚为0
IRedisService
/**
* 根据秒杀商品ID实现Redis商品库存递增
* @param pid
* @return
*/
long increment(Integer pid);/**
* 根据秒杀商品ID实现Redis商品库存递减
* @param pid
* @return
*/
long decrement(Integer pid);
RedisServiceImpl
@Override
public long increment(Integer pid) {return redisTemplate.opsForValue().increment("goods:"+pid);
}@Override
public long decrement(Integer pid) {return redisTemplate.opsForValue().decrement("goods:"+pid);
}
第二步:修改订单生成方法,加入Redis库存预减判断
请在Redis重复抢购判断的下面加入Redis库存预减操作。
//7.Redis库存预减
long stock = redisService.decrement(pid);
if(stock<0){redisService.increment(pid);throw new BusinessException(JsonResponseStatus.STOCK_EMPTY);
}
第三步:还原测试数据,重新使用jmeter压测,这时可以发现明显压测效率要提升很多。
但是还是要根据不同电脑配置情况来决定,配置太低,效率也提升不了多少。
相关文章:
微服务项目【商品秒杀接口压测及优化】
生成测试用户 将UserUtils工具类导入到zmall-user模块中,运行生成测试用户信息,可根据自身电脑情况来生成用户数量。 UserUtils: package com.xujie.zmall.utils;import com.alibaba.nacos.common.utils.MD5Utils; import com.fasterxml.j…...
1997. 访问完所有房间的第一天
题目 你需要访问 n 个房间,房间从 0 到 n - 1 编号。同时,每一天都有一个日期编号,从 0 开始,依天数递增。你每天都会访问一个房间。 最开始的第 0 天,你访问 0 号房间。给你一个长度为 n 且 下标从 0 开始 的数组 n…...
通达信交易接口以什么形式执行下单的?
通达信程交易接口 以API形式来执行下单接口,一般不再需要通过接口系统之间进行连接,通过直接调用通达信dll交易函数的方式直接进行交易,包括下单,撤单,查询资金股份、当日委托、当日成交等方面都能很快的执行出来。以a…...
CobaltStrike上线微信通知
CobaltStrike上线微信通知 利用pushplus公众号(每天免费发送200条消息) http://www.pushplus.plus/push1.html 扫码登录后需要复制token 可以测试一下发送一下消息,手机会受到如下消息。可以在微信提示里将消息免打扰关闭(默认…...
喜茶、奈雪的茶“花式”寻生路
配图来自Canva可画 疫情全面开放不少人“阳了又阳”,电解质饮品成为热销品,梨子、橘子、柠檬等水果被卖断货,凉茶、黄桃罐头被抢购一空,喜茶的“多肉大橘”、奈雪的“霸气银耳炖梨”、蜜雪冰城的“棒打鲜橙”、沪上阿姨的“鲜炖整…...
Xstream使用教程
1.Xstream介绍 官网:https://x-stream.github.io/tutorial.html 介绍:XStream 对象序列化和反序列化为 XML的一个JAVA类库。JDK 1.4以上适用。 PS:与JAXB相比,Xstream更好用一些,像XStreamImplicit这种注解,我在JAX…...
【正点原子FPGA连载】第十一章PL SYSMON测量输入模拟电压 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id692450874670 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html 第十一章PL SYSM…...
纷享销客百思特 | 数字化营销赋能企业新增长沙龙圆满落幕
为进一步帮助企业客户实现数字化转型,纷享销客联合百思特管理咨询集团,于2月10日举办 “数字化营销赋能企业新增长”主题沙龙。本次活动以“新变革新增长”为主题,现场30余位制造企业高管齐聚一堂,共同探讨企业如何在当前复杂的宏…...
oracle查看具体表占用空间 oracle查看表属于哪个用户
文章目录前言oracle查看具体表占用空间1、查看表空间总大小、使用率、剩余空间2、查看具体表的占用空间大小3、查看表空间对应日志文件oracle查看表属于哪个用户1、oracle怎么查看表属于哪个用户2、Oracle查询视图所属用户3、Oracle查询存储过程所属用户总结前言 表空间是数据…...
2.Visual Studio下载和安装
Visual Studio 是微软提供的一个集成开发环境(IDE),主要用于为 Windows 系统开发应用程序。Visual Studio 提供了构建 .Net 平台应用程序的一站式服务,可以使用 Visual Studio 开发、调试和运行应用程序。 1、Visual Studio下载 …...
「4」线性代数(期末复习)
🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀 目录 第四章 向量组的线性相关性 &2)向量组的线性相关性 &3)向…...
IDEA中使用tomcat8-maven-plugin插件
第一种方式 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.or…...
2023年妇女节是哪一天 妇女节是2023年几月几日?
2023年妇女节是哪一天是2023年几月几日? 2023年妇女节是2023年3月8日 三八妇女节是国家法定节假日吗? 妇女节不是国家法定节假日,而国家法定节假日包括:元旦、春节、清明节、劳动节、端午节、中秋节、国庆节; 关于三…...
如何运维多集群数据库?58 同城 NebulaGraph Database 运维实践
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SktQW2qn-1676450580889)(https://www-cdn.nebula-graph.com.cn/nebula-website-5.0/images/blogs/58.%20Com%20Inc/58%E5%90%8C%E5%9F%8E_%E7%94%BB%E6%9D%BF%201.jpg)] 图计算业务背景介绍 我们为什…...
尚医通(十四)Spring Cloud GateWay网关 | 跨域 | 权限认证
目录一、网关基本概念1、API网关介绍2、Spring Cloud Gateway3、Spring Cloud Gateway核心概念二、创建service_gateway模块(网关服务)1、创建service_gateway模块2、在pom.xml引入依赖3、编写application.properties配置文件4、编写启动类5、前端端口号…...
PO模式在Selenium中简单实践
初识PO模式 PO(PageObject)是一种设计模式。简单来说就是把一些繁琐的定位方法、元素操作方式等封装到类中,通过类与类之间的调用完成特定操作。 PO被认为是自动化测试项目开发实践的最佳设计模式之一。 在学习PO模式前,可以先…...
KubeSphere
文章目录一、概述二、最小化安装 KubeSphere2.1 前提2.2 安装 nfs 服务器一、概述 KubeSphere是在Kubernetes之上构建的以应用为中心的企业级分布式容器平台,提供简单易用的操作界面以及向导式操作方式,在降低用户使用容器调度平台学习成本的同时&#…...
JAVA基础阶段面试题(关键点)必备
1、简述什么是 JDK、JRE 和 JVM? JDK : 开发工具包JRE : 运行时环境JVM : java虚拟机2、写出Java的四类八种基本数据类?整数 byte short int long小数(浮点) float double布尔 boolean字符 char3、& 和 && 的区别 ?& 符号的左右两边,无…...
Shiro简介
介绍 ApacheShiro 是一个功能强大且易于使用的 Java 安全(权限)框架。Shiro 可以完成:认证、授权、加密、会话管理、与 Web集成、缓存等。借助Shiro 您可以快速轻松地保护任何应用程序一一从最小的移动应用程序到最大的 Web 和企业应用程序。 1.2:为什么要用 shiro 自2003年以…...
cmu 445 poject 3笔记
2022年的任务 https://15445.courses.cs.cmu.edu/fall2022/project3/ task1, 从磁盘读取数据的算子 task2, 聚合和join算子 task3, sort,limit,topn算子,以及sortlimit->TopN优化 leaderboard没做 本文不写代码,只记录遇到的一些思维盲点 Task1 scan…...
CHAPTER 2 Zabbix界面操作
Zabbix界面操作2.1 Zabbix界面操作1.zabbix的web界面安装2.添加监控信息3.查看监控内容4.查看图像2.2 自定义监控与监控报警1.自定义监控1.1 说明1.2 预备知识2.实现自定义监控2.1 自定义语法2.2 agent注册2.3 在server端注册(web操作)2.4 查看监控图形2.3 监控报警1.第三方报警…...
keep-alive的使用-及遇到的问题
被keep-alive包括的的组件,当组件切换是不是走销毁流程,而是缓存起来 keep-alive有三个参数include匹配name名被缓存,exclude匹配name名不会被缓存,max被缓存组件数量 不写,组件默认全部缓存 <keep-alive ><…...
华为OD面试经验分享,尤其注意机试题部分
文章目录招聘流程和背景介绍面试准备机试题目类型和解答技巧在算法部分在操作系统部分面试官提问和答题技巧面试总结和建议推荐一些华为 od 常见的机试题题目:两数之和题目:二叉树的遍历题目:链表反转题目:最大子序和招聘流程和背…...
【Java】String、StringBuffer、StringBuilder的区别
一、String 由 char[] 数组构成,使用了 final 修饰,String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,然后把指针指向新的引用对象,不仅效率低下,而且浪费大量优先的内存空间。 二…...
iOS开发:对Block使用的一次研究总结
在开发中Block是经常使用的,那我们就得知其然,知其所以然。 Block是什么? Block可以封装一个匿名函数为对象,并捕获上下文所需的数据,并传给目标对象在适当的时候回调。我们使用Block的目的其实就是回调传值,那我们去看看Block的底层,再深入了解一下Block。 Block的底…...
Spark 3.1.1 shuffle fetch 导致shuffle错位的问题
背景 最近从数据仓库小组那边反馈了一个问题,一个SQL任务出来的结果不正确,重新运行一次之后就没问题了,具体的SQL如下: select col1,count(1) as cnt from table1 where dt 20230202 group by col1 having count(1) > 1这个问题是偶发…...
2月第2周榜单丨飞瓜数据B站UP主排行榜(哔哩哔哩平台)发布!
飞瓜轻数发布2023年2月6日-2月12日飞瓜数据UP主排行榜(B站平台),通过充电数、涨粉数、成长指数三个维度来体现UP主账号成长的情况,为用户提供B站号综合价值的数据参考,根据UP主成长情况用户能够快速找到运营能力强的B站…...
Jdk19 动态编译 Java源码为 Class 文件
动态编译 Java 源码为 Class一.背景1.Jdk 版本2.需求二.Java 源码动态编译实现1.Maven 依赖2.源码包装类3.Java 文件对象封装类4.文件管理器封装类5.类加载器6.类编译器三.动态编译测试1.普通测试类2.接口实现类3.测试四.用动态编译 Class 替换 SpringBoot 的 Bean(…...
安装 GPU 版本的 tensorflow 完整版本
前言: 之前安装的 CPU 版本的 tensorflow 一直出问题,索性就直接安装 GPU 版本的 tensorflow 了(有了GPU 就不能浪费)。 安装过程: 1)看自己有无 GPU,找到对应 GPU 的版本:任务管理…...
BOM编程-设置地址栏上的URL
<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>设置地址栏上的URL</title> </head> <body> <script> function go(){ // 获…...
网站手机端做排名靠前/哪里做网络推广
为什么80%的码农都做不了架构师?>>> bboss持久层快速入门教程,快速搭建bboss持久层的开发环境,并编写一个简单的实例,实例中涉及以下功能点: 动态启动数据源(基于apache dbcp2连接池组件&#…...
一个互联网公司可以做几个网站/百度提交网址多久才会收录
1. 首先明白什么是类加载器? 顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Jav…...
福州建网站 做网页/2345纯净版推广包
已同步到个人博客,欢迎访问。 总结 最常用的判断方法是Object.prototype.slice.call(),其他判断的方法都有着各种问题: Array.isArray判断数组,需要ES6的支持typeof有各种特殊情况instanceof对于字面量不适用,并且在…...
做模特网站/友情链接的检查方法
内容参数OS Windows 10 x64 browser Firefox 65.0.2 framework Bootstrap 3.3.7 editor Visual Studio Code 1.32.1 typesetting Markdowncode <!DOCTYPE html> <html lang"zh-CN"><head><meta charset&quo…...
bing网站收录/网站优化策略分析论文
简单的问答已经实现了,那么问题也跟着出现了,我不能确定问题一定是"你叫什么名字",也有可能是"你是谁","你叫啥"之类的,这就引出了人工智能中的另一项技术:自然语言处理(NLP) : 大概意思就是 让计算机明白一句话要表达的意思,NLP就相当于计算机在…...
网站 外包方案/怎么创建网站平台
题目描述 小明很喜欢3和5这两个数字,他将能被3或5整除的数叫做美丽数。现在给你一个整数N(1<N<100000),你能告诉小明第N个美丽数是多少吗? 输入 输入包含多组测试数据。每组输入一个整数N(1<N<…...