如何设计一个百万级用户的抽奖系统?,Javaweb面试总结

本文涉及的产品
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: 如何设计一个百万级用户的抽奖系统?,Javaweb面试总结

比如抽奖,有一种场景:某个网站或者APP规定好了在某个时间点,所有人都可以参与抽奖,那么可能百万级的用户会蹲守在那个时间点,到时间大家一起参与这个抽奖。

抢红包,可能是某个电视节目上,突然说扫码可以抢红包,那么电视机前可能千万级的用户会瞬间一起打开手机扫码抢红包。

秒杀更是如此,所谓秒杀,意思是让大家都在电脑前等着,在某个时间突然就可以抢购某个限量的商品

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

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

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

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

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

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

3、一个未经过优化的系统架构

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

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

如下图所示:

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负载尚可,以及请求执行性能正常的情况下,尽可能提高一些线程数量。

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

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

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


相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
3月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
440 37
|
2月前
|
C语言
经典面试题:嵌入式系统中经常要用到无限循环,怎么样用C编写死循环呢
在嵌入式系统开发中,无限循环常用于持续运行特定任务或监听事件。使用C语言实现死循环很简单,可以通过`while(1)`或`for(;;)`的结构来编写。例如:`while (1) { /* 循环体代码 */ }`,这种写法明确简洁,适用于需要持续执行的任务或等待中断的场景。
|
3月前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
2月前
|
存储 消息中间件 缓存
系统设计面试参考-设计Spotify系统
【10月更文挑战第4天】支持用户将自己喜欢的音乐、专辑、播放列表等分享到社交媒体平台,如 Facebook、Twitter、Instagram 等。分享内容可以包括音乐链接、封面图片、简介等信息,吸引更多的用户来使用 Spotify 系统。同时,系统可以跟踪分享的效果,如点击量、转化率等,以便评估社交分享对系统推广的贡献。
|
5月前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
62 1
|
6月前
|
SQL 存储 前端开发
程序技术好文:面试知识点六:JavaWeb
程序技术好文:面试知识点六:JavaWeb
49 1
|
5月前
|
消息中间件 算法 NoSQL
面试题Kafka问题之Kafka保证系统的可用性如何解决
面试题Kafka问题之Kafka保证系统的可用性如何解决
42 0
|
5月前
|
设计模式 存储 缓存
Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
60 0
|
5月前
|
设计模式 存储 缓存
Java面试题:结合建造者模式与内存优化,设计一个可扩展的高性能对象创建框架?利用多线程工具类与并发框架,实现一个高并发的分布式任务调度系统?设计一个高性能的实时事件通知系统
Java面试题:结合建造者模式与内存优化,设计一个可扩展的高性能对象创建框架?利用多线程工具类与并发框架,实现一个高并发的分布式任务调度系统?设计一个高性能的实时事件通知系统
59 0
|
5月前
|
设计模式 存储 缓存
Java面试题:结合单例模式与Java内存模型,设计一个线程安全的单例类?使用内存屏障与Java并发工具类,实现一个高效的并发缓存系统?结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:结合单例模式与Java内存模型,设计一个线程安全的单例类?使用内存屏障与Java并发工具类,实现一个高效的并发缓存系统?结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
40 0