当前位置: 首页 > 科技观察

掌握这些,高并发秒杀系统就不用愁了!

时间:2023-03-16 02:13:57 科技观察

很多小伙伴反映,学了这么久的高并发话题,但是在实际做项目的时候,还是不知道如何应对高并发的业务场景!图片来自Pexels,甚至很多小伙伴还停留在简单的界面(CRUD)阶段,不知道如何把学到的并发知识应用到实际项目中,更不知道如何搭建高并发系统!什么样的系统是高并发系统?今天,我们就一起来解密高并发的业务场景。一个典型秒杀系统的架构,结合高并发话题下的其他文章,学以致用。电子商务系统架构在电子商务领域,有典型的闪购场景,那么什么是闪购场景呢?简单来说,一个产品的买家数量远远大于这个产品的库存,这个产品会在短时间内销售一空。例如每年的618、双11、小米新品促销等业务场景就是典型的闪购场景。我们可以将电商系统的架构简化如下图所示:如图所示,我们可以将电商系统的核心层简单划分为:负载均衡层、应用层和持久层。接下来我们预估每一层的并发量:如果负载均衡层使用高性能的Nginx,我们可以预估Nginx的最大并发量为:10W+,这里的单位是10000。假设我们在应用层使用的是Tomcat,可以估计Tomcat的最大并发量在800左右,这里的单位是百。假设持久层的缓存使用Redis,数据库使用MySQL,那么MySQL的最大并发量可以估计在1000左右,以千为单位。Redis的最大并发量可以估计在5W左右,以万为单位。所以,负载均衡层、应用层、持久层的并发度是不一样的。那么,为了提高系统的整体并发和缓存,我们通常可以采用哪些方案呢?①系统扩展系统扩展包括纵向扩展和横向扩展,增加设备和机器配置,大部分场景都有效。②缓存本地缓存或集中缓存,减少网络IO,基于内存读取数据。适用于大多数场景。③读写分离采用读写分离,分而治之,增加机器的并行处理能力。秒杀系统的特点对于秒杀系统,我们可以从业务和技术的角度来说明其自身的一些特点。这里,我们可以以12306网站为例。每年春运期间,12306网站访问量非常大,但正常网站访问量相对平缓。也就是说,在每年的春运期间,12306网站的访问量都会出现骤增。再比如,小米的秒杀系统在上午10:00开始销售产品,上午10:00之前的访问量比较平淡。上午10:00,并发流量也会突然增加。因此,我们可以用下图来表示秒杀系统的流量和并发量:从图中可以看出,秒杀系统的并发量具有瞬时峰值的特征,也称为流量秒杀现象。我们可以将秒杀系统的特点概括为:①限时、限量、限价,在规定的时间内进行;秒杀活动商品数量有限;商品价格将远低于原价活动期间,商品将以远低于原价的价格出售。例如,秒杀活动的时长仅限于当日上午10:00-10:30,产品数量只有10万个,售完即止,产品价格非常低廉,比如:1元购等业务场景。期限、限价、限价可以单独存在,也可以同时存在。②活动预热需要提前配置好活动;活动开始前,用户可以查看活动的相关信息;秒杀活动开始前,大力宣传活动。③短期购买人数多;货物将很快售罄。在系统流量呈现上,会出现秒杀现象。这个时候并发访问量很高。在大多数闪购场景中,产品会在很短的时间内售罄。秒杀系统的技术特点我们可以总结一下秒杀系统的技术特点:①瞬时并发度很高,大量用户会同时抢购商品;瞬时并发峰值很高。②系统产品页面访问量巨大;可购买的产品数量很少;库存查询访问次数远大于购买次数。产品页面经常会加入一些限流措施。例如,早期秒杀系统的产品页面会添加验证码,以平滑前端访问系统的流量。近期秒杀系统的商品详情页面,用户打开页面时会提示用户登录系统。.这些是限制对系统的访问的一些措施。③流程简单秒杀系统的业务流程一般比较简单;总的来说,秒杀系统的业务流程可以概括为:下订单,减少库存。对于这种短时间内流量很大的系统,不适合使用系统扩展,因为即使扩展了系统,扩展后的系统也会在短时间内被使用。大多数情况下,系统无需扩容即可正常访问。那么,我们可以采取哪些方案来提高秒杀系统的性能呢?秒杀系统解决方案根据秒杀系统的特点,我们可以采取以下措施来提高系统的性能。①异步解耦,将整体流程拆解,通过队列控制核心流程。②限流防刷控制网站整体流量,提高请求门槛,避免系统资源耗尽。③资源控制控制全流程的资源调度,扬长避短。因为应用层能承载的并发量远小于缓存的并发量。因此,在高并发系统中,我们可以直接使用OpenResty从负载均衡层访问缓存,避免调用应用层带来的性能损失。你可以去https://openresty.org/cn/了解更多关于OpenResty的信息。同时,由于秒杀系统的产品数量较少,我们还可以利用动态渲染技术和CDN技术来加快网站访问性能。如果秒杀活动一开始并发过高,我们可以将用户的请求放入队列中处理,弹出用户排队页面。秒杀系统时序图网上很多秒杀系统和秒杀系统的解决方案都不是真正的秒杀系统。他们使用的只是一种同步请求处理方案。一旦并发真的增加了,他们所谓的秒杀系统的性能就会急剧下降。我们先来看看秒杀系统在同步下单时的时序图。同步下单流程:①用户发起秒杀请求在同步下单流程中,首先,用户发起秒杀请求。商城服务需要执行以下流程才能处理秒杀请求。判断验证码是否正确:商城服务判断用户发起秒杀请求时提交的验证码是否正确。判断活动是否结束:验证当前秒杀活动是否结束。验证访问请求是否在黑名单中:在电商领域,存在大量的恶意竞争,也就是说其他商家可能会使用不正当手段恶意请求杀系统,占用大量的流量系统的带宽和其他系统资源。这时候就需要借助风控系统来实现黑名单机制。为了简单起见,也可以使用拦截器统计访问频率来实现黑名单机制。验证真实库存是否充足:系统需要验证商品的真实库存是否充足,是否能够支撑本次限时抢购活动的商品库存。扣除缓存中的库存:在秒杀业务中,缓存中往往会存储商品库存等信息。此时还需要验证秒杀活动使用的商品库存是否充足,并扣除秒杀活动的库存数量。计算闪购价格:由于闪购活动中商品的闪购价格与商品实际价格存在差异,因此需要计算商品的闪购价格。注意:如果秒杀场景下系统涉及的业务越复杂,涉及的业务操作就越多。在这里,我只是列举一些常见的业务操作。②提交订单订单录入:将用户提交的订单信息保存到数据库中。扣除真实库存:订单入库后,需要从商品真实库存中扣除成功下单商品的数量。如果我们用上面的流程开发一个秒杀系统,当用户发起秒杀请求时,由于系统的各个业务流程都是串行执行的,所以整个系统的性能不会太高。当并发量过高时,我们会弹出如下排队页面提示用户等待。此时的排队时间可能是15秒,也可能是30秒,甚至更长。这里有个问题:从用户发起秒杀请求到服务器返回结果这段时间,客户端和服务器的连接是不会被释放的,会占用大量的服务器资源。网上很多介绍如何实现秒杀系统的文章都是用的这种方法。那么,这个方法可以作为秒杀系统吗?答案是可以的,但是这种方式支持的并发度并不算高。这时候可能有网友会问了:我们公司是这样使用秒杀系统的!上线后我一直在用,没问题!我想说的是:使用同步下单方式确实可以作为秒杀系统,但是同步下单性能不会太高。之所以你们公司的秒杀系统同步下单没有出现大的问题,是因为你们的秒杀系统的并发度没有达到一定的水平,也就是说你们的秒杀系统的并发度其实并不高。高的。因此,很多所谓的秒杀系统都有秒杀服务,但不能称为真正的秒杀系统,因为他们使用的是同步下单流程,限制了系统的并发量。之所以上线后没有出现大的问题,是因为系统的并发度不够高,没有压垮整个系统。如果12306、淘宝、天猫、京东、小米等大型商城都这么操作秒杀系统,那他们的系统迟早会被秒杀,难怪他们的系统工程师不被开除!因此,在秒杀系统中,这种同步处理下单业务流程的方案不可取。以上就是同步下单的整个流程操作。如果订购过程更复杂,涉及的业务操作也会更多。异步下单流程:由于同步下单流程的秒杀系统不能称为真正的秒杀系统,我们需要采用异步下单流程。异步下单流程不会限制系统的高并发流量。①用户发起秒杀请求用户发起秒杀请求后,商城服务会经历以下业务流程。检查验证码是否正确:当用户发起秒杀请求时,验证码会一起发送,系统会检查验证码是否有效和正确。是否限流:系统会判断用户的请求是否限流。这里,我们可以通过判断消息队列的长度来判断。因为我们把用户的请求放在了消息队列中,而用户的请求在消息队列中是累积的,所以我们可以根据当前消息队列中待处理请求的数量来判断是否需要对用户的请求进行限流。比如在秒杀活动中,我们卖出了1000件商品,此时消息队列中有1000条请求。如果以后还有用户发起秒杀请求,我们可以停止处理后续的请求,直接按照售出完成提示将商品退还给用户。因此,使用限流后,我们可以更快的处理用户请求,释放连接资源。发送MQ:当用户的秒杀请求通过前面的验证后,我们可以将用户的请求参数等信息发送给MQ进行异步处理,同时将结果信息响应给用户。在商城服务中,会有专门的异步任务处理模块来消费消息队列中的请求,并处理后续的异步流程。当用户发起秒杀请求时,异步下单流程处理的业务操作比同步下单流程少。将后续操作通过MQ发送给异步处理模块进行处理,并快速将响应结果返回给用户,释放请求连接。②异步处理我们可以异步处理下单流程的以下操作:判断活动是否结束判断本次请求是否在系统黑名单中,为了防止电商领域同行的恶意竞争,可以采用黑名单机制添加到系统中,并将恶意请求放入系统的黑名单中。可以通过使用拦截器统计访问频率来实现。扣除缓存中闪购物品的库存数量。生成秒杀token,绑定当前用户和当前秒杀活动。只有生成秒杀令牌的请求才有资格进行秒杀活动。这里引入异步处理机制。在异步处理中,可以控制系统使用多少资源,分配多少线程来处理相应的任务。③短轮询查询闪购结果这里可以通过客户端短轮询查询闪购是否合格。例如,客户端可以每3秒轮询一次请求服务器,检查是否符合LightningDeal条件。这里我们在服务端的处理就是判断当前用户是否有秒杀token。如果服务器为当前用户生成秒杀令牌,则当前用户有资格进行秒杀。否则继续轮询,直到超时或服务器返回商品已售罄或不符合闪购条件等信息。当使用短轮询查询闪购结果时,我们也可以在页面提示用户排队,但此时客户端会每隔几秒轮询一次服务端,查看闪购资格的状态。与同步下单流程相比,无需长时间挂起请求连接。这时候可能有网友会问:采用短轮询的查询方式,会不会出现超时查询不到秒杀资格的状态?答案是:有可能!这里我们试着想象一下秒杀的真实场景从本质上讲,商家参与秒杀活动并不是为了赚钱,而是为了增加产品的销量和商家的知名度,吸引更多的用户购买他们的产品。因此,我们不必保证用户可以100%检查他们是否符合限时抢购的条件。④秒杀结算验证订单token:客户端提交秒杀结算时,会向服务器提交秒杀token,商城服务会验证当前秒杀token是否有效。添加到LightningDeal购物车:商城服务验证LightningDealToken合法有效后,会将用户的LightningDeal商品添加到LightningDeal购物车。⑤提交订单订单存储:将用户提交的订单信息保存到数据库中。DeleteToken:秒杀商品订单入库成功后,删除秒杀Token。这里可以思考一个问题:为什么我们只在异步下单流程的粉红色部分采用异步处理,而在其他部分不采取异步削峰填谷措施?这是因为在异步点餐流程的设计中,无论是产品设计还是界面设计,我们都实现了在用户发起秒杀请求时,对用户的请求进行限流操作。可以说系统的限流操作非常先进。当用户发起秒杀请求时,进行了流量限制,顺利解决了系统的流量高峰。往后看,系统的并发量和系统流量都不是很高。所以网上很多文章和帖子在介绍秒杀系统的时候都说异步调峰是在下单的时候进行一些限流操作。为了后面的操作,必须对限流操作进行预处理。在秒杀业务后面的进程中进行限流操作是没有用的。高并发“黑科技”及致胜招数假设在秒杀系统中我们使用Redis来实现缓存,假设Redis的读写并发量在5万左右。我们的商城秒杀业务需要支持100万左右的并发量。如果100万并发全部进入Redis,Redis很有可能挂掉,那我们怎么解决这个问题呢?接下来,我们将一起讨论这个问题。在高并发秒杀系统中,如果使用Redis来缓存数据,Redis缓存的并发处理能力是关键,因为很多前缀操作都需要访问Redis。异步调峰只是基础操作,关键是保证Redis的并发处理能力。解决这个问题的关键思想是:分而治之,将商品库存分开。我们在Redis中存储闪购商品库存时,可以对闪购商品库存进行“分段”,以增加Redis的并发读写。比如原来秒杀商品id为10001,库存为1000件,在Redis中存储为(10001,1000)。我们把原库存分成5份,每份库存200件。此时,我们在Redis中存储的信息为(10001_0,200),(10001_1,200),(10001_2,200),(10001_3,200),(10001_4,200)。至此,我们对库存进行划分后,每一个划分后的库存都使用一个productid加上一个数字标识进行存储。这样,当对存储商品库存的各个Key进行Hash运算时,得到的Hash结果是不同的。这意味着存储商品库存的key很有可能不在Redis的同一个slot中,可以提高Redis处理请求的性能和并发度。拆分库存后,我们还需要在Redis中存储拆分库存后的商品id和Key的映射关系。此时映射关系的key为商品id,为10001。value为库存拆分后存放库存信息的Key,即10001_0、10001_1、10001_2、10001_3、10001_4。在Redis中我们可以使用List来存储这些值。在实际处理库存信息时,我们可以先从Redis中查询秒杀商品对应的库存拆分后的所有Key,并使用AtomicLong记录当前请求数量。使用请求的数量对从Redis中查询到的秒杀产品对应的库存拆分后所有Key的长度进行取模运算,结果分别为0、1、2、3、4。然后将产品id拼接在前面获取真实的库存缓存键。此时可以根据这个Key直接去Redis中获取对应的库存信息。在高并发业务场景下,我们可以直接使用Lua脚本库(OpenResty)直接从负载均衡层访问缓存。这里,我们想一个场景:如果在秒杀业务场景中,秒杀的产品瞬间售罄。这时,当用户再次发起秒杀请求时,如果系统向负载均衡层请求应用层的服务,那么应用层的服务就会访问缓存和数据库。其实本质上是没有意义的,因为产品已经卖完了,再通过系统的应用层层层验证意义不大!!而且应用层的并发访问都是以百为单位的。也会在一定程度上降低系统的并发度。为了解决这个问题,这时候我们可以在系统的负载均衡层取出用户发送请求时携带的用户id、商品id、秒杀activityid,直接访问inventory中的库存信息通过Lua脚本等技术缓存。如果秒杀产品库存小于等于0,则直接返回用户产品售罄的提示信息,不经过应用层的层层验证。对于这个架构,我们可以参考本文电子商务系统的架构图(正文开头第一张图)。写在最后,附上并发编程需要掌握的核心技能知识图谱。祝大家在学习并发编程的时候少走弯路。后记:记住,让你比别人强的不是你做了多少年的CRUD工作,而是你掌握了比别人更深入的技能。不要总是停留在CRUD的表面,了解和掌握底层原理并熟悉源码实现,形成自己的抽象思维能力,才能灵活运用。这是你突破瓶颈,脱颖而出的重要方向!你在刷♂,玩游戏的时候,别人在这里学习,成长,提高。人与人之间最大的差距,其实就是思维。你可能不相信,优秀的人总是在一起。.作者:冰河科技编辑:陶家龙来源:转载自微信公众号冰河科技(黑客-冰河)