目錄
分析
開始
下單
客戶端通知
後端
前端
庫存解鎖
總結
首頁 php框架 Laravel 秒殺系統的設計

秒殺系統的設計

Jun 25, 2019 pm 03:06 PM
秒殺系統 設計

秒殺系統的設計

之前寫過一篇關於促銷系統的設計中提到了秒殺/直減/聚划算,但在實際工作中,並沒有真的做過秒殺系統,所以假想了一個簡單的秒殺系統來」解解饞「,促銷思路依舊順延之前的文章設計。

分析

秒殺時大量的流量湧入,秒殺開始前頻繁刷新查詢,如果大量的流量瞬間衝擊到資料庫的話,非常容易造成資料庫的崩潰。所以秒殺的主要工作就是對流量進行層層篩選最後讓盡可能平緩的流量進入到資料庫。

通常的秒殺是大量的用戶搶購少量的商品,類似這樣的需求只需要簡單的進行庫存緩存,就能在實際創建訂單前過濾大量的流量。

但但但是,只是這樣的話好像沒什麼挑戰力呀!稍微加大一下難度,假設我們的秒殺是像搶小米手機一樣,100 萬人搶 10 萬台手機呢?小米搶購時的排隊是一種方法(雖然體驗不太好),後續將會按照這個想法進行我們的秒殺設計。

提到小米就不得不說一下,其讓我知道了什麼是「運氣也是實力的一部分!」

前端限流大法: random(0, 1) ? axios.post : wait(30, '搶完啦!')

下面開始從一些程式碼的細節進行分析,原則上是對原有業務邏輯盡可能小的改動。 另外後文沒有什麼服務熔斷,多層快取等進階的玩法,只是比較簡單的業務設計。

開始

運營人員在後台將一個變體添加到秒殺促銷,並設定秒殺的庫存/秒殺折扣率/開始時間和結束時間等,我們能夠得到類似這樣的數據。

// promotion_variant (促销和变体表「sku」的一个中间表)
{
    'id': 1,
    'variant_id': 1,
    'promotion_id': 1,
    'promotion_type': 'snap_up',
    'discount_rate': 0.5,
    'stock': 100, // 秒杀库存
    'sold': 0, // 秒杀销量
    'quantity_limit': 1, // 限购
    'enabled': 1,
    'product_id': 1,
    'rest': {
        variant_name: 'xxx', // 秒杀期间变体名称
        image: 'xxx', // 秒杀期间变体图片
    }
}
登入後複製

首先便是在秒殺促銷創建成功後將促銷的信息進行緩存

# PromotionVariantObserver.php

public function saved(PromotionVariant $promotionVariant)
{
  if ($promotionVariant->promotion_type === PromotionType::SNAP_UP) {
    $seconds = $promotionVariant->ended_at->getTimestamp() - time();

    \Cache::put(
      "promotion_variants:$promotionVariant->id",
      $promotionVariant,
      $seconds
    );
  }
}
登入後複製

下單

已有的下單接口,接收到變體信息後,並不知道目前變體清單哪些參與了促銷,這裡的判斷操作是需要大量的資料庫查詢操作的。

所以此處為秒殺編寫一個新的 api ,前端檢測到當前變體處於秒殺促銷時則切換到秒殺下單 api 。

當然依舊使用原有的下單 api ,前端傳遞一個標識也是沒有問題的。

需要解釋的一點時,下單通常分為兩個步驟

第一步是「結帳( checkout )」產生一個結帳訂單,使用者可以為結帳訂單選擇地址、優惠卷、付款方式等。

第二步是 “確認 ( confirm )”,此時訂單將變成確認狀態,對庫存進行鎖定,且用戶可以進行支付。通常如果在規定時間內沒有支付,則取消該訂單,並解鎖庫存。

所以在第一步時就會對使用者進行過濾和排隊處理,防止後續的選擇地址、優惠卷等操作對資料庫進行衝擊。

# CheckoutController.php

/**
 * @param Request $request
 * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
 * @throws StockException
 */
public function snapUpCheckout(Request $request)
{
    $variantId = $request->input('variant_id');
    $quantity = $request->input('quantity', 1);

    // 加锁防止超卖
    $lock = \Cache::lock('snap_up:' . $variantId, 10);

    try {
        // 未获取锁的消费者将阻塞在这里
        $lock->block(10);

        $promotionVariant = \Cache::get('promotion_variants:' . $variantId);

        if ($promotionVariant->quantity release();

            throw new StockException('库存不足');
        }

        $promotionVariant->quantity -= $quantity;

        $seconds = $promotionVariant->ended_at->getTimestamp() - time();
        \Cache::put(
            "promotion_variants:$promotionVariant->id",
            $promotionVariant,
            $seconds
        );

    } catch (LockTimeoutException $e) {
        throw new StockException('库存不足');

    } finally {
        optional($lock)->release();
    }

    CheckoutOrder::dispatch([
        'user_id' => \Auth::id(),
        'variant_id' => $variantId,
        'quantity' => $quantity
    ]);

    return response('结账订单创建中');
}
登入後複製

可以看到在秒殺結帳 api 中,並沒有涉及到資料庫的操作。並且透過 dispatch 將創建訂單的任務分發到佇列,使用者按照進入佇列的先後順序進行對應時間的排隊等待。

現在的問題是,訂單建立成功後如何通知客戶端呢?

客戶端通知

這裡的方案無非就是輪詢或websocket, 這裡選擇對伺服器效能消耗較小的websocket ,且使用laravel 提供的laravel-echo ( laravel-echo-server ) 。當用戶秒殺成功後,前端和後端建立 websocket 鏈接,後端結帳訂單創建成功後通知前端可以進行下一步操作。

後端

後端接下來要做的就是在「CheckoutOrder」Job 中的訂單創建成功後,向websocket 對應的頻道中發送一個「OrderChecked 」事件,來表明結帳訂單已經建立完成,使用者可以進行下一步操作。

# Job/CheckoutOrder.php

// ...

public function handle()
{
  // 创建结账订单
  // ...

  // 通知客户端. websocket 编程本身就是以事件为导向的,和 laravel 的 event 非常契合。
  event(new OrderChecked($this->data->user_id));
}

// ...
登入後複製
# Event/OrderChecked.php

class OrderChecked implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    private $userId;

    /**
     * Create a new event instance.
     *
     * @param $userId
     */
    public function __construct($userId)
    {
        $this->userId = $userId;
    }

    /**
     * App.User.{id} 是 laravel 初始化时,默认的私有频道,直接使用即可
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('App.User.' . $this->userId);
    }
}
登入後複製

假設目前搶購的使用者 id 是 1,總結上面的程式碼就是向 websocket 的私人頻道「App.User.1」 推送一個 “OrderChecked” 事件。

前端

下面的程式碼是使用 vue-cli 工具初始化的預設項目。

// views/products/show.vue

<script>

import Echo from &#39;laravel-echo&#39;
import io from &#39;socket.io-client&#39;
window.io = io

export default {
  name: &#39;App&#39;,
  methods: {
    async snapUpCheckout () {
      try {
        // await post -> snap-up-checkout
        this.toCheckout()
      } catch (error) {
        // 秒杀失败
      }
    },
    toCheckout () {
      // 建立 websocket 连接
      const echo = new Echo({
        broadcaster: &#39;socket.io&#39;,
        host: &#39;http://api.e-commerce.test:6001&#39;,
        auth: {
          headers: {
            Authorization: &#39;Bearer &#39; + this.store.auth.token
          }
        }
      })

      // 监听私有频道 App.User.{id} 的 OrderChecked 事件
      echo.private(&#39;App.User.&#39; + this.store.user.id).listen(&#39;OrderChecked&#39;, (e) => {
        // redirect to checkou page
      })
    }
  }
}
</script>
登入後複製

laravel-echo 使用時需要注意的一點,由於使用了私有頻道,所以laravel-echo 預設會向服務端api /broadcasting/auth 發送一條post 請求進行身份驗證。但由於採用了前後端分類而不是 blade 模板,所以我們並不能方便的獲取 csrf token 和 session 來進行一些必要的認證。

因此需要稍微修改一下broadcast 和laravel-echo-server 的配置

# BroadcastServiceProvider.php

public function boot()
{
  // 将认证路由改为 /api/broadcasting/auth 从而避免 csrf 验证
  // 添加中间件 auth:api (jwt 使用 api.auth) 进行身份验证,避免访问 session ,并使 Auth::user() 生效。
  Broadcast::routes(["prefix" => "api", "middleware" => ["auth:api"]]);

  require base_path('routes/channels.php');
}
登入後複製
// laravel-echo-server.json

// 认证路由添加 api 前缀,与上面的修改对应
"authEndpoint": "/api/broadcasting/auth"
登入後複製

庫存解鎖

在已經為該訂單鎖定」庫存「的情況下,用戶如果斷開websocket 連線或長時間離開時需要將庫存解鎖,防止庫存無意義佔用。

這裡的庫存指的是快取庫存,而非資料庫庫存。這是因為此時訂單即使建立成功也是結帳狀態(未選擇地址,付款方式等),在個人中心也是看不見的。只有當用戶確認訂單後,才會將資料庫庫存鎖定。

所以這裡的理想實作是,用戶斷開 websocket 連線後,將該訂單鎖定的庫存歸還。且結帳訂單建立後再建立一個延時佇列對長時間未操作的訂單進行庫存歸還。

但但但是,laravel-echo 是一個廣播系統,並沒有提供客戶端斷開連接事件的回調,有些方法可以實現laravel 監聽的客戶端事件,例如在laravel-echo-server 添加hook通知laravel,但是要修改laravel-echo-server 的實現,這裡就不細說了,重點還是提供秒殺思路。

總結

秒殺系統的設計

上圖為秒殺系統的邏輯總結。至此整個秒殺流程就結束了,總的來說程式碼量不多,邏輯也較為簡單。

從圖中可以看出,整個流程中,只有在 queue 中才會和 mysql 交互,透過 queue 的限流從而最大限度的適應了 mysql 的承受能力。在 mysql 性能足夠的情況下,透過大量的 queue 同時消費訂單,用戶是完全感知不到排隊的過程的。

有問題或有更好的思路歡迎留言討論呀~

更多Laravel相關技術文章,請訪問Laravel教程欄目進行學習!

以上是秒殺系統的設計的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

首發649元 酷比魔術方塊小酷平板2 Lite來了:11吋護眼大螢幕+8000mAh大電量 首發649元 酷比魔術方塊小酷平板2 Lite來了:11吋護眼大螢幕+8000mAh大電量 Mar 05, 2024 pm 05:34 PM

3月4日消息,酷比魔方將於3月5日推出「小酷平板2Lite」平板電腦,首發價649元。據悉,新款平板搭載紫光展銳T606處理器,採用12nm工藝,由兩顆1.6GHz的ArmCortex-A75CPU和六顆ArmCortex-A55處理器組成。螢幕採用的是10.95吋IPS護眼屏,解析度為1280x800,亮度高至350尼特。影像方面,小酷平板2Lite後置1300萬像素主攝,前置500萬像素自拍鏡頭,另支援4G上網/通話、藍牙5.0、Wi-Fi5。此外,官方宣稱,這款平板電腦&l

首發899元 中興5G隨身Wi-Fi U50S開賣:最高網速500Mbps 首發899元 中興5G隨身Wi-Fi U50S開賣:最高網速500Mbps Apr 26, 2024 pm 03:46 PM

4月26日消息,中興5G隨身Wi-FiU50S目前已經正式開賣,首發899元。外觀設計上,中興U50S隨身Wi-Fi簡約時尚,易於手持和包裝。其尺寸為159/73/18mm,攜帶方便,讓您隨時隨地暢享5G高速網絡,實現暢行無阻的行動辦公與娛樂體驗。中興5G隨身Wi-FiU50S該設備支援先進的Wi-Fi6協議,峰值速率高達1800Mbps,依托驍龍X55高效能5G平台,為用戶提供極速的網路體驗。不僅支援5G雙模SA+NSA網路環境與Sub-6GHz頻段,實測網速更可達驚人的500Mbps,輕鬆滿

復古潮流! HMD與喜力聯合推出翻蓋手機:透明外殼設計 復古潮流! HMD與喜力聯合推出翻蓋手機:透明外殼設計 Apr 17, 2024 pm 06:50 PM

4月17日消息,HMD攜手知名啤酒品牌喜力以及創意公司Bodega,聯袂推出了一款獨特的翻蓋手機-無聊手機(TheBoringPhone)。這款手機不僅在設計上充滿新意,更在功能上返璞歸真,旨在引領人們回歸真實的人際交往,享受與朋友暢飲的純粹時光。無聊手機採用了獨特的透明翻蓋設計,展現出簡約而不失優雅的美感。其內部配備了2.8英寸QVGA顯示屏,外部則是一塊1.77英寸的顯示屏,為用戶提供了基本的視覺交互體驗。在攝影方面,雖然僅搭載了30萬畫素的鏡頭,但足以應付日常的簡

榮耀Magic V3首發AI離焦護眼技術:有效緩和近視發展 榮耀Magic V3首發AI離焦護眼技術:有效緩和近視發展 Jul 18, 2024 am 09:27 AM

7月12日消息,榮耀MagicV3系列今日正式發布,搭載全新榮耀視力舒緩綠洲護眼屏,在屏幕本身俱備高規格和高素質的同時,還開創性的引入AI主動式護眼技術。據悉,傳統的緩解近視的方式是“近視鏡”,近視眼鏡度數均勻分佈,保證了視線中心區域成像在視網膜之上,但周邊區域成像在視網膜後,視網膜感應到成像在後,促進眼軸向後生長,從而使度數加深。目前主要的緩解近視發展的方式之一是“離焦鏡”,其中心區域度數正常,週邊區域透過光學設計分區調整,從而使周邊區域成像落在視網膜前,

台電M50 Mini小平板來了:8.7吋IPS螢幕、5000mAh電池 台電M50 Mini小平板來了:8.7吋IPS螢幕、5000mAh電池 Apr 04, 2024 am 08:31 AM

4月3日消息,台電即將推出的M50Mini平板電腦是一款功能豐富、效能強大的裝置。這款8吋小平板新品搭載了8.7吋的IPS螢幕,為用戶提供了出色的視覺體驗。其金屬機身設計不僅美觀,也增強了設備的耐用性。在性能方面,M50Mini搭載了紫光展銳T606八核心處理器,擁有兩個A75核心和六個A55核心,確保了流暢且高效的運作體驗。同時,該平板還配備了6GB+128GB的儲存方案,並支援8GB記憶體擴展,滿足了用戶對於儲存和多任務處理的需求。在續航上,M50Mini配備了5000mAh的電池,支援Ty

ppt結束頁如何設計才夠吸引人 ppt結束頁如何設計才夠吸引人 Mar 20, 2024 pm 12:30 PM

在工作中,ppt是職場人士常使用的辦公室軟體。一個完整的ppt必須有一個好的結束頁。不同的職業要求賦予不同的ppt製作特色。關於結束頁的製作,如何才能設計的比較吸引人呢?下邊我們一起看一看,如何設計ppt結束頁吧! ppt結束頁的設計可以在文字和動畫方面進行一些調整,根據需要選擇簡潔或炫目的風格。接下來,我們將重點放在如何透過創新的表達方式來打造出符合要求的ppt結束頁。那我們就開始今天的教學吧。 1.對於結束頁的製作上,使用圖片中的任何文字都可以,結束頁重要的是表示我的簡報結束了。 2、除了這些文字,

vivo訊號最強手機! vivo X100s搭載寰宇訊號放大系統:21天線、360°環繞設計 vivo訊號最強手機! vivo X100s搭載寰宇訊號放大系統:21天線、360°環繞設計 Jun 03, 2024 pm 08:41 PM

5月13日消息,vivoX100s今晚正式發布,除了出色的影像,新機在訊號方面表現也十分強悍。根據vivo官方介紹,vivoX100s採用了創新的寰宇訊號放大系統,該系統配備了高達21根天線。這項設計基於直屏進行了重新優化,以平衡5G、4G、Wi-Fi、GPS以及NFC等眾多訊號需求。這使得vivoX100s成為了vivo有史以來訊號接收能力最強的手機。新款手機還採用了獨特的360°環繞設計,天線分佈在機身周圍。這項設計不僅增強了訊號的強度,還針對日常各種握持姿勢進行了優化,避免了因握持方式不當導

1399元起 榮耀X60i手機開售:視覺四等邊OLED直屏 1399元起 榮耀X60i手機開售:視覺四等邊OLED直屏 Jul 29, 2024 pm 08:25 PM

7月29日消息,榮耀X60i手機今日正式開售,先發1,399元。設計上,榮耀X60i手機採用居中挖孔直屏設計,四邊近乎無界的超窄邊框,大大拓寬了視野邊界。榮耀X60i參數顯示器:6.7吋高清顯示器電池:5000mAh大容量電池處理器:天璣6080處理器(台積電6nm,2x2.4G的A76+6×2G的A55)系統:MagicOS8.0系統其他功能: 5G訊號增強靈動膠囊螢幕下指紋雙MIC降噪知識問答攝影能力:後置雙攝系統:5000萬像素主攝200萬像素輔助鏡頭前置自拍鏡頭:800萬像素價格:8GB

See all articles