PHP商品秒殺功能實現思路分析


https://blog.csdn.net/weixin_41380972/article/details/86242066

 

用戶: 超大量, 正常/壞人

地域: 全國各地 [因為全國各地不同, 因此需要用cdn將服務發送到離用戶最近的那個服務器]

業務流程: [前台]商品展示, 登記, [后台]數據輸入, 數據處理

以下為架構方案 : 

 

分為兩個大層
一 用戶比較關心, 用戶看的見的層

1 商品展示層/頁

2 用戶登記層

二 后台層 / 數據持久化層

1 數據接入層

2 后續處理層

分層詳解
一 頁面狀態

1 商品展示: 秒殺倒計時頁面

2 秒殺進行中: 點擊進入秒殺頁面

3 秒殺活動結束: 提示活動已經結束

從用戶的角度看 : 

 

 

秒殺倒計時和秒殺開始是利用linux定時任務和shell腳本來做

秒殺進行中到秒殺結束是利用php處理對應的服務器程序來做

二 頁面狀態

1 秒殺進行中: 秒殺登記頁面

2 秒殺結束: 秒殺結束頁面

流程圖

 

數據持久化層

數據校驗: 完成對數據 / 用戶驗證 (加密解密算法)

存入nosql隊列: 去重復 / 排序數據 (redis有序集合)

檢測商品最大數量: 提示活動已經結束 (技術標志位)

 

頁面功能

數據持久化:  轉存nosql數據到mysql數據庫

 

干貨

第一層: 商品展示層

知識點: 頁面 / 服務器優化, cdn忘了加速, 隱藏跳轉界面, 狀態切換

linux 頁面切換:


#!/bin/bash
# 看一下我們的shell腳本是否正常執行
date >> /var/www/ms/log.txt
#### 頁面切換 ####

# 刪除index.html秒殺等待頁面
rm -rf '/var/www/ms/first.html'

# 將秒殺開始頁面切換到秒殺等待頁面
cp '/var/www/ms/first_begin.html' '/var/www/ms/first.html'

linux定時任務:
*/1 * * * * root /bin/51miao.sh #執行秒殺頁面的定時任務代碼


第二層: 秒殺進行和秒殺結束切換:

<?php
// 第一步:刪除文件
$file_path = 'first.html';
$f = file_exists($file_path) && unlink($file_path);

$file_path = 'second.html';
$f = file_exists($file_path) && unlink($file_path);


// 第二步:生成自己需要的文件
$file_path = 'first.html';
$myfile = fopen($file_path, "w");

// 獲取文件內容
$file_path = 'first_over.html';
$txt = file_get_contents($file_path);
fwrite($myfile, $txt);

$file_path = 'second.html';
$myfile = fopen($file_path, "w");

fwrite($myfile, $txt);

用戶登記層
知識點:  token加 / 解密, ajax跨域


$(function(){
    //var url = 'http://www.maizi.net/miao%20sha%20prepare/level%203/';
    //var url = 'http://www.miaosha_level3.net/';
    var url = 'http://level3.5ihy.com/';
    //check函數驗證了手機號, 會員
    function check(myphone,mynumber){
        // TODO 添加你的檢驗規則
        // 檢測 手機號
        var myphone_reg = /^(13[0-9]|15[7-9]|153|156|18[7-9])[0-9]{8}$/;
        var myphone_reg_test = myphone_reg.test(myphone);
        if(myphone_reg_test){
            return true;
        }
        alert('你是輸入信息有誤');
        return false;
    }
 
    /****  跨域操作  ****/
    $("#qianggou").click(function(){
        var myphone = $("#myphone").val();
        var mynumber = $("#mynumber").val();
        var data = {'phone':myphone, 'number':mynumber};
        var res = check(myphone,mynumber);
        if(res){
            $.ajax({
                url:url,
                data:data,
                dataType:'jsonp',
                jsonp:'callback',//傳遞給請求處理程序或頁面的,用以獲得jsonp回調函數名的參數名(默認為:callback)
                jsonpCallback:"success_jsonpCallback",//自定義的jsonp回調函數名稱,默認為jQuery自動生成的隨機函數名
                success:function(cc){
                    if(cc.msg=='ok'){
                        $.cookie('miao','ok');
                    }
                },
                error:function(){
                    $.cookie('miao',null);
                },
                timeout:300
            });
        }
    });
    // 檢測是否秒殺了
    var miao = $.cookie('miao');
    //alert(miao);
    if(miao){
        alert('你已經成功登記');
        //$("#qianggou").remove();
    }
});
jquery的cookie封裝

/**
 * Cookie plugin
 * Resources from http://down.liehuo.net
 * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 */
 
/**
 * Create a cookie with the given name and value and other optional parameters.
 *
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Set the value of a cookie.
 * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
 * @desc Create a cookie with all available options.
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Create a session cookie.
 * @example $.cookie('the_cookie', null);
 * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
 *       used when the cookie was set.
 *
 * @param String name The name of the cookie.
 * @param String value The value of the cookie.
 * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
 * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
 *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
 *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
 *                             when the the browser exits.
 * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
 * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
 * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
 *                        require a secure protocol (like HTTPS).
 * @type undefined
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */
 
/**
 * Get the value of a cookie with the given name.
 *
 * @example $.cookie('the_cookie');
 * @desc Get the value of a cookie.
 *
 * @param String name The name of the cookie.
 * @return The value of the cookie.
 * @type String
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */
jQuery.cookie = function(name, value, options) {
    if (typeof value != 'undefined') { // name and value given, set cookie
        options = options || {};
        if (value === null) {
            value = '';
            options.expires = -1;
        }
        var expires = '';
        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
            var date;
            if (typeof options.expires == 'number') {
                date = new Date();
                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
            } else {
                date = options.expires;
            }
            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
        }
        // CAUTION: Needed to parenthesize options.path and options.domain
        // in the following expressions, otherwise they evaluate to undefined
        // in the packed version for some reason...
        var path = options.path ? '; path=' + (options.path) : '';
        var domain = options.domain ? '; domain=' + (options.domain) : '';
        var secure = options.secure ? '; secure' : '';
        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
    } else { // only name given, get cookie
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
};
數據接入層
知識點: 數據校驗, 存入隊列, 商品數量檢測

數據校驗:

<?php
//1 基礎數據准備

header("Access-Control-Allow-Origin: http://www.xxxx.net"); //防止 跨域
$data = array('msg'=>'false');// 初始數據准備
 
//2 庫文件引入
include_once 'lib/Mrgister.class.php';
include_once 'lib/Mredis.class.php';
include_once 'lib/function.php';
 
//3 整理數據 獲取前台數據輸入
$I = new Mrgister();
$I->I();
//獲取前台數據
$value = serialize($I->get_value());
 
//4 數據隊列存儲流程
$Mredis = new Mredis();
if($Mredis->set_vaule($value)=='overflow'){
    sendOver();// 發送處理
}else{
    $data = array('msg'=>'ok');
}
 
//5 返回結果到前台
$callback = $_GET['callback'];
echo $callback.'('.json_encode($data).')';

function.php 


<?php

// 創建token 字符串
function create(){
    $str    = 1111;
    $end    = 9999;
    $salt   = array("L","J","S","H");
    $str    = rand($str,$end);// 5555 // L 23
    $a      = $str.$str%ord($salt[0]);// 5555. 21== 555521-555525-565656-
    $str    = rand($str,$end);
    $b      = $str.$str%ord($salt[1]);
    $str    = rand($str,$end);
    $c      = $str.$str%ord($salt[2]);
    $str    = rand($str,$end);
    $d      = $str.$str%ord($salt[3]);
    return $a.'-'.$b.'-'.$c.'-'.$d;
}
// 驗證字符串
function check($res){
    $flag   = true;
    $salt   = array("L","J","S","H");
    $res    = explode('-',$res);
    foreach($res as $k => $v){
        $v_start    = substr($v,0,4);
        $v_end      = substr($v,4);
        $v_new      = $v_start-$v_end;
        if($v_new%ord($salt[$k])){
            $flag = false;
        }
    }
    return $flag;
}
// 發送通知信息
function sendOver(){
    $url_level1 = 'http://www.miaosha_level1.net/set_file.php';// 修改稱為自己的觸發位置
    $url_level2 = 'http://www.miaosha_level2.net/set_file.php';// 修改稱為自己的觸發位置
    $url_level4 = 'http://www.miaosha_level4.net/set_file.php';// 修改稱為自己的觸發位置
    // 收錄完成
    // 觸發第一層:
    $ch = curl_init();//初始化 GET 方式
    curl_setopt($ch, CURLOPT_URL, $url_level1);//設置選項,包括URL
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_exec($ch);//執行並獲取HTML文檔內容
    curl_close($ch);//釋放curl句柄
    // 觸發第二層:
    $ch = curl_init();//初始化 GET 方式
    curl_setopt($ch, CURLOPT_URL, $url_level2);//設置選項,包括URL
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_exec($ch);//執行並獲取HTML文檔內容
    curl_close($ch);//釋放curl句柄
    // 觸發第四層:
    $ch = curl_init();//初始化 GET 方式
    curl_setopt($ch, CURLOPT_URL, $url_level4);//設置選項,包括URL
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_exec($ch);//執行並獲取HTML文檔內容
    curl_close($ch);//釋放curl句柄
}
Mredis.class.php

<?php

class Mredis{
private $redis;
/* 這里替換為實例id和實例password */
// 基礎參數准備
//protected $host = "125945f062c14ec1.m.cnsha.kvstore.aliyuncs.com";
protected $host = "127.0.0.1";
protected $port = 6379;
//protected $user = "125945f062c14ec1";
protected $pwd = "pwwd";
protected $max = 5;
protected $key = 'shop';
// 設置數據庫


// 構造函數 連接數據庫
public function __construct(){

$this->redis = new Redis();
$this->redis->connect($this->host, $this->port);
$this->redis->auth($this->key);

}
//插入數值
public function set_vaule($value){
// 設置基數標志位
if(!$this->redis->get('flag')){
$this->redis->set('flag',1);
}
// 檢測溢出
if($this->redis->get('flag')>$this->max){
return 'overflow';
}
// 插入非重復數據
if($this->redis->zAdd($this->key,$this->redis->get('flag'),$value)){
$this->redis->incr("flag");
}

}

// 返回全部存儲數據
public function get_value(){
return $this->redis->zRange($this->key,0,-1);
}
public function flushAll(){
return $this->redis->flushAll();
}

// 類結束了
}


Mrgister.class.php

<?php

class Mrgister{
    // 數據
    protected $phone=0;
    protected $number=0;
 
    // 數安全過濾
    public function I(){
        $this->phone = floatval(isset($_GET['phone'])?$_GET['phone']:0);
        $this->number = (isset($_GET['number'])?$_GET['number']:0);
        if(!($this->phone&&$this->number&&$this->check_number())){
            die('input data wrong');
        }
        return $this;
    }
    // 驗證輸入碼安全
    public function check_number(){// 其實用一個生成規則就好了
        //$this->number = '177022-879765-97143-979171';
        $flag   = true;
        $salt   = array("L","J","S","H");
        $res    = explode('-',$this->number);
        foreach($res as $k => $v){
            $v_start    = substr($v,0,4);
            $v_end      = substr($v,4);
            $v_new      = $v_start-$v_end;
            if($v_new%ord($salt[$k])){
                $flag = false;
            }
        }
        return $flag;
    }
    // 獲取正確 數值
    public function get_value(){
        return array($this->phone,$this->number);
    }
// 類結束了
}
數據處理層
知識點: 數據持久化

set_mysql.php

<?php

// 轉存信息 進入到 mysql 進行數據持久化
 
// 基礎准備
namespace jingshan;
header("Content-type:text/html;charset=utf8");
include_once 'lib/Mredis.class.php';
include_once 'lib/PdoMiao.class.php';
 
// 獲取數據
$Mredis = new Mredis();
$v = $Mredis->get_value();
$v = array_map(function($a){
    return unserialize($a);
},$v);
 
// 插入數據
$i = new PdoMiao();
$i->insert($v);
PdoMiao.class.php

<?php

 
class PdoMiao {
    protected $config     = array(
        //'hostname'          =>  'rm-bp15c7k47tx39267r.mysql.rds.aliyuncs.com', // 服務器地址
        'hostname'          => '127.0.0.1',
        'database'          =>  'jingshan',          // 數據庫名
        'hostport'          =>  '3306',        // 端口
        'charset'           =>  'utf8',      // 數據庫編碼默認采用utf8
    );
    protected $dbn;
    protected $user = 'root';
    protected $pwd  = 'H7oq5Io6';
    // 遠程連接
    public function __construct(){
        $dsn = $this->parseDsn($this->config);
        try{
            $this->dbn = new \PDO($dsn,$this->user,$this->pwd);
        }catch (\PDOException $e){
            echo 'Connection failed: '.$e->getMessage();
        }
    }
    // dsn 組裝
    protected function parseDsn($config){
        $dsn  =   'mysql:dbname='.$config['database'].';host='.$config['hostname'];
        if(!empty($config['hostport'])) {
            $dsn  .= ';port='.$config['hostport'];
        }
        if(!empty($config['charset'])){
            $dsn  .= ';charset='.$config['charset'];
        }
        return $dsn;
    }
    // 插入數據到數據庫
    Public function insert($datas){
        $sql = "INSERT INTO  `miaosha` ( `id` , `phone` , `number` ) VALUES ( NULL ,  ?,  ? );";
        $sth = $this->dbn->prepare($sql);
        foreach($datas as $k => $v){
            $sth->execute($v);
        }
    }
 
————————————————
版權聲明:本文為CSDN博主「KWTIT」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_41380972/article/details/86242066


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM