大型PHP电商网站商品秒杀功能实现思路分析


二 认清当前环境, 形式

用户: 超大量, 正常/坏人

地域: 全国各地 [因为全国各地不同, 因此需要用cdn将服务发送到离用户最近的那个服务器]

业务流程: [前台]商品展示, 登记, [后台]数据输入, 数据处理


以下为架构方案 : 


分为两个大层

一 用户比较关心, 用户看的见的层

1 商品展示层/页

2 用户登记层

二 后台层 / 数据持久化层

1 数据接入层

2 后续处理层


分层详解

一 页面状态

1 商品展示: 秒杀倒计时页面

2 秒杀进行中: 点击进入秒杀页面

3 秒杀活动结束: 提示活动已经结束


从用户的角度看 : 


下图位秒杀示例图:




秒杀倒计时和秒杀开始是利用linux定时任务和shell脚本来做

秒杀进行中到秒杀结束是利用php处理对应的服务器程序来做


二 页面状态

1 秒杀进行中: 秒杀登记页面

2 秒杀结束: 秒杀结束页面


流程图



数据持久化层

数据校验: 完成对数据 / 用户验证 (加密解密算法)

存入nosql队列: 去重复 / 排序数据 (redis有序集合)

检测商品最大数量: 提示活动已经结束 (技术标志位)


页面功能

数据持久化:  转存nosql数据到mysql数据库



干货

第一层: 商品展示层

知识点: 页面 / 服务器优化, cdn忘了加速, 隐藏跳转界面, 状态切换

linux 页面切换:

#!/usr/bin/env bash
# 看一下我们的shell脚本是否正常执行
date >> /root/456.txt

####  页面切换  ####
# 删除index.html秒杀等待页面
rm -rf '/var/www/html/index.html'
# 将秒杀开始页面切换到秒杀等待页面 
cp '/var/www/html/index_sale.html' '/var/www/html/index.html'


linux定时任务:
*/1 * * * * root /bin/51miao.sh #执行秒杀页面的定时任务代码


第二层: 秒杀进行和秒杀结束切换:

// 第一步:删除文件
$file_path = 'index.html';
$f = file_exists($file_path) && unlink($file_path);

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

// 获取文件内容
$file_path = 'index_over.html';
$txt = file_get_contents($file_path);
fwrite($myfile, $txt);


用户登记层

知识点:  token加 / 解密, ajax跨域

/**
 * Created by ziniu on 2016/5/31.
 */
$(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 基础数据准备
namespace jingshan;
header("Access-Control-Allow-Origin: http://www.miaosha_level2.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).')';


<?php
/**
 * Created by PhpStorm.
 * User: ziniu
 * Date: 2016/6/12
 * Time: 11:21
 */
// 创建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
/**
 * Created by PhpStorm.
 * User: ziniu
 * Date: 2016/6/12
 * Time: 10:15
 */

namespace jingshan;

class Mredis{
	/* 这里替换为实例id和实例password */
	// 基础参数准备
	protected $host = "125945f062c14ec1.m.cnsha.kvstore.aliyuncs.com";
	//protected $host = "127.0.0.1";
	protected $port = 6379;
	protected $user = "125945f062c14ec1";
	protected $pwd = "Li02131421";
	protected $max = 10000;
	protected $key = 'shop';
	// 设置数据库
	protected $redis;

	// 构造函数 连接数据库
	public function __construct(){
		$this->redis = new \Redis();
		/* 连接内网redis数据库 */
		if ($this->redis->connect($this->host, $this->port) == false) {
			die($this->redis->getLastError());
		}
		if($this->host!="127.0.0.1"){
			/* user:password 拼接成AUTH的密码 */
			if ($this->redis->auth($this->user . ":" . $this->pwd) == false) {
				die($this->redis->getLastError());
			}

		}
	}

	//插入数值
	public function set_vaule($value){
		// 设置基数标志位
		if(!$this->redis->get('flag')){
			$this->redis->set('flag',1);
		}
		// 插入非重复数据
		if($this->redis->zAdd($this->key,$this->redis->get('flag'),$value)){
			$this->redis->incr("flag");
		}
		// 检测溢出
		if($this->redis->get('flag')>$this->max){
			return 'overflow';
		}
	}

	// 返回全部存储数据
	public function get_value(){
		return $this->redis->zRange($this->key,0,-1);
	}
	// 类结束了
}

Mrgister.class.php

<?php
/**
 * Created by PhpStorm.
 * User: ziniu
 * Date: 2016/6/1
 * Time: 10:21
 */
namespace jingshan;
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
/**
 * Created by PhpStorm.
 * User: ziniu
 * Date: 2016/6/12
 * Time: 15:34
 */
// 转存信息 进入到 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
/**
 * Created by PhpStorm.
 * User: ziniu
 * Date: 2016/6/12
 * Time: 16:08
 */

namespace jingshan;

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);
		}
	}
	// 类结束了
}



  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
网站php需求分析说明书是为了明确网站开发所需的功能和技术要求,从而能够确保开发团队在建设过程中能够准确理解和满足客户的需求。以下是一个可能的网站php需求分析说明书的内容: 1. 项目简介:介绍网站的背景和目标。说明此项目的目的是为了提供一个用于购买商品的在线平台,并且详细描述了网站的目标用户、服务范围和特色功能。 2. 系统需求:列出了网站所需要的技术要求和基础设施要求。这包括开发语言、框架、数据库,以及服务器、安全性和可扩展性等方面的需求。 3. 功能需求:详细描述了网站需要实现的各种功能。这包括用户注册与登录、商品展示与搜索、购物车和订单管理、支付和物流等方面。每个功能都应该有具体的描述和实现要求,以确保开发团队能够正确理解和实现需求。 4. 用户界面设计需求:描述了网站的用户界面设计要求。这包括整体布局、导航结构、页面样式和交互方式等。可以提供页面原型图和设计参考,以确保开发团队和设计师能够准确理解和实现设计要求。 5. 数据库设计需求:描述了网站所需的数据库结构和数据表设计。这包括用户信息、商品信息、订单信息等表的设计要求。确保数据库能够满足网站功能需求,并具备良好的性能和扩展性。 6. 技术支持需求:描述了开发团队对于技术支持和交付物要求。这包括开发环境的搭建、代码版本管理、测试和部署方法等。同时,还需要说明项目提交的详细要求,如文档、源代码和测试报告等。 7. 项目进度和预算:制定项目的时间计划和预算安排。描述项目开发的各个阶段和里程碑,以及开发团队的人员配备和工作量分配。确保项目按照计划进行,并合理控制开发成本。 8. 风险评估和管理:识别项目开发过程中可能出现的风险,并提供相应的解决方案和风险管理策略。例如,根据项目规模和时间限制,可以提出阶段性发布或迭代开发的建议,并对可能的技术和市场风险进行评估。 总结:网站php需求分析说明书是一个详细描述网站开发需求的文档,通过精确的需求描述和解决方案提出,能够确保开发团队按照客户的要求进行开发,并最终交付一个符合预期的网站
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值