技术面试攻略系列-- 秒杀系统设计(上)
今天给大家分享技术面经,分享的大神已成功入职腾讯哟!
(PS:本系列文章以幽默风趣风格为主,较真侠和杠精请绕道~)
小帅:“老胡,老大说要提高组内的学习氛围,要双周组织一次分享会,我刚好抽中一个题目《秒杀系统设计》,我都这方面的经验,我问老大能不能换一个,他说没关系,尽你最大努力去做就行,我正头疼着呢!”(苦笑表情)
江华:“头疼那不是正好嘛,分享会的时候请病假就好。”(一脸正经表情)
老胡:“既然你老大这样说,我认为他除了希望大家能分享知识一起学习以外,更多的是考察你们的对知识整理、归纳、总结和传播能力,毕竟只是分享过去的经验,很快就到头了。”
小帅:“哦哦,原来是这样!老胡大神,你之前搞过秒杀系统不,求指点!”
老胡:“嗯…这个到饭点了,好像南塔的红烧鹅掌挺不错的。”
老胡:“哈哈,孺子可教也,我看你天资聪颖,骨骼惊奇,就是编程届的奇才啊,小小的秒杀分享不在话下。”
老胡:“所谓的秒杀系统,其实就是很多人同时去抢一个东西。毕竟如果只有1000个人抢100个商品,就根本不算秒杀。这时候需要产品、运营、开发一起去做容量预估,这样才能比较合适地去预估后续需要的机器、带宽、存储等等资源。
真实的容量预估比较困难,影响的因素比较多,这里你可以简单点,先假设你的秒杀系统将会有50w人参加,去抢500个商品。”
老胡:“对啊,小帅你要做的事情就是别让系统崩了。别慌,我们从页面入口到数据落地过程模拟走一遍,去分析要做些什么措施才能应对这50w的流量。”
老胡:“秒杀开始前,我们需要提早准备一个秒杀首页。因为用户肯定会提前登录你的系统,等待秒杀开始。”
小帅:“可是这么多人提前访问秒杀页,秒杀活动还没开始系统可能就挂了啊!”
老胡:“是的,整个秒杀页其实只有时间跳动的,其它部分用户信息、商品资料部分我们可以做成html静态页面,做数据拆分。做成静态页想必你们也有所了解,就是可以充分利用缓存,包括浏览器、CDN、服务端缓存。”
老胡:“浏览器缓存可以去了解下HTTP的Cache-Control等协议和304响应码,CDN加速和分发想必你们也有所耳闻,网上资料更全可以自行了解,服务端缓存也就是平时我们做的前端页面和后端代码动静分离,WEB服务器对静态资源进行缓存,请求就不用到达应用服务器。”
老胡:“至于服务器和倒计时时间校准,到点才返回秒杀链接给秒杀按钮,验证码,防重复提交这些细节就不一一展开了。”
老胡:“假设用户已经点击秒杀按钮,提交请求了,这时候请求都打到一台机器肯定不行的,小帅,这时候你应该怎样做呢?”
江华:“报告,小帅去挠头了,我来回答吧,要做负载均衡。”
老胡:“是的,我们需要把请求均摊多多台机器上,也就是做负载均衡。常用的负载均衡组件有Nginx、HAProxy、LVS已经土豪版F5等。负载均衡又分为请求均衡和数据均衡,这里加一层DNS分发,把请求分摊到下一层Nginx/OpenResty负载均衡层。”
江华:“老胡,Nginx/OpenResty层通过反向代理也可以做负载均衡啊,为啥还要加一层DNS轮询呢?是为了防止负载层单点吗,那也可以用Keepalive做个VIP啊。”
老胡:“没错,是可以按照你说的那样做。但是单个Nginx/OpenResty性能总会有极限的,这里加了DNS轮询除了可以避免Nginx/OpenResty单点外,还可以解决Nginx/OpenResty自身扩展性问题,例如当Nginx成为系统瓶颈的时候,无法扩容。
而通过DNS一个域名设置多个IP解析,就能增加入口Nginx实例个数,起到水平扩展的作用。当然技术架构选型要取决于实际情况,这里是举个例子。”
小帅:“老胡,这里100w是不是画错了?之前我们定了50w用户,页面也做了防重复提交。”
老胡:“防重复提交防不住羊毛党等那些高级玩家,这时候我们需要对请求进行过滤。例如我们可以在Nginx层增加IP过滤,例如每分钟的每个IP的请求次数。高级点可以用Lua脚本获取到用户ID,做一层布隆过滤,过滤非法用户。”
小帅:“OpenResty,好像木有用过哈。我可以在业务代码里过滤不?”(尴尬笑)
老胡:“可以的,假如Nginx/OpenResty这里你只是简单IP的过滤,还有20w请求到达你的后端服务器,这时候你就不用能传统基于BIO/NIO的后端服务器了。你可以选择一些基于IO多路复用和Reactor模型的组件来搭建你的服务器,如Vert.x、WebFlux。”
老胡:“比如你使用WebFlux,也就是Springboot2.0框架,每个OpenResty后端挂了4个节点,每个节点能分到5w请求,这样就把100w的并发量缩减到单节点5w并发量了。”
老胡:“按照以往的经验是可以的,不过这个不用太急下结论,你没有做过实际压测,说这些都是虚的,况且并不是5w都是有效请求,我们先看主流程能不能进行下去。”
小帅:“那这里我们先判断用户合法性吗?我们可以从Redis获取用户信息,速度很快的。如果不为空,表示用户合法,如果为空则表示用户不合法。”
老胡:“不急,Redis虽然快,但为本着让最少的流量到达后端的原则,我们可以先在本机内存里过滤。”
小帅:“对哦,我们可以先用MemberId过滤一层。比如30s内只允许一个相同的MemberId的请求通过,用GuavaCahce处理一下就行,减去Redis负担,妙啊!”
老胡:“不错,其实这里可以加个开关标记,判断秒杀活动是否结束,如果秒杀活动结束,开关关闭,后来的请求就直接拒绝掉了。”
标签: