本文对tp6使用redis队列方式进行商品秒杀,已经用ab压力测试来测试其运行压力【方法比较简单,后续还会用swoole来进行商品秒杀测试】
测试环境:windows10、php、nginx
测试工具:phpstudy、ab压力测试工具、redis、mysql
1、为了测试得更直观,我创建了两个数据表
a、商品表(total为库存):
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for ms_goods -- ---------------------------- DROP TABLE IF EXISTS `ms_goods`; CREATE TABLE `ms_goods` ( `id` int(11) NOT NULL AUTO_INCREMENT, `total` int(255) DEFAULT '50', `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT '0' COMMENT '商品名称', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=69 DEFAULT CHARSET=utf8 -- ---------------------------- -- Records of ms_goods -- ---------------------------- INSERT INTO `ms_goods` VALUES ('15', '125', '50', '华为P30'); INSERT INTO `ms_goods` VALUES ('66', '123', '0', '华为mate30'); INSERT INTO `ms_goods` VALUES ('68', '124', '50', '华为P40');b、秒杀订单表
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for ms_userget -- ---------------------------- DROP TABLE IF EXISTS `ms_userget`; CREATE TABLE `ms_userget` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_no` varchar(125) NOT NULL COMMENT '订单号', `uid` varchar(25) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '0' COMMENT '抢购到商品的用户', `goods_id` int(50) NOT NULL DEFAULT '0' COMMENT '商品id', `goods_title` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '商品名', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=436 DEFAULT CHARSET=utf8;
2、在测试之前,先把秒杀的商品信息放入redis队列里。
我先定义了要秒杀的商品数组;
private $goods_id=[68,66,15]; //这是要秒杀的商品然后用一个方法去按商品id把商品总数数据添加到队列(说实话就是为了方便添加)。
//将商品id对应的数量存入队列中 public function add(){ $redis = new \Redis(); $redis->connect('127.0.0.1', 6399); $redis->auth('alvin'); $goods_info = Db::name('goods')->where('id','in',$this->goods_id)->select()->toArray(); foreach ($goods_info as $key=>$value){ $store = $value['total']; //商品库存 for ($i=1;$i<=$store;$i++){ $redis->lPush('sellkillGoodId:'.$value['id'],$i); } echo '商品'.$value['title'].'添加成功'; } return '添加成功'; }然后执行这个方法,添加完成后,用redis的桌面工具查看,结果如图:
3、然后随便写个生成订单号的方法:
function build_order_no(){ return time().substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8); }
4、开始写一个用户秒杀的功能方法,其中可对上述张表进行操作,订单号调用上面的生成订单方法。
这里为了直观显示,我自动虚拟了请求用户的uid,也更好的看出哪些用户抢到了商品。
function redis_bf(Request $request){ $goods_id = $request->param('goods_id'); $user_id = uniqid('uid_'); $redis = new \Redis(); $redis->connect('127.0.0.1', 6399); $redis->auth('alvin'); if(!in_array($goods_id,$this->goods_id)){ return json(['msg'=>'商品id错误','data'=>[]]); } $user_ids = $redis->sMembers('buyGoodsUserId'.$goods_id); if(in_array($user_id,$user_ids)){ return json(['msg'=>'已拥有该商品,一人只能抢购一件商品','data'=>[]]); } if($redis->rPop('sellkillGoodId:'.$goods_id)){ //将抢购到商品的用户添加到该商品对应的集合中 $redis->sadd('buyGoodsUserId:'.$goods_id,$user_id); //生成订单 减少商品数量等业务 $orderNO=$this->build_order_no(); $Gtitle= Db::name('goods')->field('title')->where('id',$goods_id)->find(); $data=['uid'=>$user_id,'order_no'=>$orderNO,'goods_id'=>$goods_id,'goods_title'=>$Gtitle['title']]; Db::name('userget')->insert($data); //减少商品库存 Db::name('goods')->where('id',$goods_id)->dec('total')->update(); return json(['msg'=>'','data'=>'抢购成功成功']); }else{ Log::channel('binfaTest')->info('抢购失败的用户{user}',['user'=>$user_id]); return json(['msg'=>'抢购失败','data'=>[]]); } }
5、开始ab测试,快捷键win+R,输入cmd,找到输入命令进入ab文件夹,输入命令进行测试
ab -n 2000 -c 500 -k -r http://spider.alvinxiao.cn/swooles/redis_bf?goods_id=66这个脚本命令为请求该链接每秒2000次,500并发同时执行。
-n:请求数
-c:压力测试并发数
-k:保持链接
-r:出错不退出,继续执行
输出为:
This is ApacheBench, Version 2.3 <$Revision: 1879490 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking spider.alvinxiao.cn (be patient) Completed 200 requests Completed 400 requests Completed 600 requests Completed 800 requests Completed 1000 requests Completed 1200 requests Completed 1400 requests Completed 1600 requests Completed 1800 requests Completed 2000 requests Finished 2000 requests #以上为请求总数 Server Software: nginx/1.15.11 Server Hostname: spider.alvinxiao.cn #请求的URL主机名 Server Port: 80 #请求端口 Document Path: /swooles/redis_bf?goods_id=66 #请求路径 Document Length: 38 bytes #HTTP响应数据的正文长度 Concurrency Level: 500 #并发用户数,这是设置的参数之一 Time taken for tests: 9.550 seconds #所有这些请求被处理完成所花费的总时间 单位秒 Complete requests: 2000 #总请求数量,这是设置的参数之一 Failed requests: 1950 ##表示失败的请求数量 (Connect: 0, Receive: 0, Length: 1950, Exceptions: 0) Non-2xx responses: 1598 Keep-Alive requests: 1598 #持续请求数 Total transferred: 618432 bytes #所有请求的响应数据长度总和。包括每个HTTP响应数据的头信息和正文数据的长度 HTML transferred: 246472 bytes #所有请求的响应数据中正文数据的总和,也就是减去了Total transferred中HTTP响应数据中的头信息的长度 Requests per second: 209.43 [#/sec] (mean) #吞吐量,计算公式:Complete requests/Time taken for tests 总请求数/处理完成这些请求数所花费的时间 Time per request: 2387.484 [ms] (mean) #用户平均请求等待时间,计算公式:Time token for tests/(Complete requests/Concurrency Level)。处理完成所有请求数所花费的时间/(总请求数/并发用户数) Time per request: 4.775 [ms] (mean, across all concurrent requests) #服务器平均请求等待时间,计算公式:Time taken for tests/Complete requests,正好是吞吐率的倒数。也可以这么统计:Time per request/Concurrency Level Transfer rate: 63.24 [Kbytes/sec] received #表示这些请求在单位时间内从服务器获取的数据长度,计算公式:Total trnasferred/ Time taken for tests,这个统计很好的说明服务器的处理能力达到极限时,其出口宽带的需求量。 Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 11.2 0 501 Processing: 128 1933 448.4 2007 4895 Waiting: 127 1932 449.5 2007 4894 Total: 128 1933 448.5 2007 4895 Percentage of the requests served within a certain time (ms) 50% 2007 #50%的请求在2007ms内返回 66% 2017 75% 2054 80% 2080 90% 2179 95% 2255 98% 2356 99% 2877 #99%的请求在2877ms内返回 100% 4895 (longest request)
在执行完后,我们回到redis桌面管理工具,和数据表,看看数据是否已经存储和发生改变:
a、redis数据
此时我们可以看到redis里多出了秒杀到手的用户数据列表,66就是我们上面链接传的商品id,50就是商品总数里每个对应一个用户。
b、查看商品表,看看库存是否已经发生改变,有没有为负数。其结果证明是成功的,总数已经变为0.
c、订单表:
6、我们再测试对编号为68这个商品进行秒杀,2秒内发送请求,一次并发为500个:
ab -t 2 -c 500 http://spider.alvinxiao.cn/swooles/redis_bf?goods_id=68输出为:
This is ApacheBench, Version 2.3 <$Revision: 1879490 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking spider.alvinxiao.cn (be patient) Finished 11 requests Server Software: nginx/1.15.11 Server Hostname: spider.alvinxiao.cn Server Port: 80 Document Path: /swooles/redis_bf?goods_id=68 Document Length: 38 bytes Concurrency Level: 500 Time taken for tests: 2.132 seconds Complete requests: 11 #总共完成的请求数 Failed requests: 0 Total transferred: 2288 bytes HTML transferred: 418 bytes Requests per second: 5.16 [#/sec] (mean) Time per request: 96902.909 [ms] (mean) Time per request: 193.806 [ms] (mean, across all concurrent requests) Transfer rate: 1.05 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.5 0 1 Processing: 669 1006 336.1 1022 1758 Waiting: 370 944 386.1 947 1757 Total: 669 1007 335.9 1022 1758 Percentage of the requests served within a certain time (ms) 50% 1001 66% 1085 75% 1194 80% 1194 90% 1332 95% 1758 98% 1758 99% 1758 100% 1758 (longest request)
从上面数据可以看出,2秒的时间内,并发为500时,只有11个请求成功。
再回到数据表看看数据:
a、redis数据
从图中看出编号为68这个商品的数量2秒多的时间里剩余库存为17条:
再看看秒杀抢购到的用户为多少人:
从图可看出,抢到商品的用户为33人。
那么数据表的是否也一样?
b、数据表:
商品表编号为68的这商品,库存剩余17个:
秒杀到的订单表,抢到的用户总数为33个:
从上面的数据表和redis数据队列,可以看出一些不同。
本文由37°5【https://www.alvinxiao.com 】【https://blog.alvinxiao.com】原创,转载请注明来源。请注意原创和打造和谐的网络环境,谢谢!