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/[email protected] */ /** * 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/[email protected] */ 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
|
请发表评论