互联网公司场景面试之请设计一个秒杀系统(一)!!!
秒杀系统是一个互联网技术从业者经常遇到的架构难题。一旦秒杀活动开始,从前端到后端、DBA、运维人员,几乎整个开发团队都需要投入精力关注整个系统的运行情况,因为秒杀的流量巨大,稍有不慎,轻则服务宕机,重则数据库崩溃直接数据丢失。。。
一、秒杀系统的挑战
秒杀系统涉及到的能力是多方面的,这也是为何一些大厂的面试官喜欢问秒杀问题的原因,那我们就来看看,秒杀究竟难在哪里?为何那么具有挑战性?
1.1 系统资源
CPU/IO
秒杀活动对于系统层面的资源肯定是极度占用的,比如IO、CPU,负载都非常高。
网络带宽
秒杀时,除了流量特别巨大这个特性外,流量还有一个突发性,就是平时可能只需要5M的带宽,但是由于秒杀期间,大家都来抢单,流量突然很大,如果没有做预备措施,很容易造成带宽拥挤,最终发现后端没有请求过来,直接导致整个面试活动失败。
1.2 前端模块
静态资源
在秒杀活动中,前端属于整个秒杀架构中能够直接触达用户的模块,整个网页的图片、文字、视频等这些静态资源的加载请求,也在严峻考验着整个系统的抗压能力。
无效请求
除了静态资源,秒杀的预热阶段整个前端页面也会承载相当多的“无效请求”,比如机器人毫秒级的抢单测试、秒杀的前十几秒用户反复刷新页面等,如果放任,那会对系统造成很大的压力。
时间同步
秒杀的时候客户端的时间可能和服务端的时间不一致,这样容易导致客户端提前放开秒杀按钮,秒杀的时效性受到了影响,这个问题我们可以通过搭建时间服务器来解决。
1.3 后端模块
要处理几十万qps级别的流量,对后端的挑战无疑是特别大的,有这么几个方面的挑战:并发问题、响应能力、稳定性、可用性
并发问题
这个地方最需要注意的就是库存数量,不能100件秒杀商品卖出了101件的情况,这样就让用户薅羊毛了。
响应能力
在秒杀活动期间,可能90%的吞吐能力都被抢单这一个操作占用了,假如用户要退单,是否能够及时响应?
可用性
这是毫无疑问需要保证的,秒杀期间任何一个服务出现问题都很有可能引起雪崩效应,导致整个系统全部不可用。
稳定性
这是在保证可用性前提下更进一步的要求,即整体可用,但同时也希望系统不能出现单点故障,这个时候可以使用一些调优手段来达到目标。
1.5 中间件
秒杀系统的架构肯定离不开中间件,主要会使用到消息队列和缓存,消息队列比较成熟,可选的框架也比较多,比如RabbitMQ、RocketMQ;缓存也有Redis、Memcache可选。
缓存系统
缓存穿透、缓存雪崩、缓存击穿也是比较经典的老问题,想架构一个高可用高性能的缓存系统,这三个问题的解决是最基本的要求。
1.6 数据库
数据库是整个秒杀系统的最后一道防线,如果数据库垮了,最坏的结果是数据直接丢失,所以数据库不到要保证高性能,还要保证数据的安全性。
二、秒杀系统的核心思想
要架构一个高可用高性能的秒杀系统,我们需要把握住核心思想,这个核心思想有三点:限制流量、提升性能、数据安全。
2.1 限制流量
首先来说说限制流量的手段:过滤请求、流量削锋、请求转移。
过滤请求:
对前端而言,当秒杀活动进入到预热阶段,我们就需要对一些无效的请求进行过滤。
• (1)预热阶段,可以通过禁用【立即下单】按钮,避免大量无效请求到达后端。
• (2)对于直接用url来下单的机器人,需要通过一定的检测机制(下单频率,通常是毫秒级)予以甄别打击。
• (3)能够下单的前提是用户已登录,没有登录要提示或转到登录界面。
对后端而言,我们需要对静态页面的请求使用CDN(静态资源服务器)的方式进行返回,而不是直接请求后台服务器来获取。
流量削锋:
对流量进行削锋有两种方式,一种是通过算法来实现,一种是通过中间件来实现。首先来谈谈算法怎么实现?
• 算法主要是可以通过漏斗桶算法和令牌桶算法。顾名思义,漏斗桶因为漏斗口大小不变,所以不论进来的流量多大,都会以固定的流量流入到服务;而令牌桶指的是以一定的速率给要进到服务的流量予以颁发令牌,这样流量就是稳定的。除了这两种限流算法,还有滑动窗口限流法、固定窗口限流法等。
• 实现流量削锋的中间件有两种,一种是基于限流算法开发出的分布式组件,如阿里巴巴开源的Sentinel,官方定位为分布式系统的流量防卫兵,现已集成到SpringCloud Alibaba的全家桶当中;另一种方式是通过消息队列进行流量削锋,比如RocketMQ、RabbitMQ,当请求从前端发送到后端时,后端并不直接接收请求,而是将请求保存到消息队列中,然后再从队列中取出,逐个的消费。
请求转移:
当一台机器不足以负载每秒几十万请求时,我们会考虑增加机器的数量,而不是想着提升单台机器的性能。
机器的数量增加了,那势必需要将原来请求到单台服务器的流量分发到其他机器上,这种分发需要依赖一定的负载均衡算法来实现,如轮询、哈希等。
基于负载均衡算法实现的请求转移中间件有很多,比如nginx、zuul网关等。
2.2 提升性能
性能的提升可以从配置调优、增加缓存、读写分离等方面着手。
配置调优:
很容易想到的一个提升机器的性能的方式就是对配置进行调优,可以从两个层面入手,系统层面和应用层面。系统层面可以从网络、磁盘、内核、资源等方面进行调优;应用层面可以对中间件、JVM、MySQL等进行调优。
增加缓存:
缓存的增加也是提高系统性能的一个有效手段,比如代码层面可以使用LruCache、HashMap等,在整个架构层面可以引入Redis等缓存中间件。
缓存中一般是用于存放那些经常访问的热点数据,但是也要注意缓存的正确使用,避免诸如缓存穿透、缓存雪崩、缓存击穿等问题。
读写分离:
从操作层面来看,数据库的流量一般分为两种,一种是读(SELECT)请求,另一种是写(UPDATE、INSERT、DELETE)请求。而读请求远远多于写请求,所以我们可以将读请求和写请求分发到不同的服务器,减少读请求对单节点的压力。
2.3 数据安全
任何业务活动的开展,都需要以数据安全为前提,秒杀活动更不例外。我们可以通过主从复制、熔断保护、服务降级实现。
• 主从复制:Master/Slave模式就是一个经典的主从架构模式,这种模式经常用来保证MySQL的数据安全与高可用。
• 熔断保护:服务的熔断有点像家庭电路中的保险丝,一旦出现故障,立刻断掉整个线路,而在整个秒杀架构中我们经常会采用心跳机制检测服务是否故障,若是故障,立刻将服务踢出秒杀集群中。
• 服务降级:一旦某个服务无力承担过高的流量访问时,这个时候我们可以主动将服务下线,这种服务一般是不会影响到秒杀业务的服务,比如查看物流服务、历史订单查看等服务。
三、总结
我们这一节主要讲解了秒杀系统将会遇到的挑战和设计一个秒杀系统的核心思想,下一节我们具体讲讲秒杀系统中涉及到的重要技术难点。