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


Posted in PHP onSeptember 19, 2019

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

  • redis不仅仅是单纯的缓存,它还有一些特殊的功能,在一些特殊场景上很好用。redis中key的原子自增incrby和判断key不存在再写入的setnx方法,可以有效的防止超发。
  • 下面使用两个不同的方式来说明利用redis做商品购买库存数量限制。
  • 业务场景很简单,就是限制抢购5个商品,模拟并发请求抢购商品,每抢购一次对应redis中的key值增加一次,通过判断限购的数量来限制抢购,抢购成功写入成功日志,失败写入失败的信息记录,通过记录的数量来判断是否超发。

文件index.php

<?php
require_once './myRedis.php';
require_once './function.php';
class sendAward{
  public $conf = [];
  const V1 = 'way1';//版本一
  const V2 = 'way2';//版本二
  const AMOUNTLIMIT = 5;//抢购数量限制
  const INCRAMOUNT = 1;//redis递增数量值
  //初始化调用对应方法执行商品发放
  public function __construct($conf,$type){
    $this->conf = $conf;
    if(empty($type))
      return '';
    if($type==self::V1){
      $this->way1(self::V1);
    }elseif($type==self::V2){
      $this->way2(self::V2);
    }else{
      return '';
    }
  }
  //抢购商品方式一
  protected function way1($v){
    $redis = new myRedis($this->conf);   
    $keyNmae = getKeyName($v);
    if(!$redis->exists($keyNmae)){
      $redis->set($keyNmae,0);
    }
    $currAmount = $redis->get($keyNmae);
    if(($currAmount+self::INCRAMOUNT)>self::AMOUNTLIMIT){
      writeLog("没有抢到商品",$v);
      return;
    }
    $redis->incrby($keyNmae,self::INCRAMOUNT);
    writeLog("抢到商品",$v);
  }
  //抢购商品方式二
  protected function way2($v){
    $redis = new myRedis($this->conf);
    $keyNmae = getKeyName($v);
    if(!$redis->exists($keyNmae)){
      $redis->setnx($keyNmae,0);
    }
    if($redis->incrby($keyNmae,self::INCRAMOUNT) > self::AMOUNTLIMIT){
      writeLog("没有抢到商品",$v);
      return;
    }
    writeLog("抢到商品",$v);
  }
}
//实例化调用对应执行方法
$type = isset($_GET['v'])?$_GET['v']:'way1';
$conf = [
  'host'=>'192.168.0.214','port'=>'6379',
  'auth'=>'test','db'=>2,
];
new sendAward($conf,$type);

文件myRedis.php

<?php
/**
 * @desc 自定义redis操作类
 * **/
class myRedis{
  public $handler = NULL;
  public function __construct($conf){
    $this->handler = new Redis();
    $this->handler->connect($conf['host'], $conf['port']); //连接Redis
    //设置密码
    if(isset($conf['auth'])){
      $this->handler->auth($conf['auth']); //密码验证
    }
    //选择数据库
    if(isset($conf['db'])){
      $this->handler->select($conf['db']);//选择数据库2
    }else{
      $this->handler->select(0);//默认选择0库
    }
  }
  //获取key的值
  public function get($name){
    return $this->handler->get($name);
  }
  //设置key的值
  public function set($name,$value){
    return $this->handler->set($name,$value);
  }
  //判断key是否存在
  public function exists($key){
    if($this->handler->exists($key)){
      return true;
    }
    return false;
  }
  //当key不存在的设置key的值,存在则不设置
  public function setnx($key,$value){
    return $this->handler->setnx($key,$value);
  }
  //将key的数值增加指定数值
  public function incrby($key,$value){
    return $this->handler->incrBy($key,$value);
  }
}

文件function.php

<?php
//获取商品key名称
function getKeyName($v)
{
  return "send_goods_".$v;
}
//日志写入方法
function writeLog($msg,$v)
{
  $log = $msg.PHP_EOL;
  file_put_contents("log/$v.log",$log,FILE_APPEND);
}

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

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

v1方法日志分析

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

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

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

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

v2方法日志分析

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

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

完整代码github地址

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
使用TinyButStrong模板引擎来做WEB开发
Mar 16 PHP
PHP写UltraEdit插件脚本实现方法
Dec 26 PHP
php按百分比生成缩略图的代码分享
May 10 PHP
Win2003+apache+PHP+SqlServer2008 配置生产环境
Jul 29 PHP
PHP合并静态文件详解
Nov 14 PHP
php遍历删除整个目录及文件的方法
Mar 13 PHP
php实现字符串翻转的方法
Mar 27 PHP
PHP框架性能测试报告
May 08 PHP
laravel创建类似ThinPHP中functions.php的全局函数
Nov 26 PHP
PHP面向对象中new self()与 new static()的区别浅析
Aug 17 PHP
redis+php实现微博(二)发布与关注功能详解
Sep 23 PHP
PHP常用字符串函数用法实例总结
Jun 04 PHP
php+redis实现消息队列功能示例
Sep 19 #PHP
php文件包含的几种方式总结
Sep 19 #PHP
smarty模板的使用方法实例分析
Sep 18 #PHP
PHP MVC框架中类的自动加载机制实例分析
Sep 18 #PHP
PHP切割整数工具类似微信红包金额分配的思路详解
Sep 18 #PHP
php实现多站点共用session实现单点登录的方法详解
Sep 18 #PHP
PHP实现批量修改文件名的方法示例
Sep 18 #PHP
You might like
探讨如何使用SimpleXML函数来加载和解析XML文档
2013/06/07 PHP
PHP采用自定义函数实现遍历目录下所有文件的方法
2014/08/19 PHP
PHP运行模式汇总
2016/11/06 PHP
jQuery 关于伪类选择符的使用说明
2013/04/24 Javascript
seaJs的模块定义和模块加载浅析
2014/06/06 Javascript
js使用post 方式打开新窗口
2015/02/26 Javascript
JS基于面向对象实现的放烟花效果
2015/05/07 Javascript
JavaScript实现DIV层拖动及动态增加新层的方法
2015/05/12 Javascript
详解Bootstrap的iCheck插件checkbox和radio
2016/08/24 Javascript
js/jquery控制页面动态加载数据 滑动滚动条自动加载事件的方法
2017/02/08 Javascript
微信小程序 弹窗自定义实例代码
2017/03/08 Javascript
vue中v-text / v-html使用实例代码详解
2019/04/02 Javascript
vue生命周期的探索
2019/04/03 Javascript
webpack + vue 打包生成公共配置文件(域名) 方便动态修改
2019/08/29 Javascript
javascript中的with语句学习笔记及用法
2020/02/17 Javascript
解决vue net :ERR_CONNECTION_REFUSED报错问题
2020/08/13 Javascript
python实现ping的方法
2015/07/06 Python
基于python脚本实现软件的注册功能(机器码+注册码机制)
2016/10/09 Python
python 对txt中每行内容进行批量替换的方法
2018/07/11 Python
详解通过API管理或定制开发ECS实例
2018/09/30 Python
Python利用字典破解WIFI密码的方法
2019/02/27 Python
python3 打印输出字典中特定的某个key的方法示例
2019/07/06 Python
pytorch查看torch.Tensor和model是否在CUDA上的实例
2020/01/03 Python
Tensorflow 卷积的梯度反向传播过程
2020/02/10 Python
pandas和spark dataframe互相转换实例详解
2020/02/18 Python
自定义Django Form中choicefield下拉菜单选取数据库内容实例
2020/03/13 Python
Iconfont(矢量图标)+iconmoon(图标svg互转)配合javascript实现社交分享系统
2020/04/21 Python
python 数据分析实现长宽格式的转换
2020/05/18 Python
Python 利用argparse模块实现脚本命令行参数解析
2020/12/28 Python
在css3中background-clip属性与background-origin属性的用法介绍
2012/11/13 HTML / CSS
锐步香港官方网上商店:Reebok香港
2020/11/05 全球购物
全球最大运动品牌的男装、女装和童装官方库存商:A&A Sports
2021/01/17 全球购物
工商管理专业实习大学生自我鉴定
2013/09/19 职场文书
八年级物理教学反思
2014/01/19 职场文书
旅游活动总结
2014/08/27 职场文书
SQL注入篇学习之盲注/宽字节注入
2022/03/03 MySQL