当前位置:首页 > PHP教程 > php高级应用 > 列表

PHP+redis实现的限制抢购防止商品超发功能详解

发布:smiling 来源: PHP粉丝网  添加日期:2021-12-19 11:55:11 浏览: 评论:0 

本文实例讲述了PHP+redis实现的限制抢购防止商品超发功能,分享给大家供大家参考,具体如下:

redis不仅仅是单纯的缓存,它还有一些特殊的功能,在一些特殊场景上很好用,redis中key的原子自增incrby和判断key不存在再写入的setnx方法,可以有效的防止超发。

下面使用两个不同的方式来说明利用redis做商品购买库存数量限制。

业务场景很简单,就是限制抢购5个商品,模拟并发请求抢购商品,每抢购一次对应redis中的key值增加一次,通过判断限购的数量来限制抢购,抢购成功写入成功日志,失败写入失败的信息记录,通过记录的数量来判断是否超发。

文件index.php

  1. <?php 
  2. require_once './myRedis.php'
  3. require_once './function.php'
  4. class sendAward{ 
  5.   public $conf = []; 
  6.   const V1 = 'way1';//版本一 
  7.   const V2 = 'way2';//版本二 
  8.   const AMOUNTLIMIT = 5;//抢购数量限制 
  9.   const INCRAMOUNT = 1;//redis递增数量值 
  10.   //初始化调用对应方法执行商品发放 
  11.   public function __construct($conf,$type){ 
  12.     $this->conf = $conf
  13.     if(emptyempty($type)) 
  14.       return ''
  15.     if($type==self::V1){ 
  16.       $this->way1(self::V1); 
  17.     }elseif($type==self::V2){ 
  18.       $this->way2(self::V2); 
  19.     }else
  20.       return ''
  21.     } 
  22.   } 
  23.   //抢购商品方式一 
  24.   protected function way1($v){ 
  25.     $redis = new myRedis($this->conf);    
  26.     $keyNmae = getKeyName($v); 
  27.     if(!$redis->exists($keyNmae)){ 
  28.       $redis->set($keyNmae,0); 
  29.     } 
  30.     $currAmount = $redis->get($keyNmae); 
  31.     if(($currAmount+self::INCRAMOUNT)>self::AMOUNTLIMIT){ 
  32.       writeLog("没有抢到商品",$v); 
  33.       return
  34.     } 
  35.     $redis->incrby($keyNmae,self::INCRAMOUNT); 
  36.     writeLog("抢到商品",$v); 
  37.   } 
  38.   //抢购商品方式二 
  39.   protected function way2($v){ 
  40.     $redis = new myRedis($this->conf); 
  41.     $keyNmae = getKeyName($v); 
  42.     if(!$redis->exists($keyNmae)){ 
  43.       $redis->setnx($keyNmae,0); 
  44.     } 
  45.     if($redis->incrby($keyNmae,self::INCRAMOUNT) > self::AMOUNTLIMIT){ 
  46.       writeLog("没有抢到商品",$v); 
  47.       return
  48.     } 
  49.     writeLog("抢到商品",$v); 
  50.   } 
  51. //实例化调用对应执行方法 
  52. $type = isset($_GET['v'])?$_GET['v']:'way1'
  53. $conf = [ 
  54.   'host'=>'192.168.0.214','port'=>'6379'
  55.   'auth'=>'test','db'=>2, 
  56. ]; 
  57. new sendAward($conf,$type); 

文件myRedis.php

  1. <?php 
  2. /** 
  3.  * @desc 自定义redis操作类 
  4.  * **/ 
  5. class myRedis{ 
  6.   public $handler = NULL; 
  7.   public function __construct($conf){ 
  8.     $this->handler = new Redis(); 
  9.     $this->handler->connect($conf['host'], $conf['port']); //连接Redis 
  10.     //设置密码 
  11.     if(isset($conf['auth'])){ 
  12.       $this->handler->auth($conf['auth']); //密码验证 
  13.     } 
  14.     //选择数据库 
  15.     if(isset($conf['db'])){ 
  16.       $this->handler->select($conf['db']);//选择数据库2 
  17.     }else
  18.       $this->handler->select(0);//默认选择0库 
  19.     } 
  20.   } 
  21.   //获取key的值 
  22.   public function get($name){ 
  23.     return $this->handler->get($name); 
  24.   } 
  25.   //设置key的值 
  26.   public function set($name,$value){ 
  27.     return $this->handler->set($name,$value); 
  28.   } 
  29.   //判断key是否存在 
  30.   public function exists($key){ 
  31.     if($this->handler->exists($key)){ 
  32.       return true; 
  33.     } 
  34.     return false; 
  35.   } 
  36.   //当key不存在的设置key的值,存在则不设置 
  37.   public function setnx($key,$value){ 
  38.     return $this->handler->setnx($key,$value); 
  39.   } 
  40.   //将key的数值增加指定数值 
  41.   public function incrby($key,$value){ 
  42.     return $this->handler->incrBy($key,$value); 
  43.   } 

文件function.php

  1. <?php 
  2. //获取商品key名称 
  3. function getKeyName($v
  4.   return "send_goods_".$v
  5. //日志写入方法 
  6. function writeLog($msg,$v
  7.   $log = $msg.PHP_EOL; 
  8.   file_put_contents("log/$v.log",$log,FILE_APPEND); 

1.ab工具并发测试way1方法

  1. [root@localhost oversend]# ab -c 100 -n 200 http://192.168.0.213:8083/index.php?v=way1 
  2. This is ApacheBench, Version 2.3 <$Revision: 655654 $> 
  3. Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 
  4. Licensed to The Apache Software Foundation, http://www.apache.org/ 
  5. Benchmarking 192.168.0.213 (be patient) 
  6. Completed 100 requests 
  7. Completed 200 requests 
  8. Finished 200 requests 
  9. Server Software:    nginx 
  10. Server Hostname:    192.168.0.213 
  11. Server Port:      8083 
  12. Document Path:     /index.php?v=way1 
  13. Document Length:    0 bytes 
  14. Concurrency Level:   100 
  15. Time taken for tests:  0.089 seconds 
  16. Complete requests:   200 
  17. Failed requests:    0 
  18. Write errors:      0 
  19. Total transferred:   30600 bytes 
  20. HTML transferred:    0 bytes 
  21. Requests per second:  2243.13 [#/sec] (mean) 
  22. Time per request:    44.581 [ms] (mean) 
  23. Time per request:    0.446 [ms] (mean, across all concurrent requests) 
  24. Transfer rate:     335.16 [Kbytes/sec] received 
  25. Connection Times (ms) 
  26.        min mean[+/-sd] median  max 
  27. Connect:    0  6  2.2   5   17 
  28. Processing:   2  28 16.3   25   55 
  29. Waiting:    1  26 15.2   24   50 
  30. Total:     5  34 16.3   30   60 
  31. Percentage of the requests served within a certain time (ms) 
  32.  50%   30 
  33.  66%   35 
  34.  75%   54 
  35.  80%   56 
  36.  90%   57 
  37.  95%   60 
  38.  98%   60 
  39.  99%   60 
  40.  100%   60 (longest request) 

v1方法日志分析

  1. [root@localhost log]# less -N way1.log  
  2.    1 抢到商品 
  3.    2 抢到商品 
  4.    3 抢到商品 
  5.    4 抢到商品 
  6.    5 抢到商品 
  7.    6 抢到商品 
  8.    7 没有抢到商品 
  9.    8 没有抢到商品 
  10.    9 没有抢到商品 
  11.    10 没有抢到商品 
  12.    11 没有抢到商品 
  13.    12 没有抢到商品 

观察日志发现 抢到商品的记录有6条超过正常的5条,说明超发了

2.ab工具并发测试way2方法

  1. [root@localhost oversend]# ab -c 100 -n 200 http://192.168.0.213:8083/index.php?v=way2 
  2. This is ApacheBench, Version 2.3 <$Revision: 655654 $> 
  3. Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 
  4. Licensed to The Apache Software Foundation, http://www.apache.org/ 
  5. Benchmarking 192.168.0.213 (be patient) 
  6. Completed 100 requests 
  7. Completed 200 requests 
  8. Finished 200 requests 
  9. Server Software:    nginx 
  10. Server Hostname:    192.168.0.213 
  11. Server Port:      8083 
  12. Document Path:     /index.php?v=way2 
  13. Document Length:    0 bytes 
  14. Concurrency Level:   100 
  15. Time taken for tests:  0.087 seconds 
  16. Complete requests:   200 
  17. Failed requests:    0 
  18. Write errors:      0 
  19. Total transferred:   31059 bytes 
  20. HTML transferred:    0 bytes 
  21. Requests per second:  2311.68 [#/sec] (mean) 
  22. Time per request:    43.259 [ms] (mean) 
  23. Time per request:    0.433 [ms] (mean, across all concurrent requests) 
  24. Transfer rate:     350.58 [Kbytes/sec] received 
  25. Connection Times (ms) 
  26.        min mean[+/-sd] median  max 
  27. Connect:    0  6  5.4   5   13 
  28. Processing:   3  31 16.6   30   70 
  29. Waiting:    1  30 16.6   30   70 
  30. Total:     5  37 18.5   32   82 
  31. Percentage of the requests served within a certain time (ms) 
  32.  50%   32 
  33.  66%   41 
  34.  75%   45 
  35.  80%   50 
  36.  90%   68 
  37.  95%   80 
  38.  98%   81 
  39.  99%   82 
  40.  100%   82 (longest request) 

v2方法日志分析

  1. [root@localhost log]# less -N v2.log  
  2. [root@localhost log]# less -N way2.log  
  3.    1 抢到商品 
  4.    2 抢到商品 
  5.    3 抢到商品 
  6.    4 抢到商品 
  7.    5 没有抢到商品 
  8.    6 抢到商品 
  9.    7 没有抢到商品 
  10.    8 没有抢到商品 
  11.    9 没有抢到商品 
  12.    10 没有抢到商品 

总结:观察日志可知抢到商品的日志记录是5条并没有超发,说明利用这种方式可以限制住库存的数量。之所以超发是因为方法一中通过加法来判断限制条件的同时,并发一大,就会越过这个判断条件出现会超发,redis的在这方面就体现优势了。

Tags: PHP+redis PHP商品超发

分享到:

相关文章