前言:
秒殺這個問題,一直以來都是經典的面試題。但是秒殺也分大小。如果一個產品的用戶不超過5w,上來就問雙十一級別的秒殺。那就沒有意思了~,所以今天就簡單聊下一般條件下的秒殺的思路。方法只有兩個,一個是裝載秒殺商品。一個就是模擬用戶進場秒殺。
圖片發自簡書App
工具介紹
首先環境就比較簡單
- Apache
- PHP 7.3
- redis
框架我選擇的ThinkPHP5.1 不過這次我主要還是選擇貼近原生的寫法
選擇apache的原因很簡單。自帶壓力測試工具ab。符合我們的需要。雖然我們知道nginx來做web服務器性能更好。
php7.* 這個不用多介紹了PHP 7 和 PHP 5的性能不是一個世界的
redis 雖然可以實現秒殺的方式有很多。redis算是非常常見的緩存和中間件工具了。在性能和上手難度上都是很不錯的選擇
一.裝載秒殺商品
我們先假設我們有300個人來搶30件商品。那麼我們就在我們的商品庫裏面裝載30件不同id的商品
秒殺商品一般都是定時添加的。所以我們需要一個定時任務控制器用cli模式執行
class Crontab
{
public function addGoods()
{
//設定商品數量
$count=30;
$listKey="2019_04_15_goods_list";
//創建連接redis對象
$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
for ($i=1;$i<=$count;$i++){
//將商品id push到列表中
$redis->rPush($listKey,$i);
}
}
}
然後當我們需要裝載商品的時候我們使用php命令去執行下我們的方法
php /項目地址/public/index.php index/crontab/addgoods
用redis客戶端查看下商品id是否放入成功
查看商品id
二.秒殺商品
秒殺商品其實就是一個將集合中的商品id取出和用戶id綁定的過程。只是這個過程進行的非常的快。那麼我們將秒殺分爲兩步,如果秒殺成功,則記錄下用戶id和商品id 也就是所謂的秒殺訂單。如果秒殺失敗,我們則簡單的記錄一個秒殺失敗的人數。來確定這次秒殺有多少有效用戶參與。
public function kill()
{
//假裝是用戶的唯一標識
$uuid=md5(uniqid('user').time());
//創建連接redis對象
$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
$listKey="2019_04_15_goods_list";
$orderKey="2019_04_15_buy_order";
$failUserNum="2019_04_15_fail_user_num";
if ($goodsId=$redis->lPop($listKey)) {
//秒殺成功
//將幸運用戶存在集合中
$redis->hSet($orderKey,$goodsId,$uuid);
}else{
//秒殺失敗
//將失敗用戶計數
$redis->incr($failUserNum);
}
echo "SUCCESS";
}
壓力測試模擬秒殺
剛剛有提到會使用apache自帶的ab做測試
小試牛刀 300併發 3000訪問量
ab -c 300 -n 3000 http://shop.example.com/index.php/index/index/kill
啥也不說就是幹
運行結果
雖說還是比較慢,但是3000次請求,是全部命中沒有死掉的用戶。加上我本身docker性能沒給到最大。加上只有單機節點。我對這個成績還是比較滿意的
下面來看看搶到商品的幸運用戶
[root@2f7621a62356 bin]# redis-cli
127.0.0.1:6379> HGETALL 2019_04_15_buy_order
商品和 用戶id的對應關係
再看看秒殺失敗的用戶數量
搶購失敗次數
這時候的商品list已經空空如也了。