以前、プロモーションシステムの設計に関する記事を書き、フラッシュセール/直接割引/熟花スアンについて触れましたが、実際の仕事ではフラッシュセールシステムを実際にやったことがなく、そこで私は仮説を立てました。単純なフラッシュ セール システムは「欲求を解消する」ために作成され、プロモーションのアイデアは前の記事のデザインを引き続き踏襲しました。
フラッシュ セール中に大量のトラフィックが流入し、フラッシュ セールが開始される前にクエリが頻繁に更新されます。大量のトラフィックが瞬時にデータベースに到達すると、非常に危険です。データベースを崩壊させるのは簡単です。したがって、フラッシュ セールの主な仕事は、トラフィックをレイヤーごとにフィルタリングし、最終的にはできるだけ少ない および トラフィックをできるだけスムーズにデータベースに入力できるようにすることです。 通常のフラッシュ セールでは、多数のユーザーが少量の商品を購入する必要があります。このようなニーズの場合、在庫をキャッシュするだけで、実際に注文を作成する前に大量のトラフィックをフィルタリングできます。
でもでもでも、全然大変そうにないですよ!少し難易度を高めるために、フラッシュ セールが Xiaomi 携帯電話を手に入れるようなものだと仮定します。100 万人が 100,000 台の携帯電話を手に入れたらどうなるでしょうか? Xiaomi のラッシュ セール中に列に並ぶのも 1 つの方法です (ただし、エクスペリエンスはあまり良くありません)。フラッシュ セールのデザインは、将来的にはこの考えに基づいて行われる予定です。
Xiaomi に関して言えば、「運も強さの一部だ!」ということを教えてくれたと言わざるを得ません。また、次の記事にはサービス サーキット ブレーカーやマルチレベル キャッシュなどの高度なゲームプレイはなく、比較的単純なビジネス設計のみが記載されています。フロントエンドの電流制限方法: ランダム( 0, 1) ? axios.post: wait(30, 'All completed!')
次はコードの詳細を分析したものですが、原則として元のビジネス ロジックは最小限の変更で済みます。できるだけ。
開始
// 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 を引き続き使用しており、フロントエンドでロゴを渡すことに問題はありません。説明が必要な点は、通常、注文は 2 つのステップに分かれているということです。
最初のステップは、チェックアウト注文を生成する「チェックアウト (チェックアウト)」です。チェックアウト注文のアドレス、クーポン、支払い方法などを選択できます。
2 番目のステップは「確認」です。この時点で注文が確定し、在庫がロックされ、ユーザーは支払いを行うことができます。通常、規定の時間内に支払いが行われない場合、注文はキャンセルされ、在庫のロックが解除されます。
したがって、最初のステップでは、アドレスやクーポンの選択などの後続の操作がデータベースに影響を与えるのを防ぐために、ユーザーがフィルタリングされてキューに登録されます。
# 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 にはデータベース操作が含まれていないことがわかります。そして、オーダーを作成するタスクはディスパッチによってキューに分散され、ユーザーはキューに入った順に対応する時間列に並んで待ちます。
ここでの問題は、注文が正常に作成された後にクライアントにどのように通知するかということです。
クライアント通知
バックエンド
# 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 であるとします。要約すると、上記のコードは、「OrderChecked」イベントを WebSocket のプライベート チャネル「App.User.1」にプッシュすることです。
フロントエンド
// views/products/show.vue <script> import Echo from 'laravel-echo' import io from 'socket.io-client' window.io = io export default { name: 'App', methods: { async snapUpCheckout () { try { // await post -> snap-up-checkout this.toCheckout() } catch (error) { // 秒杀失败 } }, toCheckout () { // 建立 websocket 连接 const echo = new Echo({ broadcaster: 'socket.io', host: 'http://api.e-commerce.test:6001', auth: { headers: { Authorization: 'Bearer ' + this.store.auth.token } } }) // 监听私有频道 App.User.{id} 的 OrderChecked 事件 echo.private('App.User.' + this.store.user.id).listen('OrderChecked', (e) => { // redirect to checkou page }) } } } </script>
laravel-echoを使用する際に注意すべき点の1つは、プライベートチャネルの使用により、laravel-echoはサーバーAPI
/broadcasting/authに投稿リクエストを送信することです。認証のデフォルト。ただし、ブレード テンプレートの代わりにフロントエンド カテゴリとバックエンド カテゴリが使用されるため、必要な認証を実行するための csrf トークンとセッションを簡単に取得することはできません。 したがって、ブロードキャストと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 接続を切断した後に、ロックされた注文の在庫を返すことです。チェックアウト注文が作成されると、長期間操作されなかった注文に在庫を戻すための遅延キューが作成されます。
しかし、laravel-echo はブロードキャスト システムであり、クライアント切断イベントのコールバックを提供しません。laravel-echo-server にフックを追加するなど、laravel がリッスンするクライアント イベントを実装する方法がいくつかあります。 laravel ですが、laravel-echo-server の実装を変更する必要があります。ここでは詳細には触れませんが、フラッシュ セールのアイデアを提供することに重点を置いています。
上の図は、フラッシュ キル システムの論理的な概要です。この時点で、フラッシュセールのプロセス全体が終了しますが、一般に、コードの量はそれほど多くなく、ロジックは比較的単純です。
図からわかるように、プロセス全体で、キュー内でのみ mysql と対話し、キューの 電流制限を通じて、耐久性に適応できます。 mysql を最大限に活用します。 mysql のパフォーマンスが十分である場合、ユーザーは多数のキューを通じて同時に注文を消費でき、ユーザーはキューのプロセスをまったく意識しません。
ご質問やより良いアイデアがある場合は、ディスカッション用のメッセージを残してください~
Laravel 関連の技術記事の詳細については、Laravel チュートリアル 列にアクセスして学習してください。 !
以上がフラッシュキルシステムの設計の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。