php(TP 5)+redisによる秒殺買い占め(ユーザ購入回数制限とユーザ購入回数制限なし)(付ソースコード)

56924 ワード

ユーザーの購入回数を制限しない
https://www.cnblogs.com/qqlong/p/9327844.html 参考住所
説明:今回の環境テストはLinuxで実行する
使用する環境、バージョン、ツール
  :lnmp + redis  。
  :Centos 7、nginx 1.7.4、mysql 5.8、 php 7.3nts、 redis 5.0.6
  :ab      

ThinkPHP 5をインストールする.0の最新バージョンインストールRedisキャッシュLNMP Yumインストールmysql 5.8 Yum abテストツールをインストールnginx仮想ドメイン名を構成
ユーザーの購入回数を制限しない
主なコードは次のとおりです.

#!/bin/bash

/**
 * User: hzbskak
 * Date: 2019/12/25 - 16:23
 */

namespace app\index\controller;


use think\Db;
use think\Exception;

class Seckill
{
    public $price = 10;
    public $user_id = 1;
    public $goods_id = 1;
    public $sku_id   = 11;
    public $number = 1;
    private $redis = null;

    public function __construct()
    {
        //      
        //     redis     
        $this->redis=new \Redis();
        $this->redis->connect('127.0.0.1',6379);
    }

    //    
    public function go()
    {
        //     
        if( !$this->userPop())
        {
            $this->insertLog('no users buy');
            return;
        }

        //     
        $count=$this->redis->lpop('goods_store');
        if(!$count){
            $this->insertLog($this->user_id .' error:no store redis');
            return;
        }


            //    
            $order_sn=$this->build_order_no();

            $order_rs = Db::name( 'order')
                ->insert([
                    'order_sn' => $order_sn,
                    'user_id' => $this->user_id,
                    'goods_id' => $this->goods_id,
                    'sku_id' => $this->sku_id,
                    'price' => $this->price
                ]);

            //    
            $store_rs = Db::name( 'store')
                ->where( 'sku_id', $this->sku_id)
                ->setDec( 'number', $this->number);
            if($store_rs){
                $this->insertLog($this->user_id .'       ');
                return;
            }else{
                $this->insertLog($this->user_id .'       ');
                return;
            }

    }

    //       
    //     id
    public function userPop()
    {
        return $this->user_id = $this->redis->rpoplpush( 'user_line_up', 'user_pop_queue');
    }

    public function user()
    {
        dump( $this->redis->lRange( 'user_line_up', 0, -1));
    }

    public function clear()
    {
        dump( $this->redis->flushDB());
    }

    public function make()
    {
        echo $this->goodsStockAddQueue();
        echo $this->userLineUp();
    }

    public function test()
    {
        dump( $this->redis->keys( '*'));

        dump( $this->redis->lLen( 'user_line_up'));
        dump( $this->redis->lLen( 'goods_store'));
    }

    //        $i  user_id
    public function userLineUp()
    {
        //         
        $num = [];
        for ( $a=0;$a<2;$a++)
        {
            for ( $i=1;$i<1001;$i++)
            {
                array_push( $num, $i);
            }
            //     
            shuffle( $num);
        }
        for ( $i=0; $i< count( $num); $i++)
        {
            $this->redis->lPush( 'user_line_up', $num[$i]);
        }
        echo '     :'.$this->redis->lLen( 'user_line_up');
    }


    //         
    public function goodsStockAddQueue()
    {
        $store=500;
        $res=$this->redis->llen('goods_store');
        $count=$store-$res;
        for($i=0;$i<$count;$i++){
            $this->redis->lpush('goods_store',1);
        }
        echo '      :'.$this->redis->llen('goods_store');
    }

    //       
    function build_order_no(){
        return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
    }

    //    
    function insertLog($event,$type=0){
        Db::name( 'log')
            ->insert( [
                'event' => $event,
                'type' => $type
            ]);
    }
}

テスト:
Webページまたはコマンドラインで、次のコードを順番に実行します.( )
http://seckill.ln//index/seckill/clear 
    
true

http://seckill.ln//index/seckill/make  
   
      :500     :2000

http://seckill.ln/index/seckill/test  
   
array(2) {
  [0] => string(11) "goods_store"
  [1] => string(12) "user_line_up"
}
int(2000)
int(500)
int(0)

テストアドレス:
http://seckill.ln/index/seckill/go

コマンドラインに次のように入力します.
[root@localhost ~]# ab -r -n 6000 -c 5000  http://seckill.ln/index/seckill/go
# -n     ,-c      -r        ,    
, ab : socket: Too many open files (24)
エラーを解決するアドレス
実行結果: Failed Requests 5256, , , 。 , return。 Time taken for tests: 15.576 seconds Requests per second: 385.20 [#/sec] (mean)
[root@localhost ~]# ab -r -n 6000 -c 5000  http://seckill.ln/index/seckill/go
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking seckill.ln (be patient)
Completed 600 requests
Completed 1200 requests
Completed 1800 requests
Completed 2400 requests
Completed 3000 requests
Completed 3600 requests
Completed 4200 requests
Completed 4800 requests
Completed 5400 requests
Completed 6000 requests
Finished 6000 requests


Server Software:        nginx
Server Hostname:        seckill.ln
Server Port:            80

Document Path:          /index/seckill/go
Document Length:        0 bytes

Concurrency Level:      5000
Time taken for tests:   15.576 seconds
Complete requests:      6000
Failed requests:        5087
   (Connect: 0, Receive: 0, Length: 5087, Exceptions: 0)
Write errors:           0
Non-2xx responses:      5087
Total transferred:      1771965 bytes
HTML transferred:       864790 bytes
Requests per second:    385.20 [#/sec] (mean)
Time per request:       12980.180 [ms] (mean)
Time per request:       2.596 [ms] (mean, across all concurrent requests)
Transfer rate:          111.09 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0  168 575.7      0    3011
Processing:     1  725 2051.4     13   15316
Waiting:        1  725 2051.5     13   15316
Total:          5  893 2186.6     13   15447

Percentage of the requests served within a certain time (ms)
  50%     13
  66%    191
  75%    501
  80%    865
  90%   3504
  95%   5808
  98%   8273
  99%  10239
 100%  15447 (longest request)

データベースのデータが正しいかどうか: 在庫表ih_store:
mysql> select * from ih_store;
+----+----------+--------+--------+-------+
| id | goods_id | sku_id | number | freez |
+----+----------+--------+--------+-------+
|  1 |        1 |     11 |      0 |     0 |
+----+----------+--------+--------+-------+
1 row in set (0.02 sec)
number 0, : number UNSIGNED ,
オーダー表ih_order:
mysql> select  COUNT(*) from ih_order;
+----------+
| COUNT(*) |
+----------+
|      500 |
+----------+
1 row in set (0.02 sec)

これで,ユーザの購入回数を制限しないコードが完了する.
パケットアドレス
データベース: test.sql
/*
 Navicat Premium Data Transfer

 Source Server         : ln
 Source Server Type    : MySQL
 Source Server Version : 50728
 Source Host           : 192.168.238.129:3306
 Source Schema         : test

 Target Server Type    : MySQL
 Target Server Version : 50728
 File Encoding         : 65001

 Date: 26/12/2019 11:59:26
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for ih_goods
-- ----------------------------
DROP TABLE IF EXISTS `ih_goods`;
CREATE TABLE `ih_goods`  (
  `goods_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `cat_id` int(11) NOT NULL,
  `goods_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`goods_id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of ih_goods
-- ----------------------------
INSERT INTO `ih_goods` VALUES (1, 0, '    ');

-- ----------------------------
-- Table structure for ih_log
-- ----------------------------
DROP TABLE IF EXISTS `ih_log`;
CREATE TABLE `ih_log`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `event` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `type` tinyint(4) NOT NULL DEFAULT 0,
  `addtime` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for ih_order
-- ----------------------------
DROP TABLE IF EXISTS `ih_order`;
CREATE TABLE `ih_order`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_sn` char(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `user_id` int(11) NOT NULL,
  `status` int(11) NOT NULL DEFAULT 0,
  `goods_id` int(11) NOT NULL DEFAULT 0,
  `sku_id` int(11) NOT NULL DEFAULT 0,
  `price` float NOT NULL,
  `addtime` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '   ' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for ih_store
-- ----------------------------
DROP TABLE IF EXISTS `ih_store`;
CREATE TABLE `ih_store`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `goods_id` int(11) NOT NULL,
  `sku_id` int(10) UNSIGNED NOT NULL DEFAULT 0,
  `number` int(10) NOT NULL DEFAULT 0,
  `freez` int(11) NOT NULL DEFAULT 0 COMMENT '    ',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '  ' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of ih_store
-- ----------------------------
INSERT INTO `ih_store` VALUES (1, 1, 11, 500, 0);

SET FOREIGN_KEY_CHECKS = 1;