测试
自动化测试、 黑盒/白盒测试、 性能测试、 单元测试、 集成测试
压力测试 : 正压力 负压力———系统负载(访问量、并发数、吞吐量等)
正压力: 不断增加系统负载,测试不被压垮的极限值
负压力: 系统不是独立存在,而依赖于其支撑的系统/服务,可能是应用,可能是数据库减掉支撑服务,测试系统自身的容错性和健壮性
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);}
@Overridepublic 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() {@Overridepublic 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) thenreturn 2;--用2表示已秒杀过endlocal num = redis.call("get",stockNUmKey);if(num and tonumber(num)<1) thenreturn 0;--用0表示库存不足elseredis.call("decr",stockNUmKey);redis.call("set",orderKey,1);endreturn 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 + "用户,秒杀失败!";
}
}
