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

  • 相关阅读:
    sweetalert 1.0多次回调函数bug
    ThinkPHP添加扩展配置失败
    记一次相机内存卡数据恢复
    流量监控脚本监控网卡
    ip网关配置
    centos7【防火墙】常用规则-docker服务防火墙规则
    ssh服务及安全配置
    代码库
    linux计划任务防暴力破解脚本+免密操作
    阿里去短信接口包
  • 原文地址:https://www.cnblogs.com/zhaoshaopeng/p/12855087.html
Copyright © 2011-2022 走看看