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 相关文章推荐
图书管理程序(二)
Oct 09 PHP
MySql 按时间段查询数据方法(实例说明)
Nov 02 PHP
PHP无刷新上传文件实现代码
Sep 19 PHP
解析php中的escape函数
Jun 29 PHP
强制PHP命令行脚本单进程运行的方法
Apr 15 PHP
php判断表是否存在的方法
Jun 18 PHP
dvwa+xampp搭建显示乱码的问题及解决方案
Aug 23 PHP
php命令行(cli)模式下报require 加载路径错误的解决方法
Nov 23 PHP
php正则表达式验证(邮件地址、Url地址、电话号码、邮政编码)
Mar 14 PHP
详解PHP的Yii框架的运行机制及其路由功能
Mar 17 PHP
详解Yii2高级版引入bootstrap.js的一个办法
Mar 21 PHP
CodeIgniter框架数据库基本操作示例
May 24 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
深入了解php4(2)--重访过去
2006/10/09 PHP
PHPCMS忘记后台密码的解决办法
2016/10/30 PHP
php设计模式之抽象工厂模式分析【星际争霸游戏案例】
2020/01/23 PHP
使用jquery给input和textarea设定ie中的focus
2008/05/29 Javascript
javascript 定义初始化数组函数
2009/09/07 Javascript
js 第二代身份证号码的验证机制代码
2011/05/12 Javascript
你需要知道的10个最佳javascript开发实践小结
2012/04/15 Javascript
JavaScript开发人员的10个关键习惯小结
2014/12/05 Javascript
微信小程序 实战实例开发流程详细介绍
2017/01/05 Javascript
axios学习教程全攻略
2017/03/26 Javascript
基于react框架使用的一些细节要点的思考
2017/05/31 Javascript
谈谈VUE种methods watch和compute的区别和联系
2017/08/01 Javascript
select自定义小三角样式代码(实用总结)
2017/08/18 Javascript
微信小程序实现图片上传功能实例(前端+PHP后端)
2018/01/10 Javascript
彻底搞懂并解决vue-cli4中图片显示的问题实现
2020/08/31 Javascript
python logging类库使用例子
2014/11/22 Python
Python实现二维有序数组查找的方法
2016/04/27 Python
python 递归遍历文件夹,并打印满足条件的文件路径实例
2017/08/30 Python
Python Sympy计算梯度、散度和旋度的实例
2019/12/06 Python
在spyder IPython console中,运行代码加入参数的实例
2020/04/20 Python
Python延迟绑定问题原理及解决方案
2020/08/04 Python
python连接mysql数据库并读取数据的实现
2020/09/25 Python
美国专业级皮肤病和spa品质护肤品的高级零售网站:SkinCareRx
2017/02/06 全球购物
澳大利亚自然和有机的健康美容产品一站式商店:Ziani Beauty
2017/12/28 全球购物
贝斯特韦斯特酒店集团官网:Best Western
2019/01/03 全球购物
旅游管理本科生求职信
2013/10/14 职场文书
毕业生求职推荐信
2013/11/04 职场文书
开业庆典主持词
2014/03/21 职场文书
优秀求职信
2014/05/29 职场文书
学生会竞选演讲稿怎么写
2014/08/26 职场文书
上班迟到检讨书300字
2014/10/18 职场文书
责任书格式
2015/01/29 职场文书
教师工作证明范本
2015/06/12 职场文书
复制别人的成功真的会成功吗?
2019/10/17 职场文书
JavaScript 反射学习技巧
2021/10/16 Javascript
MySQL实现用逗号进行拼接、以逗号进行分割
2022/12/24 MySQL