测试
自动化测试、 黑盒/白盒测试、 性能测试、 单元测试、 集成测试
压力测试 : 正压力 负压力———系统负载(访问量、并发数、吞吐量等)
正压力: 不断增加系统负载,测试不被压垮的极限值
负压力: 系统不是独立存在,而依赖于其支撑的系统/服务,可能是应用,可能是数据库减掉支撑服务,测试系统自身的容错性和健壮性
JMeter
是⼀个提供快捷方便的压力测试的⼯具
下载地址:https://jmeter.apache.org/download_jmeter.cgi
官网文档:http://www.jmeter.com.cn/2747.html
jmeter在windows端的执⾏⼊⼝为 /bin/jmeter.bat
在测试计划下添加线程组,在线程组中添加http请求,在http请求中配置请求参数(使⽤csv⽂件)。
具体的jmeter和csv如下:seckill.csvseckill.jmx
解决方案
事务
Redis的事务是原子操作,exec是触发事务执行的命令
相关命令: multi exec watch discard
//提供调用事务的方法
public boolean hasKey(String key){
return redisTemplate.hasKey(key);
}
public Object execute(SessionCallback sessionCallback){
return redisTemplate.execute(sessionCallback);
}
@Override
public String seckill(String userId, String goodsId){
String start = (String) redisUtil.get(goodsId + "_startTime");
String end = (String) redisUtil.get(goodsId + "_endTime");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date startTime = null,endTime=null;
try {
startTime = format.parse(start);
endTime = format.parse(end);
} catch (Exception e) {
}
//首先判断秒杀状态
if (startTime == null || new Date().before(startTime)) {
return "秒杀还未开始";
}
if (new Date().after(endTime)) {
return "秒杀已结束";
}
//判断用户是否曾经秒杀成功过
if (redisUtil.get(goodsId + "_" + userId) != null) {
return userId+ "用户,每人限购一件";
}
SessionCallback sessionCallback = new SessionCallback() {
@Override
public Object execute(RedisOperations redisOperations) throws DataAccessException {
//编写事务的逻辑
redisOperations.watch(goodsId + "_stockNum");
//判断秒杀商品库存是否充足
int stockNum = (int) redisUtil.get(goodsId + "_stockNum");
if (stockNum < 1) {
return "商品已被秒杀一空";
}
redisOperations.multi();
redisUtil.decr(goodsId + "_stockNum");
redisUtil.set(goodsId + "_" + userId, 1);
return redisOperations.exec();
}
};
redisUtil.execute(sessionCallback);
if (redisUtil.hasKey(goodsId + "_" + userId)) {
return userId + "用户,秒杀成功!";
}
return userId + "用户,秒杀失败!";
}
分布式锁
//提供调用脚本的方法
public Object execute(RedisScript script, List keyList){
return redisTemplate.execute(script,keyList);
}
local userId = KEYS[1];
local goodsId = KEYS[2];
local stockNUmKey = goodsId..'_stockNum';
local orderKey = goodsId..'_'..userId;
--判断是否秒杀成功过
local orderExists = redis.call("get",orderKey);
if(orderExists and tonumber(orderExists)==1) then
return 2;--用2表示已秒杀过
end
local num = redis.call("get",stockNUmKey);
if(num and tonumber(num)<1) then
return 0;--用0表示库存不足
else
redis.call("decr",stockNUmKey);
redis.call("set",orderKey,1);
end
return 1;--秒杀成功
@Override
public String seckillByLua(String userId, String goodsId) {
String start = (String) redisUtil.get(goodsId + "_startTime");
String end = (String) redisUtil.get(goodsId + "_endTime");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date startTime = null, endTime = null;
try {
startTime = format.parse(start);
endTime = format.parse(end);
} catch (Exception e) {
}
//首先判断秒杀状态
if (startTime == null || new Date().before(startTime)) {
return "秒杀还未开始";
}
if (new Date().after(endTime)) {
return "秒杀已结束";
}
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setResultType(Long.class);
//获取lua脚本资源
script.setScriptSource(new ResourceScriptSource(
new ClassPathResource("lua/seckill.lua")
));
List<String> keyList = new ArrayList<>();
keyList.add(userId);
keyList.add(goodsId);
Object result = redisUtil.execute(script, keyList);
String reStr=String.valueOf(result);
if ("0".equals(reStr)){
return "商品已被秒杀一空";
}else if ("2".equals(reStr)){
return userId + "用户,每人限购一件";
}else if ("1".equals(reStr)){
return userId + "用户,秒杀成功!";
}else {
return userId + "用户,秒杀失败!";
}
}