基于Spring Cloud的电商平台秒杀系统设计
2023-01-07尹玲
尹 玲
(广州科技职业技术大学 广东 广州 510550)
0 引言
在电商平台上,商品的超低价格引起人们对商品的购买欲望,这些超低价格的商品限量销售使得大量购物者疯狂抢购,甚至有黄牛党采用专业的工具抢购。商品在进行秒杀时成百万上千万的请求瞬间涌入,数据库和服务端资源有限,如果架构方面没有合理的设计,系统必然会崩溃。秒杀系统的业务流程简单,即用户下单成功核减库存,但秒杀系统瞬时并发量极大,特价商品的数量限制导致秒杀成功的请求数量很少。如何解决秒杀过程中的瞬时高并发,防止超卖是本系统重点研究的问题。
1 系统关键技术介绍
1.1 Spring Cloud 架构及微服务组件
微服务是可以独立部署、水平扩展、独立访问(或者有独立的数据库)的服务单元[1],Spring Cloud 是一个基于Spring Boot 实现的云应用开发工具,是一套微服务治理的框架[2]。秒杀系统采用Spring Cloud 分布式微服务集群架构,使用Spring Cloud 管理商品服务,秒杀服务、订单服务、库存服务等这些微服务,必然离不开Spring Cloud 的相关组件。其主要组件如下:
Spring Cloud Gateway:网关对请求进行路由、过滤和限流。
Netflix Eureka:注册中心存储所有服务信息。
Netflix Hystrix:熔断器通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力[3]。
RestTemplate:Spring 提供、用于访问的Rest 服务客户端,主要用于微服务间的调用。
1.2 负载均衡Nginx
负载均衡是将任务分摊到多个处理单元执行,在服务器集群中,Nginx 服务器起到一个代理服务器的角色(即反向代理)[4],避免其中一个服务器压力过大,将来自用户的请求转发给不同的服务器。同时,Nginx 服务器还可以做静态资源代理。负载均衡Nginx 保证所有后端服务器的性能充分发挥,从而保持服务器集群的整体性能最优。
1.3 缓存Redis
缓存是指将频繁访问的数据放在内存中,从而加快用户访问速度的技术。Redis 技术是一种基于内存的数据库,处理请求能力极高,正常情况下对读请求处理速度为90 000/s,写请求处理速度为50 000/s,并且提供一定的数据持久化的功能,缓存Redis 的使用可以提高数据的查询性能和效率。
1.4 消息中间件RocketMQ
RocketMQ 是阿里巴巴开源的分布式消息中间件,消息队列基于生产者-消费者设计模式,按具体场景与规则,针对上层请求生产者往队列的末尾添加数据,多个消费者从队列里面依次读取数据然后自行处理,消息队列具有异步、解耦、削峰的特点[5]。
1.5 数据库MySQL
MySQL 为一款免费的通用数据库,通过不断的迭代升级,现在各项读写性能均达到主流水平之上,而且也支持主从、集群模式。
2 系统实现
2.1 系统架构图
秒杀系统架构图如图1所示。
图1 系统架构图
2.2 前端层面实现
秒杀商品的页面信息很多都不会改变,为减少秒杀时服务器压力,秒杀商品的图片和文字元素都需要尽可能静态化。在服务端通过Thymeleaf 模板语言生成静态页面,用户可以通过Nginx访问页面,降低对Java服务端的压力。
前端层面除了生成静态页面减少服务器压力,还可以进行限流操作,尽可能提升秒杀效率。针对普通购物者,当购物者点击购买按钮一次后,按钮置灰,防止用户重复提交请求。针对黄牛,防止黄牛使用抓包工具和定时脚本自动发请求,需要在后端Controller 层进行处理,即对同一个用户的请求进行校验,限制同一个用户短时间内的大量请求,去除重复请求。如果想简化操作还可以花钱让市面上的专业风控团队来处理。风控系统会根据一系列规则,判断当前用户是否为刷单账号、僵尸账号等,对那些不符合平台要求的直接拦截过滤掉。
2.3 Nginx 层和网关层限流
在前端层面尽管已经拦截了恶意请求,减少了部分流量,但是秒杀的总流量还是很多的,研究人员还需要做负载均衡。负载均衡理论上讲就是集群,单台机器的性能有上限,流量太大时扛不住。那研究人员可以多加几台机器,提升物理硬件方面的能力。在Nginx 层下面的Node 节点,可以将请求按照轮询、权重等方式,均匀地分布在每一台服务器上面。例如1 000 个请求过来,研究人员有10 台Node 节点服务,那么按照轮询策略每个节点分担100 个请求,可以极大地提升服务的响应速度和健壮性。在真实的秒杀场景中,Nginx 层还会加一级4 层负载,在硬件层面的F5或者软件层面的LVS,对Nginx服务的IP做负载均衡。
Nginx 层除了做反向代理,还可以做好限流控制。Nginx 层和网关层限流具体如图2所示。
图2 Nginx 层和网关层限流设计图
应用级别限流:通过Nginx 配置属性limit_req_zone和limit_req,来控制客户端每个IP 地址限制为每秒一次请求。并且在网关层通过对用户权限控制,只有用户权限校验通过,才给予令牌权限允许访问秒杀接口服务。
接口级别的限流:一般秒杀系统允许用户每秒/次的访问量。具体是通过RedisSon(Redis+lua 客户端)工具来实现,具体限流实现为用户ID 为key,过期时间expire 属性设置为1 秒。每次接口请求进来,先通过用户ID 去查询Redis,如果查询不到则允许访问接口,同时在Redis 中set 以用户ID 为key 每秒/次的访问量,来控制同一个用户下次的访问。
2.4 缓存Redis 层实现
在秒杀的过程中,对于成功进入后端的请求在访问数据库时,会先查询数据库商品库存是否足够,库存足够才会下单成功,进行写数据库操作。秒杀时有数十万的请求进来同时查询数据库,由于数据库读写属于磁盘的输入输出,性能较低,数据库的连接资源非常有限,此时数据库可能会宕机。秒杀系统最大的瓶颈是对数据库的读写,如果能够把部分数据或业务逻辑转移到内存缓存,效率会有极大的提升。所以在秒杀开始前,可以在后台写一个控制按钮,点击按钮实现将数据提前写入缓存Redis 中,让请求过来的数据都在Redis 层处理计算。Redis 采用主从模式,Redis master(主服务)处理所有的写请求,Redis slave(从服务)用于读操作缓解系统的查库压力,再由Redis master 将数据同步到Redis slave 上。
另外为了防止超卖,研究人员需要加锁来保证,让请求从并行化切换为串行化处理。例如1 000 件商品,平均分成10 份商品库,每份100 件,然后生成10 把锁,每把锁对应一份商品库。代码业务中谁拿到锁,谁就可以产生交易,没有锁的请求则处于等待状态。使用redis 分布式锁来保护秒杀的数据库操作如图3所示。
图3 使用redis 分布式锁抢锁示意图
Redis 加锁代码示例(使用Redis+SpringAOP 的方式实现分布式锁)
2.5 RocketMQ 消息队列和MySQL 数据库
系统使用RocketMQ 的削峰特性来更新下单信息到数据库,预防因秒杀时大量订单直接更新数据库,导致数据库服务宕机。RocketMQ消息队列对订单进行分组存储管理,然后让多个消费者来进行消费,消费成功后把订单信息写入数据库。MySQL 数据库用来做最终持久化,将最终的交易数据写到硬盘。如图4所示。
图4 使用RocketMQ 消息队列更新数据库示意图
3 测试数据
JMeter 是Apache 组织基于Java 开发的压力测试工具,用于对软件做压力测试。JMeter 对秒杀系统的压测结果如图5所示。
图5 JMeter 压测结果图
当前秒杀样本数据50 000,从压力测试接口来看,吞吐量为929/s,表示系统每秒可以处理完900 个以上的工作量,基本上可以达到秒杀系统的使用要求。
4 结论
本系统结合目前市面最为流行的开发技术Spring Cloud 架构,运用其相关组件完成了微服务电商平台秒杀系统设计。该设计使用负载均衡Nginx 做反向代理和限流,使用缓存Redis 提高系统的响应速度和利用分布式锁防止超卖,使用RocketMQ 消息队列在高并发环境下进行削峰和服务间的解耦。当然,电商实现线上秒杀的方案不止一种,系统也可进一步优化。秒杀存在于大众生活的方方面面,秒杀系统的研究及性能提升具有重要意义。