如何设计一个百万级用户的抽奖系统?

公众号:狸猫技术窝

做者:爱钓鱼的桌子哥,资深架构师node


一、抽奖系统的背景引入

本文给你们分享一个以前经历过的抽奖系统的流量削峰架构的设计方案。数据库

抽奖、抢红包、秒杀,这类系统其实都有一些共同的特色,那就是在某个时间点会瞬间涌入大量的人来点击系统,给系统形成瞬间高于平时百倍、千倍甚至几十万倍的流量压力。多线程

好比抽奖,有一种场景:某个网站或者APP规定好了在某个时间点,全部人均可以参与抽奖,那么可能百万级的用户会蹲守在那个时间点,到时间你们一块儿参与这个抽奖。架构

抢红包,多是某个电视节目上,忽然说扫码能够抢红包,那么电视机前可能千万级的用户会瞬间一块儿打开手机扫码抢红包。并发

秒杀更是如此,所谓秒杀,意思是让你们都在电脑前等着,在某个时间忽然就能够抢购某个限量的商品负载均衡

好比某个手机平时卖5999,如今限量100台价格才2999,50%的折扣,可能百万级的用户就会蹲守在电脑前在好比凌晨12点一块儿点击按钮抢购这款手机。分布式

相似的场景其实如今是不少的,那么本文就用一个抽奖系统举例,说说应对这种瞬时超高并发的流量,应该如何设计流量削峰的架构来应对,才能保证系统不会忽然跨掉?高并发


二、结合具体业务需求分析抽奖系统

假设如今有一个抽奖的业务场景,用户在某个时间能够参与抽奖,好比一共有1万个奖,奖品就是某个礼物。性能

而后参与抽奖的用户可能有几十万,一瞬间可能几十万请求涌入过来,接着瞬间其中1万人中奖了,剩余的人都是没中奖的。而后中奖的1万人的请求会联动调用礼品服务,完成这1万中奖人的礼品发放。优化

简单来讲,需求场景就是如此,然而这里就有不少的地方值得优化了。


三、一个未通过优化的系统架构

先来看一个未通过任何优化的系统架构,简单来讲就是有一个负载均衡的设备会把瞬间涌入的超高并发的流量转发到后台的抽奖服务上。

这个抽奖服务就是用普通的Tomcat来部署的,里面实现了具体的抽奖逻辑,假设刚开始最常规的抽奖逻辑是基于MySQL来实现的,接着就是基于Tomcat部署的礼品服务,抽奖服务若是发现中奖了须要调用礼品服务去发放礼品。

以下图所示:


四、负载均衡层的限流

4.1 防止用户重复抽奖

首先第一次在负载均衡层能够作的事情,就是防止重复抽奖。

咱们能够在负载均衡设备中作一些配置,判断若是同一个用户在1分钟以内屡次发送请求来进行抽奖,就认为是恶意重复抽奖,或者是他们本身写的脚本在刷奖,这种流量一概认为是无效流量,在负载均衡设备那个层次就给直接屏蔽掉。

举个例子,好比有几十万用户瞬间同时抽奖,最多其实也就几十万请求而已,可是若是有人重复抽奖或者是写脚本刷奖,那可能瞬间涌入的是几百万的请求,就不是几十万的请求了,因此这里就能够把无效流量给拦截掉。

以下图所示:

4.2 所有开奖后暴力拦截流量

其实秒杀、抢红包、抽奖,这类系统有一个共同的特色,那就是假设有50万请求涌入进来,可能前5万请求就直接把事儿干完了,甚至是前500请求就把事儿干完了,后续的几十万流量是无效的,不须要让他们进入后台系统执行业务逻辑了。

什么意思呢?

举个例子,秒杀商品,假设有50万人抢一个特价手机,人家就准备了100台手机,那么50万请求瞬间涌入,其实前500个请求就把手机抢完了,后续的几十万请求不必让他转发到Tomcat服务中去执行秒杀业务逻辑了,不是吗?

抽奖、红包都是同样的 ,可能50万请求涌入,可是前1万个请求就把奖品都抽完了,或者把红包都抢完了,后续的流量其实已经不须要放到Tomcat抽奖服务上去了,直接暴力拦截返回抽奖结束就能够了。

这样的话,其实在负载均衡这一层(能够考虑用Nginx之类的来实现)就能够拦截掉99%的无效流量。

因此必须让抽奖服务跟负载均衡之间有一个状态共享的机制。

就是说抽奖服务一旦所有开奖完毕,直接更新一个共享状态。而后负载均衡感知到了以后,后续请求所有拦截掉返回一个抽奖结束的标识就能够了。

这么作可能就会作到50万人一块儿请求,结果就可能2万请求到了后台的Tomcat抽奖服务中,48万请求直接拦截掉了。

咱们能够基于Redis来实现这种共享抽奖状态,它很是轻量级,很适合两个层次的系统的共享访问。

固然其实用ZooKeeper也是能够的,在负载均衡层能够基于zk客户端监听某个znode节点状态。一旦抽奖结束,抽奖服务更新zk状态,负载均衡层会感知到。

下图展现了上述所说的过程:


五、Tomcat线程数量的优化

其次就是对于线上生产环境的Tomcat,有一个相当重要的参数是须要根据本身的状况调节好的,那就是他的工做线程数量。

众所周知,对于进入Tomcat的每一个请求,其实都会交给一个独立的工做线程来进行处理,那么Tomcat有多少线程,就决定了并发请求处理的能力。

可是这个线程数量是须要通过压测来进行判断的,由于每一个线程都会处理一个请求,这个请求又须要访问数据库之类的外部系统,因此不是每一个系统的参数均可以同样的,须要本身对系统进行压测。

可是给一个经验值的话,Tomcat的线程数量不宜过多。由于线程过多,普通虚拟机的CPU是扛不住的,反而会致使机器CPU负载太高,最终崩溃。

同时,Tomcat的线程数量也不宜太少,由于若是就100个线程,那么会致使没法充分利用Tomcat的线程资源和机器的CPU资源。

因此通常来讲,Tomcat线程数量在200~500之间都是能够的,可是具体多少须要本身压测一下,不断的调节参数,看具体的CPU负载以及线程执行请求的一个效率。

在CPU负载尚可,以及请求执行性能正常的状况下,尽量提升一些线程数量。

可是若是到一个临界值,发现机器负载太高,并且线程处理请求的速度开始降低,说明这台机扛不住这么多线程并发执行处理请求了,此时就不能继续上调线程数量了。


六、基于Redis实现抽奖业务逻辑

如今问题又来了,虽然在负载均衡那个层面,已经把好比50万流量中的48万都拦截掉了,可是可能仍是会有2万流量进入抽奖服务

此时抽奖服务天然是能够多机器来部署的,好比假设一台Tomcat能够抗500请求,那么2万并发就是40台机器。

若是你是基于云平台来部署系统的,搞活动临时租用一批机器就能够了,活动结束了机器立马能够释放掉,如今云平台都很方便。

可是有个问题,你的数据库MySQL能抗住2万的并发请求吗?

若是你基于MySQL来实现核心的抽奖业务逻辑,40个Tomcat部署的抽奖服务频繁对MySQL进行增删改查,这一个MySQL实例也是很难抗住的。

因此此时还得把MySQL给替换成Redis,一般这种场景下,建议是基于Redis来实现核心的业务逻辑。

Redis单机抗2万并发那是很轻松的一件事情,因此在这里又须要作进一步的优化。以下图:


七、发放礼品环节进行限流削峰

接着问题又来了,假设抽奖服务在2万请求中有1万请求抽中了奖品,那么势必会形成抽奖服务对礼品服务调用1万次。

礼品服务假设也是优化后的Tomcat,能够抗500并发,难道礼品服务也要去部署20台机器吗?

其实这是不必的,由于抽奖以后彻底可让礼品服务在后台慢慢的把中奖的礼品给发放出去,不须要一会儿就立马对1万个请求完成礼品的发放逻辑。

因此这里能够在抽奖服务和礼品服务之间,引入消息中间件,进行限流削峰

也就是说,抽奖服务把中奖信息发送到MQ,而后礼品服务假设就部署两个Tomcat,慢慢的从MQ中消费中奖消息,而后慢慢完成1完礼品的发放就能够了。

假设两个礼品服务实例每秒能够完成100个礼品的发放,那么1万个礼品也就是延迟100秒发放完毕罢了。

也就是你抽奖以后,可能过了一两分钟,会看到本身的礼品发放的一些物流配送的进度之类的。

并且礼品服务可能须要在MySQL数据库中作不少增删改查的操做,好比插入中奖纪录,而后进行礼品发货等等。

此时由于礼品服务就2个Tomcat实例,因此对MySQL的并发读写不会过高,那么数据库层面也是能够抗住的。

整个过程,以下图所示:


八、系统架构设计总结

其实对于商品秒杀、抽奖活动、抢红包类的系统而言,架构设计的思路不少都是相似的,核心思路都是对于这种瞬时超高流量的系统,尽量在负载均衡层就把99%的无效流量拦截掉

而后在1%的流量进入核心业务服务后,此时每秒并发仍是可能会上万,那么能够基于Redis实现核心业务逻辑 ,抗住上万并发。

最后对于相似秒杀商品发货、抽奖商品发货、红包资金转帐之类的很是耗时的操做,彻底能够基于MQ来限流削峰,后台有一个服务慢慢执行便可。


做者简介:

爱钓鱼的桌子哥,资深架构师

做者前后工做于滴滴、百度、字节跳动等国内一线互联网大厂,从事基础架构相关工做。带领团队设计与构建了大规模的分布式存储系统、分布式消息中间件、分布式数据库,对分布式架构设计、系统高可用体系构建、基础中间件架构都有丰富的经验。


END

长按下图二维码,即刻关注【狸猫技术窝】 阿里、京东、美团、字节跳动 顶尖技术专家坐镇 为IT人打造一个 “有温度” 的技术窝!

相关文章
相关标签/搜索