模拟秒杀系统

一、模拟秒杀系统

用户Id:userId,随机数生成

商品Id:prodId,固定1001

商品库存:key=SecKill_1001,value=100  【redis缓存】

已秒杀成功用户:key=SecKill_1001_User 【set集合】

@Controller
public class SecKillController {

    @Autowired
    private SecKillService service;

    @ResponseBody
    @RequestMapping(value = "secKill", method = RequestMethod.POST)
    public boolean secKill(String prodId) {
        String userId = new Random().nextInt(50000) + "";
        return service.doSecKillByScript(userId, prodId);
    }
}
@Service
public class SecKillService {

    private Jedis jedis = new Jedis("127.0.0.1", 6379);

    // Lua脚本
    private static String secKillScript = "local userId=KEYS[1];\n" +
            "local prodId=KEYS[2];\n" +
            "local keyStore='SecKill_'..prodId;\n" +
            "local keyUser=keyStore..'_User';\n" +
            "local userExists=redis.call(\"sismember\",keyUser,userId);\n" +
            "if tonumber(userExists)==1 then\n" +
            "\treturn 2;\n" +
            "end\n" +
            "local num=redis.call(\"get\",keyStore);\n" +
            "if tonumber(num)<=0 then\n" +
            "\treturn 0;\n" +
            "else\n" +
            "\tredis.call(\"decr\",keyStore); \n" +
            "\tredis.call(\"sadd\",keyUser,userId); \n" +
            "end\n" +
            "return 1;";

    public boolean doSecKill(String userId, String prodId) {
        String keyStore = "SecKill_" + prodId;
        String keyUser = "SecKill_" + prodId + "_User";
        // 监视库存
        jedis.watch(keyStore);
        // 获取库存
        String store = jedis.get(keyStore);
        // 秒杀未开始
        if (StringUtils.isBlank(store)) {
            System.out.println("秒杀未开始!");
            jedis.close();
            return false;
        }
        // 秒杀成功
        if (jedis.sismember(keyUser, userId)) {
            System.out.println("秒杀成功,不能再参与!");
        }
        // 判断库存
        if (Integer.parseInt(store) <= 0) {
            System.out.println("秒杀结束!");
            jedis.close();
            return false;
        }
        // 秒杀 开启事务——解决超卖问题,但会引发库存遗留问题
        Transaction transaction = jedis.multi();
        transaction.decr(keyStore);
        transaction.sadd(keyUser, userId);
        List<Object> list = transaction.exec();
        if (list == null || list.size() == 0) {
            System.out.println("秒杀失败!");
            jedis.close();
            return false;
        }
        jedis.close();
        System.out.println("秒杀成功!");
        return true;
    }

   // 利用Lua脚本解决超卖和库存遗留问题
    public boolean doSecKillByScript(String userId, String prodId) {
        JedisPool jedisPool = GenerateJedisPool.jedisPool;
        Jedis jedis = jedisPool.getResource();
        String sha1 = jedis.scriptLoad(secKillScript);
        Object result = jedis.evalsha(sha1,2,userId,prodId);
        String s = String.valueOf(result);
        if("0".equals(s)){
            System.err.println("已抢空!");
        }else if("1".equals(s)){
            System.out.println("秒杀成功!");
        }else if("2".equals(s)){
            System.err.println("该用户已抢过!");
        }else {
            System.err.println("抢购异常!");
        }
        jedis.close();
        return true;
    }

    // 连接池
    private static class GenerateJedisPool{
        public static JedisPool jedisPool;
        static {
            JedisPoolConfig poolConfig = new JedisPoolConfig();
            // jedis实例数
            poolConfig.setMaxTotal(1000);
            // 空闲实例数
            poolConfig.setMaxIdle(200);
            // 最大等待毫秒数
            poolConfig.setMaxWaitMillis(10 * 1000);
            // 是否检查jedis可用性
            poolConfig.setTestOnBorrow(true);
            jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
        }
    }
}

二、ab压测工具(Windows10)

Apache下载链接:http://httpd.apache.org/

 

 

 

解压进入bin目录,开启命令窗口即可压测,提前开启redis服务以及web服务,准备好相应数据

ab -c 100 -n 20000 -p E:\post.txt -T application/x-www-form-urlencoded http://localhost:8081/secKill

ps: -c 100 【指定并发数】

-n 20000 【指定请求数】

-T application/x-www-form-urlencoded 【用于POST/ PUT数据的内容类型】

http://localhost:8081/secKill 【目标URL,up是本机测试】

-p E:\post.txt 【-p是指定post请求参数文件位置,如下图】

 

压测结果 

 

缓存结果 

 

服务端结果 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值