NoSQL(Redis)秒殺
概念
秒殺
說明:秒殺就是指商家的限時(shí)大甩賣(商家為了售賣商品所采取的一種銷售手段)
特征:1-限時(shí),2-低價(jià)
種類:一元秒殺、低價(jià)限量秒殺、低價(jià)限時(shí)限量秒殺
好處:因?yàn)槊霘a(chǎn)品參與者數(shù)量眾多,可以瞬間聚集人氣,提升品牌影響力,是一種不錯(cuò)的促銷手段。
并發(fā)
生活中:指同時(shí)有n個(gè)用戶一起去收營臺結(jié)賬的表現(xiàn)可以稱之為并發(fā)
網(wǎng)絡(luò)中:指同時(shí)有n個(gè)用戶一起訪問網(wǎng)站的表現(xiàn)可以稱之為并發(fā)
并發(fā)導(dǎo)致的問題:
生活中-忙不過來,程序中-服務(wù)器可能崩潰或者出現(xiàn)意外結(jié)果(負(fù)庫存)
在計(jì)算機(jī)中:通過消息隊(duì)列實(shí)現(xiàn)
MySQL負(fù)庫存(秒殺可能出現(xiàn)的問題)
修改mysql.ini max_connections = 10 #聲明同時(shí)支持多少個(gè)用戶連接
打開 navicat.exe 執(zhí)行sql語句
create database if not exists miaosha charset=utf8;
use miaosha;
create table goods (id int primary key auto_increment,num int) engine=innodb;
insert into goods values (null, 100);
在站點(diǎn)目錄下創(chuàng)建mysql.php輸入下述命令
<?php
#語法:ab -n 1000 -c 100 請求地址
#說明:n-請求總數(shù), c- 每次請求量
//1.創(chuàng)建PDO對象
$pdo = new PDO('mysql:dbname=miaosha', 'root', 'root');
//2.查詢庫存
$pdoStatement = $pdo->query('select num from goods where id = 1');
$res = $pdoStatement->fetch(PDO::FETCH_ASSOC);
$num = $res['num'];
//3.判斷庫存
if ($num) {
//減庫存
$pdo->exec('update goods set num=num-1 where id = 1');
echo '搶購成功';
} else {
echo '對不起,你來晚了,庫存不足';
}
通過本地Apache安裝目錄下bin目錄中的ab測壓工具測試并發(fā)
ab.exe -n 1000 -c 100 http://127.0.0.1/mysql.php
多測試幾次 查看數(shù)據(jù)庫 可能會出現(xiàn)負(fù)庫存 這些是并發(fā)量高,數(shù)據(jù)處理不過來,當(dāng)前面用戶下單時(shí),后面用戶也讀取到了庫存數(shù)據(jù) 就會出現(xiàn)負(fù)庫存
Redis消息隊(duì)列(解決秒殺問題)
使用Workerman框架
下載Workerman框架....
在站點(diǎn)目錄下創(chuàng)建testworkerman.php輸入手冊中的定時(shí)器代碼
<?php
use \Workerman\Worker;
use \Workerman\Lib\Timer;
require_once __DIR__ . '/Workerman/Autoloader.php';
$task = new Worker();
// 開啟多少個(gè)進(jìn)程運(yùn)行定時(shí)任務(wù),注意業(yè)務(wù)是否在多進(jìn)程有并發(fā)問題
$task->count = 1;
$task->onWorkerStart = function($task)
{
// 每2.5秒執(zhí)行一次
$time_interval = 2.5;
Timer::add($time_interval, function()
{
echo "task run\n";
});
};
// 運(yùn)行worker
Worker::runAll();
打開DOS窗口通過php.exe執(zhí)行testworkerman.php文件查看效果
實(shí)現(xiàn)
登錄redis設(shè)置存放商品秒殺數(shù)據(jù)信息
flushall
hmset goods_seckill_1 start_time 0 stop_time 0 price 30 real_num 3 seckill_num 3
hmset goods_seckill_2 start_time 0 stop_time 0 price 30 real_num 2 seckill_num 2
在站點(diǎn)目錄下創(chuàng)建redis.php輸入下述命令
<?php
#步驟1:接受數(shù)據(jù)
$uid = 1;
$goods_id = 1;
#步驟2:連接Redis
$redis = new Redis;
$redis->connect('192.168.159.128', 6379);
$redis->auth('123');
$redis->select(0);
#步驟3:過濾(判斷時(shí)間和庫存)
//獲取商品信息
$goodsInfo = $redis->hmget("goods_seckill_{$goods_id}"., array(
'start_time', 'stop_time', 'real_num', 'seckill_num', 'price'
));
//判斷是否開始
//判斷是否結(jié)束
//判斷庫存
if ($goodsInfo['seckill_num'] < 1) {
echo json_encode(array('state' => 0, '對不起,寶貝已被搶完!'));
die;
}
#步驟4:將用戶請求加入消息隊(duì)列中
$len = $redis->lpush("goods_seckill_{$goods_id}_rs", $uid.'%'.$goods_id.'%'.$goodsInfo['price']);
#步驟5:判斷庫存(規(guī)則:隊(duì)列中前n個(gè)搶購成功)
if ($len > $goodsInfo['real_num']) {
//搶購失?。?duì)列長度 > 庫存)
echo json_encode(array('state' => 0, '對不起,寶貝已被搶完!'));
die;
} else {
//搶購成功,減庫存(注:千萬不能直接操作mysql因?yàn)橛胁l(fā)限制)
echo json_encode(array('state' => 1, '秒殺成功'));
die;
}
在站點(diǎn)目錄創(chuàng)建workerman.php輸入下述命令
<?php
use \Workerman\Worker;
use \Workerman\Lib\Timer;
require_once __DIR__ . '/Workerman/Autoloader.php';
$task = new Worker();
// 開啟多少個(gè)進(jìn)程運(yùn)行定時(shí)任務(wù),注意業(yè)務(wù)是否在多進(jìn)程有并發(fā)問題
$task->count = 1;
$task->onWorkerStart = function($task)
{
//每0.1秒執(zhí)行一次(精度可以達(dá)到毫秒0.001)
$time_interval = 0.1;
Timer::add($time_interval, function()
{
$goods_id = 1;
#步驟1:連接Redis
$redis = new Redis;
$redis->connect('192.168.159.128', 6379);
$redis->auth('123');
$redis->select(0);
#步驟2:獲取秒殺相關(guān)信息
$allowBuyNum = $redis->hget("goods_seckill_{$goods_id}", 'seckill_num'); //秒殺剩余庫存
$orderInfoString = $redis->rpop("goods_seckill_{$goods_id}_rs"); //隊(duì)列搶購用戶信息
#步驟3:判斷(有庫存 && 有人搶購)
if($allowBuyNum > 0 && $orderInfoString)
{
echo "allowBuyNum:$allowBuyNum\n";
#步驟4:減庫存
$redis->hincrby('goods_seckill_1', 'seckill_num', -1);
#步驟5:生成訂單
$pdo = new \PDO('mysql:dbname=php15shop', 'root', 'root');
$userOrderInfo = explode('%', $orderInfoString); //$uid.'%'.$goods_id.'%'.$price
$order_id = date('Ymd').time().uniqid();
$total_price = $userOrderInfo[2];
$member_id = $userOrderInfo[0];
$goods_id = $userOrderInfo[1];
$create_time = time();
$update_time = time();
#主表(sh_order)
$sql = "insert into sh_order (order_id, total_price, member_id, create_time, update_time)
value
('{$order_id}', $total_price, $member_id, $create_time, $update_time)";
$pdo->exec($sql);
#從表(sh_order_goods)
$sql = "insert into sh_order_goods (order_id, goods_id, goods_number, goods_price) value('{$order_id}', $goods_id , $total_price, 2)";
$pdo->exec($sql);
echo "over...\n";
}
});
};
// 運(yùn)行worker
Worker::runAll();
通過DOS窗口運(yùn)行workerman.php文件,監(jiān)聽隊(duì)列數(shù)據(jù)
通過本地Apache安裝目錄下的bin目錄ab測壓工具測試并發(fā)
ab -n 1000 -c 100 http://127.0.0.1/redis2/redis.php
查看主表數(shù)據(jù)...
搭建秒殺項(xiàng)目虛擬主機(jī)
創(chuàng)建虛擬目錄seckill
將seckill項(xiàng)目解壓到站點(diǎn)目錄中
打開seckill站點(diǎn)目錄并修改數(shù)據(jù)庫信息
將之前的shop商城數(shù)據(jù)復(fù)制一份 創(chuàng)建新數(shù)據(jù)庫并修改sh_goods表增加字段is_seckill(是否秒殺商品)
在Admin后臺創(chuàng)建Goods控制器seckillConfig方法
//商品秒殺配置
public function seckillConfig()
{
#步驟2:加載視圖
return $this->fetch('');
}
創(chuàng)建視圖文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>商品秒殺配置</title>
</head>
<body>
<form action="" method="post">
<p>秒殺開始時(shí)間<input type="text" name="start_time" /></p>
<p>秒殺結(jié)束時(shí)間<input type="text" name="stop_time" /></p>
<p>秒殺價(jià)格<input type="text" name="price" /></p>
<p>秒殺數(shù)量<input type="text" name="num" /></p>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
數(shù)據(jù)處理:修改商品狀態(tài)為秒殺 將秒殺商品數(shù)據(jù)保存的redis中
redis鍵規(guī)則:
hmset goods_seckill_1 start_time 0 stop_time 0 price 30 real_num 3 seckill_num 3
<a href="{:url('admin/goods/seckillConfig', array('goods_id' => $list['goods_id']))}"
class="showContent tablelink">
秒殺商品配置
</a>
修改admin后臺的goods控制器sekillconfig方法進(jìn)行數(shù)據(jù)處理
public function seckillConfig()
{
#步驟1:判斷是否post提交
if (request()->isPost()) {
#步驟2:接受數(shù)據(jù)
$start_time = input('start_time');
$stop_time = input('stop_time');
$price = input('price');
$num = input('num');
$goods_id = input('goods_id');
#步驟3:插入數(shù)據(jù)
$redis = new \Redis;
$redis->connect('192.168.159.128', 6379);
$redis->auth('123');
#hmset 鍵 字段1 值1 ... 字段n 值n
#hmset goods_seckill_1 start_time 0 stop_time 0 price 30 real_num 3 seckill_num 3
$tempData = array(
'start_time' => $start_time,
'stop_time' => $stop_time,
'price' => $price,
'real_num' => $num,
'seckill_num' => $num,
);
$rs = $redis -> hMset('goods_seckill_'.$goods_id, $tempData);
#步驟4:判斷
if ($rs) {
#修改商品狀態(tài)為秒殺
Goods::where('goods_id', $goods_id)->update([
'is_seckill' => 1
]);
#跳轉(zhuǎn)到商品秒殺列表頁
$this->success("商品秒殺配置成功", url("admin/goods/seckill"));
}else{
$this->error("商品秒殺配置失敗");
}
} else {
#步驟2:加載視圖
return $this->fetch('');
}
}
整合日期插件
下載jq插件包放到查念public/plugin目錄好
商品秒殺頁配置引入
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>商品秒殺配置</title>
</head>
<body>
<form action="" method="post">
<p>秒殺開始時(shí)間
<input type="text" name="start_time"
onClick="WdatePicker({el:this,dateFmt:'yyyy-MM-dd HH:mm:ss'})"
autocomplete="off"
/>
</p>
<p>
秒殺結(jié)束時(shí)間<input type="text" name="stop_time"
onClick="WdatePicker({el:this,dateFmt:'yyyy-MM-dd HH:mm:ss'})"
autocomplete="off"
/>
</p>
<p>秒殺價(jià)格<input type="text" name="price" /></p>
<p>秒殺數(shù)量<input type="text" name="num" /></p>
<p><input type="submit" value="提交"></p>
</form>
<script language="javascript" type="text/javascript" src="/plugin/My97DatePicker/WdatePicker.js"></script>
</body>
</html>
修改控制器方法格式化日期
start_time => strtltime($start_time)
stop_time => strtotime($stop_time)
在Admin后臺創(chuàng)建Goods控制器seckill方法
public function seckill()
{
#步驟1:查詢所有數(shù)據(jù)
$seckills = Goods::where('is_seckill', 1)->select();
#步驟2:過濾數(shù)據(jù)
foreach ($seckills as $seckill) {
#$seckill->goods_id
#$seckill->goods_name
#查詢商品秒殺信息
$redis = new \Redis;
$redis->connect('192.168.159.128', 6379);
$redis->auth('123');
$temp = $redis -> hMget('goods_seckill_'.$seckill->goods_id, array(
'start_time',
'stop_time',
'price',
'real_num'
));
#將商品秒殺信息添加到$seckill中
$seckill->start_time = $temp['start_time'];
$seckill->stop_time = $temp['stop_time'];
$seckill->price = $temp['price'];
$seckill->real_num = $temp['real_num'];
}
#步驟3:加載視圖
return $this->fetch('', [
'seckills'=>$seckills
]);
}
創(chuàng)建視圖并循環(huán)顯示數(shù)據(jù)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<table border="1" cellpadding="10" cellspacing="0">
<tr>
<td>商品ID</td>
<td>商品名稱</td>
<td>商品價(jià)格</td>
<td>商品數(shù)量</td>
<td>開始時(shí)間</td>
<td>結(jié)束時(shí)間</td>
<td>距離結(jié)束</td>
</tr>
{foreach $seckills as $seckill}
<tr>
<td>{$seckill.goods_id}</td>
<td>{$seckill.goods_name}</td>
<td>{$seckill.price}</td>
<td>{$seckill.real_num}</td>
<td>{:date('Y-m-d H:i:s', $seckill.start_time)}</td>
<td>{:date('Y-m-d H:i:s', $seckill.stop_time)}</td>
<td>0</td>
</tr>
{/foreach}
</table>
</body>
</html>
距離倒計(jì)時(shí)(修改控制器 ) 增加字段
$seckill->time = $temp['stop_time']
距離倒計(jì)時(shí)(修改視圖)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script type="text/javascript" src="{:config('admin_static')}/js/jquery.js"></script>
<script>
function timer(intDiff,idName) {
window.setInterval(function() {
var day = 0,
hour = 0,
minute = 0,
second = 0; //時(shí)間默認(rèn)值
if (intDiff > 0) {
day = Math.floor(intDiff / (60 * 60 * 24));
hour = Math.floor(intDiff / (60 * 60)) - (day * 24);
minute = Math.floor(intDiff / 60) - (day * 24 * 60) - (hour * 60);
second = Math.floor(intDiff) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60);
}
if (minute <= 9) minute = '0' + minute;
if (second <= 9) second = '0' + second;
$(idName).html(day + "天" + hour + '時(shí)' + minute + '分' + second + '秒');
//console.log(idName);
//$(idName+' .day_show').html(day + "天");
//$(idName+' .hour_show').html('<s id="h"></s>' + hour + '時(shí)');
//$(idName+' .minute_show').html('<s></s>' + minute + '分');
//$(idName+' .second_show').html('<s></s>' + second + '秒');
intDiff--;
}, 1000);
}
</script>
</head>
<body>
<table border="1" cellpadding="10" cellspacing="0">
<tr>
<td>商品ID</td>
<td>商品名稱</td>
<td>商品價(jià)格</td>
<td>商品數(shù)量</td>
<td>開始時(shí)間</td>
<td>結(jié)束時(shí)間</td>
<td>距離結(jié)束</td>
</tr>
{foreach $seckills as $seckill}
<tr>
<td>{$seckill.goods_id}</td>
<td>{$seckill.goods_name}</td>
<td>{$seckill.price}</td>
<td>{$seckill.real_num}</td>
<td>{:date('Y-m-d H:i:s', $seckill.start_time)}</td>
<td>{:date('Y-m-d H:i:s', $seckill.stop_time)}</td>
<td id="time{$seckill.goods_id}">0</td>
</tr>
<script>
timer({$seckill->time},'#time{$seckill.goods_id}');
</script>
{/foreach}
</table>
</body>
</html>
完成前臺秒殺功能
修改home/index/index
<li><a href="{:url('home/seckill/index')}">商品秒殺</a></li>
在后臺創(chuàng)建seckill控制器index方法
<?php
namespace app\home\controller;
use think\Controller;
use app\home\model\Goods;
class SeckillController extends Controller
{
public function index()
{
#步驟1:查詢所有數(shù)據(jù)
$seckills = Goods::where('is_seckill', 1)->select();
#步驟2:過濾數(shù)據(jù)
foreach ($seckills as $seckill) {
#$seckill->goods_id
#$seckill->goods_name
#查詢商品秒殺信息
$redis = new \Redis;
$redis->connect('192.168.159.128', 6379);
$redis->auth('123');
$temp = $redis -> hMget('goods_seckill_'.$seckill->goods_id, array(
'start_time',
'stop_time',
'price',
'real_num',
'seckill_num',
));
#將商品秒殺信息添加到$seckill中
$seckill->start_time = $temp['start_time'];
$seckill->stop_time = $temp['stop_time'];
$seckill->price = $temp['price'];
$seckill->real_num = $temp['real_num'];
$seckill->seckill_num = $temp['seckill_num'];
}
#步驟3:加載視圖
return $this->fetch('', [
'seckills'=>$seckills
]);
}
}
創(chuàng)建視圖并循環(huán)顯示數(shù)據(jù)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>一元秒殺!還包郵!</title>
<style type="text/css">
*{margin:0; padding:0;}
body{font-family:"微軟雅黑";}
.html{ overflow:auto}
.c{ clear:both;}
a{text-decoration: NONE; color:#000; font-size:16px;}
.bodybg{ background:url({:config('home_static')}/image/bg.jpg); width:100%;}
.banner{ background:url({:config('home_static')}/image/banner.jpg) no-repeat center; height:542px;}
.nav{ border:none;width:1000px;margin:0 auto;}
.probg{ background:#f4ad1e; width:1000px; margin:0 auto; text-align:center;}
.fl{ float:left; padding-left:9px; padding-top:9px;}
.onebg{ width:321px; height:321px; background:#FFF;}
.titlebg{ width:321px; height:33px; background:#FFF;}
.buybg{ background:url({:config('home_static')}/image/pricebg.jpg); width:321px; height:70px;}
.foreprice{ font-size:17px; text-decoration:line-through; color:#FFF;float:left; padding-top:13px; padding-left:25px;}
.button{ width:96px;height:70px;float:right; padding:0;}
.ads{ width:1000px; height:326px; margin:0 auto;}
.sm{ background:#FFF; width:100%; margin:0 auto;}
.xize{ width:1000px; margin:0 auto; text-align:left; font-size:36px; font-weight:700; color:#9E051A;}
.shuoming{ width:1000px; margin:0 auto; text-align:left; font-size:20px;color:#9E051A; line-height:35px;}
</style>
</head>
<body>
<div class="banner"></div>
<div class="bodybg">
<div class="nav"><img src="{:config('home_static')}/image/nav.jpg" width="1000" height="70"/></div>
<div class="probg">
{foreach $seckills as $seckill}
<!-- 單個(gè)商品循環(huán)-->
<div class="fl">
<div class="onebg" >
<a href="javascript:void(0);" ><img src="{:config('home_static')}/image/1.jpg" width="321px;height:321px"/></a>
</div>
<div class="titlebg">
<a href="javascript:void(0);" >{$seckill.goods_name}</a>
</div>
<div class="buybg" style="background: red;">
<div class="foreprice" style="text-decoration:none;">價(jià)格:{$seckill.price}</div>
<div class="foreprice" style="text-decoration:none;">庫存:{$seckill.seckill_num}</div>
<div class="button">
<!-- <img src="{:config('home_static')}/image/button.jpg"/> -->
<input type="button" value="立即搶購" style="margin-top:20px; width: 80px;height: 30px; cursor: pointer;" data-id="{$seckill.goods_id}"/>
</div>
</div>
</div>
<!-- 單個(gè)商品循環(huán)結(jié)束-->
{/foreach}
<div>
<a href="javascript:void(0);" ><img src="{:config('home_static')}/image/ads.jpg" /></a>
</div>
<div class="c"></div>
</div>
<div class="sm">
<br />
<div class="xize">1元秒殺細(xì)則:</div>
<div class="shuoming">1.參與秒殺前,請?jiān)敿?xì)閱讀秒殺規(guī)則,凡參與1元秒殺活動的用戶,均視為同意秒殺規(guī)則。<br />2.秒殺商品將于2014年7月7日08:00:00上線-2014年7月11日23:59:59結(jié)束,當(dāng)天商品售罄時(shí)當(dāng)天秒殺結(jié)束,活動期間每一個(gè)云中央注冊會員每期僅限秒殺一個(gè)商品,秒殺多件成功者,并通過收貨人及聯(lián)系方式可判定為同一人的,則取消全部訂單。<br />3.秒殺成功以支付成功為準(zhǔn),早秒早得;秒殺下單后30分鐘內(nèi)未付款者自動取消訂單,請?zhí)貏e注意。<br />4.請確保秒殺填寫的收貨人信息真實(shí)有效,因聯(lián)系方式填寫錯(cuò)誤導(dǎo)致未收到禮品的,由用戶自行承擔(dān)損失。<br />5.對于任何通過不正當(dāng)手段參與秒殺者,不正當(dāng)手段包括但不限于使用秒殺器或類似作弊軟件,云中央網(wǎng)站有權(quán)依據(jù)自身技術(shù)判斷,并在不事先通知的情況下取消其秒殺資格或者取消訂單。
</div>
</div>
</div>
</body>
</html>
立即搶購(入隊(duì))
在home/平臺創(chuàng)建Seckill控制器創(chuàng)建add方法
public function add()
{
#步驟1:接受數(shù)據(jù)
$uid = session('member_id');
if(!$uid) {
echo json_encode(array('state' => 0, 'msg'=>'請登錄后重試...'));
die;
};
$goods_id = input('goods_id');
if(!$goods_id) {
echo json_encode(array('state' => 0, 'msg'=>'非法操作...'));
die;
}
#步驟2:連接Redis
$redis = new \Redis;
$redis->connect('192.168.159.128', 6379);
$redis->auth('123');
$redis->select(0);
#步驟3:過濾(判斷時(shí)間和庫存)
//獲取商品信息
$goodsInfo = $redis->hmget("goods_seckill_{$goods_id}", array(
'start_time', 'stop_time', 'real_num', 'seckill_num', 'price'
));
if(!$goodsInfo) {
echo json_encode(array('state' => 0, 'msg'=>'秒殺商品不存在...'));
die;
}
//判斷是否開始
if ($goodsInfo['start_time'] > time()) {
echo json_encode(array('state' => 0, 'msg'=>'未開始'));
die;
}
//判斷是否結(jié)束
if ($goodsInfo['stop_time'] < time()) {
echo json_encode(array('state' => 0, 'msg'=>'已結(jié)束'));
die;
}
//判斷庫存
if ($goodsInfo['seckill_num'] < 1) {
echo json_encode(array('state' => 0, 'msg'=>'對不起,寶貝已被搶完!'));
die;
}
#步驟4:將用戶請求加入消息隊(duì)列中
$len = $redis->lpush("goods_seckill_{$goods_id}_rs", $uid.'%'.$goods_id.'%'.$goodsInfo['price']);
#步驟5:判斷庫存(規(guī)則:隊(duì)列中前n個(gè)搶購成功)
if ($len > $goodsInfo['real_num']) {
//搶購失敗(隊(duì)列長度 > 庫存)
echo json_encode(array('state' => 0, 'msg'=>'對不起,寶貝已被搶完!'));
die;
} else {
//搶購成功,減庫存(注:千萬不能直接操作mysql因?yàn)橛胁l(fā)限制)
echo json_encode(array('state' => 1, 'msg'=>'秒殺成功'));
die;
}
}
修改秒殺列表發(fā)送異步請求
<script type="text/javascript" src="{:config('home_static')}/js/jquery-1.8.3.min.js"></script>
<script>
$(function(){
$('.buybg input').click(function(){
//禁用按鈕
var thisObj = $(this);
$(this).attr('disabled', 'disabled');
$(this).css('cursor', 'default');
$(this).val('搶購中...');
var goods_id = $(this).attr('data-id');
$.post("{:url('home/seckill/add')}", {goods_id:goods_id},function(data){
if (data.state) {
alert(data.msg);
location.href = "{:url('home/order/seckill')}";
} else {
alert(data.msg)
}
//還原按鈕
$(thisObj).removeAttr('disabled');
$(thisObj).css('cursor', 'pointer');
$(thisObj).val('立即搶購');
}, 'json');
});
});
</script>
使用Workerman框架(出隊(duì))
<?php
use \Workerman\Worker;
use \Workerman\Lib\Timer;
require_once __DIR__ . '/Workerman/Autoloader.php';
$task = new Worker();
// 開啟多少個(gè)進(jìn)程運(yùn)行定時(shí)任務(wù),注意業(yè)務(wù)是否在多進(jìn)程有并發(fā)問題
$task->count = 1;
$task->onWorkerStart = function($task)
{
//每0.1秒執(zhí)行一次(精度可以達(dá)到毫秒0.001)
$time_interval = 0.1;
Timer::add($time_interval, function()
{
$goods_id = 9;
#步驟1:連接Redis
$redis = new Redis;
$redis->connect('192.168.159.128', 6379);
$redis->auth('123');
$redis->select(0);
#步驟2:獲取秒殺相關(guān)信息
$allowBuyNum = $redis->hget("goods_seckill_{$goods_id}", 'seckill_num'); //秒殺剩余庫存
$orderInfoString = $redis->rpop("goods_seckill_{$goods_id}_rs"); //隊(duì)列搶購用戶信息
#步驟3:判斷(有庫存 && 有人搶購)
if($allowBuyNum > 0 && $orderInfoString)
{
echo "allowBuyNum:$allowBuyNum\n";
#步驟4:減庫存
$redis->hincrby('goods_seckill_1', 'seckill_num', -1);
#步驟5:生成訂單
$pdo = new \PDO('mysql:dbname=seckill', 'root', 'root');
$userOrderInfo = explode('%', $orderInfoString); //$uid.'%'.$goods_id.'%'.$price
$order_id = date('Ymd').time().uniqid();
$total_price = $userOrderInfo[2];
$member_id = $userOrderInfo[0];
$goods_id = $userOrderInfo[1];
$create_time = time();
$update_time = time();
#主表(sh_order)
$sql = "insert into sh_order (is_seckill, order_id, total_price, member_id, create_time, update_time)
value
(1, '{$order_id}', $total_price, $member_id, $create_time, $update_time)";
$pdo->exec($sql);
#從表(sh_order_goods)
$sql = "insert into sh_order_goods (order_id, goods_id, goods_number, goods_price) value('{$order_id}', $goods_id, 1, $total_price)";
$pdo->exec($sql);
echo "over...\n";
}
});
};
// 運(yùn)行worker
Worker::runAll();