简介
上一篇文章我们介绍了悲观锁和乐观锁,Redis(二十一)-Redis的事务冲突(悲观锁和乐观锁)。这篇文章我们接着来学习Redis,本文主要介绍如何通过Redis来实现秒杀,以及如何通过ab工具来模拟并发。
秒杀案例的基本实现
商品秒杀是电商系统中的一个常见场景,就是商家将拿出少量某商品以特价的方式,在有限的时间内进行销售。在秒杀的场景中需要注意的情况是:1. 商品不能超卖,2. 同一个人只能抢一次。所以,
秒杀的基本实现思路有如下七个步骤:
//uid 是用户ID // prodid 是商品ID public boolean doSeckill(String uid, String prodid) { // 1.uid和prodid非空判断 if (StringUtils.isAnyBlank(uid, prodid)) { System.out.println("uid和prodid为空"); return false; } // 2.连接redis Jedis jedis = new Jedis("127.0.0.1", 6379); // 3.拼接key // 3.1. 库存key String skKey = "sk:" + prodid + ":qt"; // 3.2. 秒杀成功用户key String userKey = "sk:" + prodid + ":user"; // 4.获取库存,如果库存null,秒杀还没有开始 String skValue = jedis.get(skKey); if (skValue == null) { System.out.println("秒杀还没开始,请等待"); jedis.close(); return false; } // 5.判断用户是否重复操作 Boolean sismember = jedis.sismember(userKey, uid); if (sismember) { System.out.println("该用户已经抢过一次,请勿重复抢购"); jedis.close(); return false; } // 6. 判断如果商品数量,库存数量小于1,秒杀结束 if (Integer.parseInt(skValue) <= 0) { System.out.println("该商品的库存不足,秒杀失败"); jedis.close(); return false; } // 7. 秒杀过程 // 7.1. 库存-1 jedis.decr(skKey); // 7.2. 用户加入到set集合中 jedis.sadd(userKey, uid); System.out.println("秒杀成功"); return true; }
为了防止某个用户重复抢,所以需要将抢过的用户id放在set 这个数据类型中,在每次请求是需要判断该set中有没有该用户。
为了防止超卖的情况,所以,在每次请求之前都需要查看一下当前的商品数量,如果商品数量等于0则返回库存不足,用户成功抢到商品之后需要将库存-1。
定义的测试接口是:
@RequestMapping("/skill/do") public boolean setandGetValue() { String prodid = "1010"; StringBuilder uidSB = new StringBuilder(""); for (int i = 0; i < 6; i++) { int nextInt = new Random().nextInt(10); uidSB.append(nextInt); } boolean result = seckillService.doSeckill(uidSB.toString(), prodid); return result; }
在redis中设置秒杀商品的数量为10个。设置的命令是:set sk:1010:qt 10
请求地址是:http://10.41.152.17:8080/seckill/skill/do。其中seckill是项目名。单个请求的测试结果如下图所示:
ab工具模拟并发
这里通过Linux的ab工具模拟并发测试,ab工具的安装使用非常简单。
安装ab工具
在联网的情况下只需要通过 yum install httpd-tools
命令即可安装ab工具。
安装完成之后通过ab --help
命令可以查看ab命令的各个参数的含义。
其中最常用的参数是:
-n 最大的请求数 -c 同时最大的并发数 -p postfile 指定post请求的参数 -T 指定请求的content-type
可以通过vim postfile 模拟表单提交参数,以&符号结尾,存放在当前目录,输入内容 prodid=1010& (PS: 在本次测试中实际上没有到)
输入下面的测试命令测试一波,该命令指定了最大请求数是1000个,并发数是100个,10.41.152.17 是你本机的IP地址。
ab -n 1000 -c 100 -p postfile -T 'application/x-www-form-urlencoded' http://10.41.152.17:8080/seckill/skill/do
在测试之前依然通过set sk:1010:qt 10 命令设置10个秒杀商品qt。
执行结果如下三个图片所示:
从上述两个图片可以看出此基本实现过程有如下几个问题:
前面的线程都提示 商品库存不足,秒杀失败,后面的线程还能提示 秒杀成功,检查商品的数量发现商品数量为-6,也就是说出现了超卖的情况。
后面的jedis客户端请求会出现 connect timed out 连接超时的情况。
这两个问题的解决方案会在后续的文章中详细介绍。
总结
本文通过jedis客户端简单实现了秒杀的案例,并通过ab工具模拟了并发测试。但是,由于代码设计考虑不周,会出现超卖以及jedis客户端会出现连接超时的情况。