之前写过一篇关于 促销系统的设计 中提到了秒杀/直减/聚划算,但在实际工作中,并没有真的做过秒杀系统,所以假想了一个简单的秒杀系统来”解解馋“,促销思路依旧顺延之前的文章设计。
分析
秒杀时大量的流量涌入,秒杀开始前频繁刷新查询,如果大量的流量瞬间冲击到数据库的话,非常容易造成数据库的崩溃。所以秒杀的主要工作就是对流量进行层层筛选最后让尽可能少且平缓的流量进入到数据库。
通常的秒杀是大量的用户抢购少量的商品,类似这样的需求只需要简单的进行库存缓存,就能在实际创建订单前过滤大量的流量。
但但但是,只是这样的话好像没什么挑战力呀!稍微加大一下难度,假设我们的秒杀是像抢小米手机一样,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 < $quantity) { $lock->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 '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 使用时需要注意的一点,由于使用了私有频道,所以 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教程栏目进行学习!
Atas ialah kandungan terperinci 秒杀系统的设计. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Laravel menonjol dengan memudahkan proses pembangunan web dan menyampaikan ciri -ciri yang kuat. Kelebihannya termasuk: 1) Sintaks ringkas dan sistem ORM yang kuat, 2) Sistem penghalaan dan pengesahan yang cekap, 3) sokongan perpustakaan pihak ketiga yang kaya, yang membolehkan pemaju memberi tumpuan kepada menulis kod elegan dan meningkatkan kecekapan pembangunan.

LaravelispredomininglyableDFramework, direka bentuk-sidelogic, databasemenagement, dan perkembangan, evenitalsupportsfrontenddevelopmentwithbladetemplates.

Laravel dan Python mempunyai kelebihan dan kekurangan mereka sendiri dari segi prestasi dan skalabiliti. Laravel meningkatkan prestasi melalui sistem pemprosesan dan beratur yang tidak segerak, tetapi disebabkan oleh batasan PHP, mungkin terdapat kesesakan apabila kesesuaian tinggi hadir; Python berfungsi dengan baik dengan kerangka asynchronous dan ekosistem perpustakaan yang kuat, tetapi dipengaruhi oleh GIL dalam persekitaran yang pelbagai.

Laravel sesuai untuk projek -projek yang pasukannya biasa dengan PHP dan memerlukan ciri -ciri yang kaya, manakala rangka kerja Python bergantung kepada keperluan projek. 1. Laravel menyediakan sintaks elegan dan ciri -ciri yang kaya, sesuai untuk projek yang memerlukan perkembangan dan fleksibiliti pesat. 2. Django sesuai untuk aplikasi yang kompleks kerana konsep "inklusi bateri" nya. 3.Flask sesuai untuk prototaip cepat dan projek kecil, memberikan fleksibiliti yang hebat.

Laravel boleh digunakan untuk pembangunan front-end. 1) Gunakan enjin templat bilah untuk menghasilkan HTML. 2) Mengintegrasikan Vite untuk menguruskan sumber hadapan. 3) Membina spa, PWA atau laman web statik. 4) Menggabungkan penghalaan, middleware dan eloquentorm untuk membuat aplikasi web lengkap.

PHP dan Laravel boleh digunakan untuk membina aplikasi sisi pelayan yang cekap. 1.Php adalah bahasa skrip sumber terbuka yang sesuai untuk pembangunan web. 2. Laravel menyediakan penghalaan, pengawal, eloquentorm, enjin templat bilah dan fungsi lain untuk memudahkan pembangunan. 3. Meningkatkan prestasi aplikasi dan keselamatan melalui caching, pengoptimuman kod dan langkah keselamatan. 4. Strategi Ujian dan Penggunaan untuk memastikan operasi aplikasi yang stabil.

Laravel dan Python mempunyai kelebihan dan kekurangan mereka sendiri dari segi keluk pembelajaran dan kemudahan penggunaan. Laravel sesuai untuk pembangunan pesat aplikasi web. Kurva pembelajaran agak rata, tetapi memerlukan masa untuk menguasai fungsi lanjutan. Tatabahasa Python adalah ringkas dan lengkung pembelajaran adalah rata, tetapi sistem jenis dinamik perlu berhati -hati.

Kelebihan Laravel dalam pembangunan back-end termasuk: 1) sintaks elegan dan eloquentorm memudahkan proses pembangunan; 2) ekosistem yang kaya dan sokongan komuniti aktif; 3) Kecekapan pembangunan yang lebih baik dan kualiti kod. Reka bentuk Laravel membolehkan pemaju untuk membangunkan lebih cekap dan meningkatkan kualiti kod melalui ciri dan alat yang kuat.


Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

Video Face Swap
Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Artikel Panas

Alat panas

Penyesuai Pelayan SAP NetWeaver untuk Eclipse
Integrasikan Eclipse dengan pelayan aplikasi SAP NetWeaver.

mPDF
mPDF ialah perpustakaan PHP yang boleh menjana fail PDF daripada HTML yang dikodkan UTF-8. Pengarang asal, Ian Back, menulis mPDF untuk mengeluarkan fail PDF "dengan cepat" dari tapak webnya dan mengendalikan bahasa yang berbeza. Ia lebih perlahan dan menghasilkan fail yang lebih besar apabila menggunakan fon Unicode daripada skrip asal seperti HTML2FPDF, tetapi menyokong gaya CSS dsb. dan mempunyai banyak peningkatan. Menyokong hampir semua bahasa, termasuk RTL (Arab dan Ibrani) dan CJK (Cina, Jepun dan Korea). Menyokong elemen peringkat blok bersarang (seperti P, DIV),

DVWA
Damn Vulnerable Web App (DVWA) ialah aplikasi web PHP/MySQL yang sangat terdedah. Matlamat utamanya adalah untuk menjadi bantuan bagi profesional keselamatan untuk menguji kemahiran dan alatan mereka dalam persekitaran undang-undang, untuk membantu pembangun web lebih memahami proses mengamankan aplikasi web, dan untuk membantu guru/pelajar mengajar/belajar dalam persekitaran bilik darjah Aplikasi web keselamatan. Matlamat DVWA adalah untuk mempraktikkan beberapa kelemahan web yang paling biasa melalui antara muka yang mudah dan mudah, dengan pelbagai tahap kesukaran. Sila ambil perhatian bahawa perisian ini

Muat turun versi mac editor Atom
Editor sumber terbuka yang paling popular

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa