zoukankan      html  css  js  c++  java
  • 使用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地址

  • 相关阅读:
    Python 模块chardet安装 setup.py
    Windows下Python安装lxml
    intellij idea 如何更改比编辑器文本字体和大小
    [转]C#设计模式(8)-Builder Pattern
    [转]C#设计模式(4)-Simple Factory Pattern
    [转]C#委托的异步调用
    [转]浅谈C#中常见的委托
    C# 线程池
    [转]C#中的委托和事件(续)
    C#(.net)中的DllImport
  • 原文地址:https://www.cnblogs.com/lisqiong/p/10223470.html
Copyright © 2011-2022 走看看