【Java】如何让系统抗住双十一的预约抢购活动?

一、问题解析

在大促活动期间,“预约抢购”已经是各大电商平台的主要促销手段,京东自然也会和一些大的供应商合作,推出一些低价的爆款产品,比如 2019 年的 “1499 元抢购飞天茅台”活动,就让很多人每天准时准点拿着手机拼人品。

那这类电商领域的大促抢购场景涉及专栏的哪些内容呢?它们是怎么通过架构设计的方式组合在一起,实现一个完整的需求流程呢?这就是今天要讨论的话题。

我们先把需求梳理一下,总的来说,实现一个抢购系统大概可以分为四个阶段。

  • 商品预约:用户进入商品详情页面,获取购买资格,并等待商品抢购倒计时。
  • 等待抢购:等待商品抢购倒计时,直到商品开放抢购。
  • 商品抢购:商品抢购倒计时结束,用户提交抢购订单,排队等待抢购结果,抢购成功后,扣减系统库存,生成抢购订单。
  • 订单支付:等待用户支付成功后,系统更新订单状态,通知用户购买成功。

接下来,我们就针对各阶段容易出现的问题,来分析其中的技术考点和解决方案。

17.1 商品预约阶段

这几年,很多电商平台为了方便流量运营,改造了传统秒杀场景,通过先预约再抢购的方式预热商品,并根据预约量调整运营策略。而且在预约抢购的活动中,为了增加商品售卖量,会允许抢购前,预约资格超过实际的库存数量。

那么问题来了:如何在高并发量的情况下,让每个用户都能得到抢购资格呢?这是预约抢购场景第一个技术考察点。 那你可以基于“06 | 分布式系统中,如何回答锁的实现原理?”来控制抢购资格的发放。

我们基于 Redis 实现分布式锁(这是最常用的方式),在加锁的过程中,实际上是给 Key 键设置一个值,为避免死锁,还要给 Key 键设置一个过期时间。

SET lock_key unique_value NX PX 10000
  • lock_key 就是 key 键;
  • unique_value 是客户端生成的唯一的标识;
  • NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;
  • PX 10000 表示设置 lock_key 的过期时间为 10s,这是为了避免客户端发生异常而无法释放锁。

而解锁的过程就是将 lock_key 键删除,但不能乱删,要保证执行操作的客户端就是加锁的客户端。而这个时候, unique_value 的作用就体现出来,你可以通过 Lua 脚本判断 unique_value 是否为加锁客户端。

选用 Lua 脚本是为了保证解锁操作的原子性。因为 Redis 在执行 Lua 脚本时,可以以原子性的方式执行,保证了锁释放操作的原子性。

// 释放锁时,先比较 unique_value 是否相等,避免锁的误释放if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])elsereturn0end

这样一来,就通过使用 SET 命令和 Lua 脚本在 Redis 单节点上完成了分布式锁的加锁和解锁。但你要注意,此方案是基于单节点的 Redis 实例实现的,如果此时 Redis 实例发生故障宕机,那么锁变量就没有了,客户端也就无法进行锁操作,就会影响到业务的正常执行。 所以,基于 Redis 实现分布式锁时,你还要掌握如何保证锁的可靠性,也就是怎么基于多个 Redis 节点实现分布式锁(这部分也可以参考 06 讲中的内容)。

17.2 等待抢购阶段

用户预约成功之后,在商品详情页面中,会存在一个抢购倒计时,这个倒计时的初始时间是从服务端获取的,用户点击购买按钮时,系统还会去服务端验证是否已经到了抢购时间。

在等待抢购阶段,流量突增,因为在抢购商品之前(尤其是临近开始抢购之前的一分钟内),大部分用户会频繁刷新商品详情页,商品详情页面的读请求量剧增, 如果商品详情页面没有做好流量控制,就容易成为整个预约抢购系统中的性能瓶颈点。

那么问题来了:如何解决等待抢购时间内的流量突增问题呢?有两个解决思路。

  • 页面静态化:提前对抢购商品的详情页面做静态化,生成一个静态页面,再把页面放到距离用户最近的 CDN 节点中,这样一来,当浏览器访问页面时,就会自动缓存该页面的静态资源文件(对于静态化技术,很多页面端的模板引擎都支持这样的功能,我就不展开讲了)。
  • 服务端限流:对商品详情页中的动态请求接口设置最大并发访问数量(具体的数量根据上线前的性能压测为准),防止超出预期的请求集中访问系统,造成系统压力过载。操作上,你可以在商品详情页的后端系统入口层(如网关系统)中进行接口限流,如果使用 Nginx 来做反向代理,可以直接基于 Nginx 配置限流算法,比如 Nginx 的 ngx_http_limit_req_module(限制单位时间内所有 IP 的请求数量)和 ngx_stream_limit_conn_module(限制单位时间内单个 IP 的请求数量)两个模块就提供了限流控制的功能,所以你还要提前掌握限流策略的原理,如令牌桶算法的原理。

17.3 商品抢购阶段

在商品抢购阶段,用户会点击提交订单,这时,抢购系统会先校验库存,当库存足够时,系统会先扣减库存,然后再生成订单。在这个过程中,短时间之内提交订单的写流量非常高,所以为了做流量削峰,会将提单请求暂存在消息队列中,并提示用户“抢购排队中……”然后再由后端服务异步处理用户的请求。

而你可以基于数据库和缓存两种方式,来实现校验库存和扣减库存的操作。

但因为抢购场景的瞬时流量极高,一般不会直接基于数据库来实现(因为每次操作数据库,即使通过消息队列做了流量削峰,对数据库来说压力也很大,会产生性能瓶颈)。如果非要基于数据库的话,你要通过分布式锁来优化扣减库存的并发操作,但此阶段的分布式锁对可靠性的要求会极高(因为在大促抢购阶段,小的可用性故障,都可能造成大的线上事故),所以基于单节点 Redis 实现的分布式锁不合适,你要选择多节点 Redis 实现分布式锁,或者选型 ZooKeeper。

为了避免上述问题,我们一般基于缓存来存储库存,实现扣减库存的操作。这样在提交订单时,库存的查询和锁定就不会给数据库带来性能瓶颈。不过你仍要注意,基于缓存(如 Redis)的库存扣减操作,仍要考虑缓存系统的单点问题,就算是多节点存储库存,也要引入锁的策略,保证 Redis 实现库存的一致性。

实现了校验库存和扣减库存之后,最后一步是生成抢购订单。由于数据库表会承载订单数据,一旦出现瞬时流量,磁盘 I/O、数据库请求连接数等资源都会出现性能瓶颈,你可以考虑对订单表分库分表,通过对用户 ID 字段进行 Hash 取模,实现分库分表,提高系统的并发能力。

从“商品抢购阶段的架构设计”中我们可以总结出三个技术考点:流量削峰、扣减库存、分库分表。

  • “流量削峰”的面试考点

流量削峰是由于正式抢购场景下,短时间内的提单请求非常高,所以引入消息队列做异步化,然后在抢购系统的后端服务中,启动若干个队列处理消息队列中的提单请求,再执行校验库存、下单等逻辑。

那么如何快速处理消息队列中的提单请求,避免出现大量的消息积压,就是本阶段的考点之一了,方案可以参考“08 | MQ:如何回答消息队列的丢失、重复与积压问题?”

  • “扣减库存”的面试考点

我刚刚提到,当基于 Redis 实现库存的扣减时,要考虑怎么解决 Redis 的单点问题。而如果基于 Redis 集群来实现扣减库存,还要解决 Redis 在哨兵模式部署的情况下,因为主从切换带来的数据不一致的问题。这就涉及“06 | 分布式系统中,如何回答锁的实现原理?”中的内容。

  • “分库分表”的面试考点

生成订单后如何实现分库分表?你可以参考“04 | 亿级商品存储下,如何深度回答分布式系统的原理性问题?”中的解决方案。

当然还有一个容易忽略的问题:带宽的影响。由于抢购入口的请求量会非常大,可能会占用大量带宽,为了不影响提交订单的请求,有时会从网络工程的角度解决,通过单独的子域名绑定独立的网络服务器,这里就会涉及 DNS 的设计与优化手段。

17.4 订单支付阶段

在用户支付订单完成之后,一般会由支付平台回调系统接口,更新订单状态。在支付回调成功之后,抢购系统还会通过异步通知的方式,实现订单更新之外的非核心业务处理,比如积分累计、短信通知等,此阶段可以基于 MQ 实现业务的异步操作。

订单支付后操作

不过针对服务的异常(如宕机),会存在请求数据丢失的可能,比如当支付回调系统后,修改订单状态成功了,但是在异步通知积分系统,更新用户累计积分时,订单系统挂掉了,此时 MQ 还没有收到这条消息,那么这条消息数据就无法还原了。

订单支付后操作(异常)

所以你还要考虑“05 | 海量并发场景下,如何回答分布式事务一致性问题?”中,可靠消息投递机制:先做消息的本地存储,再通过异步重试机制,来实现消息的补偿。比如当支付平台回调订单系统,然后在更新状态的同时,插入一个消息,之后再返回第三方支付操作成功的结果。最后,通过数据库中的这条消息,再异步推送其他系统,完成后续的工作。

二、粉丝福利

最近很多同学问我有没有java学习资料,我根据我从小白到架构师多年的学习经验整理出来了一份80W字面试解析文档、简历模板、学习路线图、java必看学习书籍 、 需要的小伙伴 可以关注我
公众号:“ 灰灰聊架构 ”, 回复暗号:“ 159 ”即可获取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/22401.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CET-4 作文模板

1. 解决问题型 写作要点 问题现状 The issue of [具体问题] has become increasingly prominent in recent years, particularly in [具体领域]. [数据/例子说明现状]. Furthermore, [数据/例子说明现状]. This trend is evident in [具体案例/数据], highlighting the severi…

720云「3D空间漫游」功能爆发!户型标注、自动导览、切换视图…

一、新增 [开场封面] 支持图片、视频开场 作品第一印象必须惊艳!使用频率极高的功能,终于给3D漫游安排上啦~你可以自定义上传一张图片或一段视频,支持对桌面端、移动端分别进行设置并预览,完美适配不同终端。 二、升级模型交互体验…

《QT从基础到进阶·四十二》QT运行后项目图标,exe图标问题,VS加载.pro文件问题

1、QT图标有时候不能正常显示,不管是加到qrc还是用绝对路径,都无法正常显示,之前是可以的,具体原因目前还不太清楚,我在VS项目——vcpkg——use vcpkg把否改为是就可以了 2、出现无法定位程序输入点的报错&#xff0c…

【教学类-13-05】20240604《数字色块图-5*7*8-A4横板-横切》中4班

背景需求: 【教学类-13-04】20230404《数字色块图判断密码是否正确-5*7*8-A4横板-横切》(中班主题《我爱我家》)_图案密码色块-CSDN博客文章浏览阅读530次。【教学类-13-04】20230404《数字色块图判断密码是否正确-5*7*8-A4横板-横切》(中班主…

Java中加号的多种用途

在Java中, 符号有多种用途,主要根据上下文而定。以下是在Java中的一些主要用途: 加法运算符: 这是最常见的用途,用于数字相加。 int a 5;int b 3;int sum a b; // sum is 8 字符串连接符: 当用…

常用运维工具之 WGCLOUD(国产软件)介绍

WGCLOUD是一款免费开源的运维监控软件,轻量高效,部署方便,上手简单,界面简单流畅 WGCLOUD是国产运维软件,可以适配大部分的信创环境,比如麒麟、统信等操作系统 WGCLOUD具体支持监控的操作系统如下&#x…

网络的功能和实现方法简介

网络的功能: 计算机网络是研究怎么样在两个端用户之间提供访问通路的。所以网络的功能是为网络上的任意两个端用户之间提供访问通路。 计算机通信的特点: 间歇性和突发性。即时而线路中没有信息流过,时而突来的大量数据需要迅速传输。为此计…

s32k314【入门新手篇】-开发环境安装【ds32开发平台】

软件包下载 登录nxp官网下载:https://www.nxp.com/ 然后输入关键字:S32 查看 下载安装包 以上三步请先注册好并登录你的个人账号 下载完之后如下: 软件安装 eb安装并激活【试用版】 激活 2 安装ds 弹出什么就安装什么就好了。 …

kettle从入门到精通 第六十五课 ETL之kettle 执行动态SQL语句,轻松实现全量增量数据同步

本次课程的逻辑是同步t1表数据到t2表,t1和t2表的表机构相同,都有id,name,createtime三个字段。 CREATE TABLE t1 (id bigint NOT NULL AUTO_INCREMENT,name varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,cr…

C++系列-STL简介

🌈个人主页:羽晨同学 💫个人格言:“成为自己未来的主人~” 什么是STL STL是C标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。 STL的版本 原始版本 Alexander Stepa…

WHAT - 容器化系列(五)- 计算

目录 前言:云计算一、PaaS 和容器化On-site、IaaS、PaaS、SaaSPass 和容器二、容器的本质三、从零构建一个 docker四、k8s CRICRIcontainerdpod的调度前言:云计算 云计算(Cloud Computing)是一种基于互联网的计算方式,它将计算资源(如服务器、存储、网络、应用程序等)通…

History 模式和 Hash 模式路由的区别、优缺点及在开发生产环境中的注意事项

在现代单页应用(SPA)开发中,前端路由是至关重要的一部分。常见的路由模式有两种:History 模式和 Hash 模式。本文将详细探讨这两种模式的区别、优缺点,并在开发和生产环境中的注意事项。 路由模式简介 Hash 模式 H…

网工内推 | 上市公司网工,Base广东,思科DE/IE认证优先

01 广州赛意信息科技股份有限公司 🔷招聘岗位:技术架构师 🔷职责描述: 1、设计、开发和维护工业数据库及其架构,包括数据采集、存储、处理和分析的工具和系统。 2、开发和维护数据管道和工作流程,确保数据…

通过Excel,生成sql,将A表数据插入B表

文章目录 投机取巧的方式,进行表数据初始化通过navicat搜索A表数据,然后复制进excel中通过excel的函数方式,将该批量数据自动生成插入B表的sql语句然后一次性拷贝生成的sql语句,放进navicat中一次执行,直接完成数据初始化

美容美发门店收银管理系统源码分享-美业系统App端闪退怎么办?

美业SaaS系统 连锁多门店美业收银系统源码 多门店管理 / 会员管理 / 预约管理 / 排班管理 / 商品管理 / 活动促销 PC管理后台、手机APP、iPad APP、微信小程序 ▶ 手机App应用闪退怎么办?博弈美业系统为例 • 可能原因: 1、手机版本过低 2、未更新…

域内路由选择协议——RIP

例题 RIP(Routing Information Protocol)是一种基于距离向量的路由协议,使用跳数作为度量标准来决定最优路径。下面我们详细分析为什么RIP协议要这样设计。 RIP协议的基本工作原理 距离向量算法: 每个路由器维护一张路由表&…

MySQL Hints:控制查询优化器的选择

码到三十五 : 个人主页 MySQL Hints是优化数据库查询性能的一种强大工具。它们允许开发者在SQL查询中嵌入指令,以影响MySQL优化器的决策过程。在某些情况下,优化器可能无法选择最佳的查询执行计划,这时我们可以使用Hints来引导优化…

【光谱特征选择】连续投影算法SPA(含python代码)

目录 一、背景 二、代码实现 三、项目代码 一、背景 连续投影算法(Successive Projection Algorithm,SPA)是一种用于光谱分离的简单且有效的算法。它主要应用于高光谱图像处理,用于提取混合光谱数据中的端元(endme…

生命周期钩子小案例

文章目录 一、在created中发送数据二、在mounted中获取焦点 一、在created中发送数据 <body><div id"app"><ul><li v-for"(item, index) in list" :key"item.id" class"news"><div class"left"…

ES最新提案(下一代ES)

目录 do表达式throw表达式函数的部分执行管道运算符数值分隔符Math.signbit()双冒号运算符Realm API#!命令import.metado表达式 Do 表达式是一种提案,旨在使块级作用域(如 {…} 内部)能够返回一个值。在现有的JavaScript中,块级作用域通常不返回任何值。提案中的 do 关键字…