高并发核心编程SpringCloud+Nginx秒杀实战,秒杀系统的系统架构

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
云原生内存数据库 Tair,内存型 2GB
简介: 秒杀系统的系统架构本节分多个维度介绍crazy-springcloud开发脚手架的架构,包括分层架构、限流架构、分布式锁架构、削峰的架构。秒杀的分层架构从分层的角度来说,秒杀系统架构可以分成3层,大致如下:(1)客户端:负责内容提速和交互控制。(2)接入层:负责认证、负载均衡、限流。(3)业务层:负责保障秒杀数据的一致性。

秒杀系统的系统架构

本节分多个维度介绍crazy-springcloud开发脚手架的架构,包括分层架构、限流架构、分布式锁架构、削峰的架构。

秒杀的分层架构

从分层的角度来说,秒杀系统架构可以分成3层,大致如下:

(1)客户端:负责内容提速和交互控制。

(2)接入层:负责认证、负载均衡、限流。

(3)业务层:负责保障秒杀数据的一致性。

1.客户端负责内容提速和交互控制

客户端需要完成秒杀商品的静态化展示。无论是在桌面浏览器还是在移动端App展示秒杀商品,秒杀商品的图片和文字元素都需要尽可能静态化,尽量减少动态元素。这样就可以通过CDN来提速和抗峰值。

另外,在客户端这一层的用户交互上需要具备一定的控制用户行为和禁止重复秒杀的能力。比如,当用户提交秒杀请求之后,可以将秒杀按钮置灰,禁止重复提交。

2.接入层负责认证、负载均衡、限流

秒杀系统的特点是并发量极大,但实际的优惠商品有限,秒杀成功的请求数量很少,如果不在接入层进行拦截,大量请求就会造成数据库连接耗尽、服务端线程耗尽,导致整体雪崩。因此,必须在接入层进行用户认证、负载均衡、接口限流。

对于总流量较小的系统,可以在内部网关(如Zuul)完成用户认证、负载均衡、接口限流的功能,具体的分层架构如图10-2所示。

网络异常,图片无法展示
|

图10-2 在内部网关(如Zuul)完成认证、负载均衡、接口限流

对于总流量较大的系统会有一层甚至多层外部网关,因此限流的职责会从内部网关剥离到外部网关,内部网关(如Zuul)仍然具备权限认证、负载均衡的能力,具体的分层架构如图10-3所示。

网络异常,图片无法展示
|

图10-3 外部网关与内部网关相结合完成权限认证、负载均衡、接口限 流

3.业务层负责保障数据一致性

秒杀的业务逻辑主要是下订单和减库存,都是数据库操作。大家都知道,数据库层只能承担“能力范围内”的访问请求,既是非常脆弱的一层,又是需要进行事务保护的一层。在业务层还需要防止超出库存的秒杀(超卖和少卖),为了安全起见,可以使用分布式锁对秒杀的数据库操作进行保护。

秒杀的限流架构

前面提到,秒杀系统中的秒杀商品总是有限的。除此之外,服务节点的处理能力、数据库的处理能力也是有限的,因此需要根据系统的负载能力进行秒杀限流。

总体来说,在接入层可以进行两个级别的限流策略:应用级别的限流和接口级别的限流。

什么是应用级别的限流策略呢?对于整个应用系统来说,一定会有一个QPS的极限值,如果超了极限值,整个应用就会不响应或响应得非常慢。因此,需要在整个应用的维度做好应用级别的限流配置。应用级别的限流应该配置在最顶层的反向代理,具体如图10-4所示。

网络异常,图片无法展示
|

图10-4 应用级别的限流

应用级别的流量限制可以通过Nginx的limit_req_zone和limit_req两个指令完成。假定要配置Nginx虚拟主机的限流规则为单IP限制为每秒1次请求,整个应用限制为每秒10次请求,那么具体的配置如下:

limit_req_zone $binary_remote_addr zone=perip:10m rate=1r/s;
limit_req_zone $server_name zone=perserver:1m rate=10r/s;
server {
 ...
 limit_req zone=perip burst=5;
 limit_req zone=perserver burst=10;
}

什么是接口级别的限流策略呢?单个接口可能会有突发访问情况,可能会由于突发访问量太大造成系统崩溃,典型的就是本章所介绍的秒杀类接口。接口级别的限流就是配置单个接口的请求速率,是细粒度的限流。

接口级别的限流也可以通过Nginx的limit_req_zone和limit_req两个指令配合完成,对获取秒杀令牌的接口,同时进行用户Id和商品Id限流的配置如下:

limit_req_zone $arg_goodId zone=pergood:10m rate=100r/s;
limit_req_zone $arg_userId zone=peruser:1m rate=1r/s;
server {
 #lua:获取秒杀token令牌
 location = /seckill-provider/api/seckill/redis/token/v2 {
 limit_req zone=peruser burst=5;
 limit_req zone=pergood burst=10;
 #获取秒杀token lua脚本
 content_by_lua_file luaScript/module/seckill/getToken.lua;
 }
}

以上定义了两个限流规则:pergood和peruser:pergood规则根据请求参数的goodId值进行限流,同一个goodId值的限速为每秒100次请求;peruser规则根据请求参数的userId值进行限流,同一个userId值的限速为每秒1次请求。

但是,Nginx的限流指令只能在同一块内存区域有效,而在生产场景中秒杀的外部网关往往是采用多节点部署的,所以这就需要用到分布式限流组件。高性能的分布式限流组件可以使用Redis+Lua来开发,京东的抢购就是使用Redis+Lua完成限流的,并且无论是Nginx外部网关还是Zuul内部网关,都可以使用Redis+Lua限流组件。

理论上,接入层的限流有多个维度:

(1)用户维度的限流:在某一时间段内只允许用户提交一次请求,比如可以采取客户端IP或者用户ID作为限流的key。

(2)商品维度的限流:对于同一个抢购商品,在某个时间段内只允许一定数量的请求进入,可以采取秒杀商品ID作为限流的key。

无论是哪个维度的限流,只要掌握其中的一个,其他维度的限流在技术实现上都是差不多的。本书的秒杀练习使用的是接口级别的限流策略,在获取秒杀令牌的REST接口时,针对每个秒杀商品的ID配置限流策略,限制每个商品ID每秒内允许通过的请求次数。

如果大家对进行用户维度的限流感兴趣,可以自行修改配置进行尝试。

秒杀的分布式锁架构

前面提到了超卖或少卖问题:比如10万次请求同时发起秒杀请求,正常需要进行10万次库存扣减,但是由于某种原因,往往会造成多减库存或者少减库存,这就会出现超卖或少卖问题。

解决超卖或者少卖问题有效的办法之一就是利用分布式锁将对同一个商品的并行数据库操作予以串行化。秒杀场景的分布式锁应该具备如下条件:

(1)一个方法在同一时间只能被一个机器的一个线程执行。

(2)高可用地获取锁与释放锁。

(3)高性能地获取锁与释放锁。

(4)具备可重入特性。

(5)具备锁失效机制,防止死锁。

(6)具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

常用的分布式锁有两种:ZooKeeper分布式锁和Redis分布式锁。如果使用ZooKeeper分布式锁来保护秒杀的数据库操作,那么它的架构图大致如图10-5所示。

网络异常,图片无法展示
|

图10-5 使用ZooKeeper分布式锁来保护秒杀的数据库操作

实际上,除了提供分布式锁外,ZooKeeper还能提供高可靠的分布式计数器、高可靠的分布式ID生成器的基础能力。ZooKeeper分布式计数器、分布式锁、分布式ID生成器等基础知识也是大家必须系统地学习和掌握的知识,但是不属于在本书介绍的内容,如果对这一块不了解,可翻阅本书姊妹篇《Netty、Redis、ZooKeeper高并发实战》。

ZooKeeper分布式锁虽然高可靠,但是性能不高,不能满足秒杀场景分布式锁的第3个条件(高性能地获取锁与释放锁),所以在秒杀的场景建议使用Redis分布式锁来保护秒杀的数据库操作。

秒杀的削峰架构

通过接入网关的限流能够拦截无效的刷单请求和超出预期的那部分请求,但是,当秒杀的订单量很大时,比如有100万商品需要参与秒杀,这时后端服务层和数据库的并发请求压力至少为100万。这种请求下,需要使用消息队列进行削峰。

削峰从本质上来说就是更多地延缓用户请求,以及层层过滤用户的访问需求,遵从“最后落地到数据库的请求数要尽量少”的原则。通过消息队列可以大大地缓冲瞬时流量,把同步的直接调用转换成异步的间接推送,中间通过一个队列在入口承接瞬时的流量洪峰,在出口平滑地将消息推送出去。消息队列就像“水库”一样,拦蓄上游的洪水,削减进入下游河道的洪峰流量,从而达到减免洪水灾害的目的。使用消息队列对秒杀进行削峰的架构如图10-6所示。

网络异常,图片无法展示
|

图10-6 使用消息队列对秒杀进行削峰

对于秒杀消息的入队可以直接在内部网关完成。内部网关在完成用户的权限验证、秒杀令牌的有效性验证之后,将秒杀消息发往消息队列即可。秒杀服务通过消息队列的订阅完成秒杀消息的消费。常用消息队列系统:Kafka、RocketMQ、ActiveMQ、RabbitMQ、ZeroMQ、MetaMQ等。本书的内容主要聚焦在Spring Cloud和Nginx,对消息队列这里不做过多的介绍,使用消息队列进行削峰的秒杀实现版本可参见后续的疯狂创客圈社群博客。

本文给大家讲解的内容是高并发核心编程,Spring Cloud+Nginx秒杀实战,秒杀系统的系统架构

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
9天前
|
Java UED Sentinel
微服务守护神:Spring Cloud Sentinel,让你的系统在流量洪峰中稳如磐石!
【8月更文挑战第29天】Spring Cloud Sentinel结合了阿里巴巴Sentinel的流控、降级、熔断和热点规则等特性,为微服务架构下的应用提供了一套完整的流量控制解决方案。它能够有效应对突发流量,保护服务稳定性,避免雪崩效应,确保系统在高并发下健康运行。通过简单的配置和注解即可实现高效流量控制,适用于高并发场景、依赖服务不稳定及资源保护等多种情况,显著提升系统健壮性和用户体验。
30 1
|
2天前
|
运维 监控 持续交付
深入浅出:微服务架构的设计与实战
微服务,一个在软件开发领域如雷贯耳的名词,它代表着一种现代软件架构的风格。本文将通过浅显易懂的语言,带领读者从零开始了解微服务的概念、设计原则及其在实际项目中的运用。我们将一起探讨如何将一个庞大的单体应用拆分为灵活、独立、可扩展的微服务,并分享一些实践中的经验和技巧。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和深入的理解。
21 3
|
12天前
|
负载均衡 应用服务中间件 网络安全
Django后端架构开发:Nginx服务优化实践
Django后端架构开发:Nginx服务优化实践
29 2
|
20天前
|
存储 缓存 负载均衡
高并发系统架构的设计挑战与应对策略
【8月更文挑战第18天】高并发系统架构设计是一项复杂而重要的任务。面对性能瓶颈、稳定性与可靠性、并发控制和可扩展性等挑战,开发人员需要采取一系列有效的策略和技术手段来应对。通过负载均衡、缓存技术、数据库优化、异步处理、并发控制、弹性设计及监控与调优等手段,可以设计出高性能、高可用和高可扩展性的高并发系统架构,为用户提供优质的服务体验。
|
19天前
|
Java 微服务 Spring
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
文章介绍了如何利用Spring Cloud Alibaba快速构建大型电商系统的分布式微服务,包括服务限流降级等主要功能的实现,并通过注解和配置简化了Spring Cloud应用的接入和搭建过程。
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
|
21天前
|
弹性计算 监控 数据挖掘
事件驱动架构的优势与应用:深度解析与实战应用
【8月更文挑战第17天】事件驱动架构以其松耦合、可扩展性、异步处理、实时性和高可靠性等优势,在实时数据处理、复杂业务流程、弹性伸缩和实时通信等多个领域展现出巨大的应用潜力。通过合理应用事件驱动架构,可以构建灵活、可扩展和可维护的系统架构,满足不断变化的业务需求和技术挑战。对于开发者而言,深入理解事件驱动架构的核心概念和优势,将有助于更好地设计和实现高质量的软件系统。
|
22天前
|
应用服务中间件 Linux nginx
高并发下Nginx配置限流
【8月更文挑战第16天】
44 1
|
1月前
|
XML 存储 Android开发
Android实战经验之Kotlin中快速实现MVI架构
本文介绍MVI(Model-View-Intent)架构模式,强调单向数据流与不可变状态管理,提升Android应用的可维护性和可测试性。MVI分为Model(存储数据)、View(展示UI)、Intent(用户动作)、State(UI状态)与ViewModel(处理逻辑)。通过Kotlin示例展示了MVI的实现过程,包括定义Model、State、Intent及创建ViewModel,并在View中观察状态更新UI。
78 12
|
7天前
|
前端开发 开发者 C#
WPF开发者必读:MVVM模式实战,轻松实现现代桌面应用架构,让你的代码更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,MVVM(Model-View-ViewModel)模式通过分离应用程序的逻辑和界面,提高了代码的可维护性和可扩展性。本文介绍了MVVM模式的三个核心组件:Model(数据模型)、View(用户界面)和ViewModel(处理数据绑定和逻辑),并通过示例代码展示了如何在WPF项目中实现MVVM模式。通过这种方式,开发者可以构建更加高效和可扩展的桌面应用程序。
23 0
|
9天前
|
消息中间件 Java RocketMQ
微服务架构师的福音:深度解析Spring Cloud RocketMQ,打造高可靠消息驱动系统的不二之选!
【8月更文挑战第29天】Spring Cloud RocketMQ结合了Spring Cloud生态与RocketMQ消息中间件的优势,简化了RocketMQ在微服务中的集成,使开发者能更专注业务逻辑。通过配置依赖和连接信息,可轻松搭建消息生产和消费流程,支持消息过滤、转换及分布式事务等功能,确保微服务间解耦的同时,提升了系统的稳定性和效率。掌握其应用,有助于构建复杂分布式系统。
26 0
下一篇
DDNS