测试

自动化测试、 黑盒/白盒测试、 性能测试、 单元测试、 集成测试
压力测试 : 正压力 负压力———系统负载(访问量、并发数、吞吐量等)
正压力: 不断增加系统负载,测试不被压垮的极限值
负压力: 系统不是独立存在,而依赖于其支撑的系统/服务,可能是应用,可能是数据库减掉支撑服务,测试系统自身的容错性和健壮性

JMeter

是⼀个提供快捷方便的压力测试的⼯具
下载地址:https://jmeter.apache.org/download_jmeter.cgi
官网文档:http://www.jmeter.com.cn/2747.html

jmeter在windows端的执⾏⼊⼝为 /bin/jmeter.bat

在测试计划下添加线程组,在线程组中添加http请求,在http请求中配置请求参数(使⽤csv⽂件)。
image.png
image.png
image.png
image.png
具体的jmeter和csv如下:seckill.csvseckill.jmx

解决方案

事务

Redis的事务是原子操作,exec是触发事务执行的命令
相关命令: multi exec watch discard

  1. //提供调用事务的方法
  2. public boolean hasKey(String key){
  3. return redisTemplate.hasKey(key);
  4. }
  5. public Object execute(SessionCallback sessionCallback){
  6. return redisTemplate.execute(sessionCallback);
  7. }
  1. @Override
  2. public String seckill(String userId, String goodsId){
  3. String start = (String) redisUtil.get(goodsId + "_startTime");
  4. String end = (String) redisUtil.get(goodsId + "_endTime");
  5. SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  6. Date startTime = null,endTime=null;
  7. try {
  8. startTime = format.parse(start);
  9. endTime = format.parse(end);
  10. } catch (Exception e) {
  11. }
  12. //首先判断秒杀状态
  13. if (startTime == null || new Date().before(startTime)) {
  14. return "秒杀还未开始";
  15. }
  16. if (new Date().after(endTime)) {
  17. return "秒杀已结束";
  18. }
  19. //判断用户是否曾经秒杀成功过
  20. if (redisUtil.get(goodsId + "_" + userId) != null) {
  21. return userId+ "用户,每人限购一件";
  22. }
  23. SessionCallback sessionCallback = new SessionCallback() {
  24. @Override
  25. public Object execute(RedisOperations redisOperations) throws DataAccessException {
  26. //编写事务的逻辑
  27. redisOperations.watch(goodsId + "_stockNum");
  28. //判断秒杀商品库存是否充足
  29. int stockNum = (int) redisUtil.get(goodsId + "_stockNum");
  30. if (stockNum < 1) {
  31. return "商品已被秒杀一空";
  32. }
  33. redisOperations.multi();
  34. redisUtil.decr(goodsId + "_stockNum");
  35. redisUtil.set(goodsId + "_" + userId, 1);
  36. return redisOperations.exec();
  37. }
  38. };
  39. redisUtil.execute(sessionCallback);
  40. if (redisUtil.hasKey(goodsId + "_" + userId)) {
  41. return userId + "用户,秒杀成功!";
  42. }
  43. return userId + "用户,秒杀失败!";
  44. }

分布式锁

  1. //提供调用脚本的方法
  2. public Object execute(RedisScript script, List keyList){
  3. return redisTemplate.execute(script,keyList);
  4. }
  1. local userId = KEYS[1];
  2. local goodsId = KEYS[2];
  3. local stockNUmKey = goodsId..'_stockNum';
  4. local orderKey = goodsId..'_'..userId;
  5. --判断是否秒杀成功过
  6. local orderExists = redis.call("get",orderKey);
  7. if(orderExists and tonumber(orderExists)==1) then
  8. return 2;--用2表示已秒杀过
  9. end
  10. local num = redis.call("get",stockNUmKey);
  11. if(num and tonumber(num)<1) then
  12. return 0;--用0表示库存不足
  13. else
  14. redis.call("decr",stockNUmKey);
  15. redis.call("set",orderKey,1);
  16. end
  17. 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 + "用户,秒杀失败!";
        }
    }