写点什么

15 个经典面试问题,如何设计一个百万级用户的抽奖系统?

用户头像
Geek_f90455
关注
发布于: 39 分钟前

4、负载均衡层的限流

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 状态,负载均衡层会感知到。


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


5、Tomcat 线程数量的优化

其次就是对于线上生产环境的 Tomcat,有一个至关重要的参数是需要根据自己的情况调节好的,那就是他的工作线程数量。


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


但是这个线程数量是需要经过压测来进行判断的,因为每个线程都会处理一个请求,这个请求又需要访问数据库之类的外部系统,所以不是每个系统的参数都可以一样的,需要自己对系统进行压测。


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


同时,Tomcat 的线程数量也不宜太少,因为如果就 100 个线程,那么会导致无法充分利用 Tomcat 的线程资源和机器的 CPU 资源。


所以一般来说,Tomcat 线程数量在 200~500 之间都是可以的,但是具体多少需要自己压测一下,不断的调节参数,看具体的 CPU 负载以及线程执行请求的一个效率。


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


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


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

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


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


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


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


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


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


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


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

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


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


其实这是没必要的,因为抽奖之后完全可以让礼品服务在后台慢慢的把中奖的礼品给发放出去,不需要一下子就立马对 1 万个请求完成礼品的发放逻辑。


所以这里可以在抽奖服务和礼品服务之间,引入消息中间件,进行限流削峰


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

最后

对于很多 Java 工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。


整理的这些资料希望对 Java 开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。


再免费分享一波我的 Java 面试真题+视频学习详解+技能进阶书籍


点击这里即可免费获取以上我收集整理的全部学习资料



用户头像

Geek_f90455

关注

还未添加个人签名 2021.07.06 加入

还未添加个人简介

评论

发布
暂无评论
15个经典面试问题,如何设计一个百万级用户的抽奖系统?