聊的不止技术。跟着小帅写代码,还原和技术大牛一对一真实对话,剖析真实项目筑成的一砖一瓦,了解最新最及时的资讯信息,还可以学到日常撩妹小技巧哦,让我们开始探索主人公小帅的职场生涯吧!
(PS:本系列文章以幽默风趣风格为主,较真侠和杠精请绕道~)
老胡:“哟,小帅,怎样绷着个苦瓜脸似的?”
江华:“失恋了?哦…呸,单身狗哪来的失恋。”
小帅:“…”
小帅:“老胡,老大说要提高组内的学习氛围,要双周组织一次分享会,我刚好抽中一个题目《秒杀系统设计》,我都没这方面的经验,我问老大能不能换一个,他说没关系,尽你最大努力去做就行,我正头疼着呢!”(苦笑表情)
江华:“头疼那不是正好嘛,分享会的时候请病假就好。”(一脸正经表情)
老胡:“…”
小帅:“…”
老胡:“既然你老大这样说,我认为他除了希望大家能分享知识一起学习以外,更多的是考察你们的对知识整理、归纳、总结和传播能力,毕竟只是分享过去的经验,很快就到头了。”
小帅:“哦哦,原来是这样!老胡大神,你之前搞过秒杀系统不,求指点!”
老胡:“嗯…这个到饭点了,好像南塔的红烧鹅掌挺不错的。”
小帅:“在送过来的路上了。”
老胡:“哈哈,孺子可教也,我看你天资聪颖,骨骼惊奇,就是编程届的奇才啊,小小的秒杀分享不在话下。”
江华:“…”(好…贱)
老胡:“所谓的秒杀系统,其实就是很多人同时去抢一个东西。毕竟如果只有1000个人抢100个商品,就根本不算秒杀。这时候需要产品、运营、开发一起去做容量预估,这样才能比较合适地去预估后续需要的机器、带宽、存储等等资源。真实的容量预估比较困难,影响的因素比较多,这里你可以简单点,先假设你的秒杀系统将会有50w人参加,去抢500个商品。”
小帅:“Woc,50w用户,系统会崩掉吧…”
老胡:“对啊,小帅你要做的事情就是别让系统崩了。别慌,我们从页面入口到数据落地过程模拟走一遍,去分析要做些什么措施才能应对这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,系统能支撑住吗?”
老胡:“按照以往的经验是可以的,不过这个不用太急下结论,你没有做过实际压测,说这些都是虚的,况且并不是5w都是有效请求,我们先看主流程能不能进行下去。”
小帅:“那这里我们先判断用户合法性吗?我们可以从Redis获取用户信息,速度很快的。如果不为空,表示用户合法,如果为空则表示用户不合法。”
老胡:“不急,Redis虽然快,但为本着让最少的流量到达后端的原则,我们可以现在本机内存里过滤。”
小帅:“对哦,我们可以先用MemberId过滤一层。比如30s内只允许一个相同的MemberId的请求通过,用GuavaCahce处理一下就行,减去Redis负担,妙啊!”
老胡:“不错,其实这里可以加个开关标记,判断秒杀活动是否结束,如果秒杀活动结束,开关关闭,后来的请求就直接拒绝掉了。”
老胡:“接着才是根据MemberId限流,然后判断用户合法性和用户抢购状态。小帅,你上面提到的判断用户合法性的方法,你觉得存在什么问题?”
小帅:“好像没有啥问题呀?”(挠头表情)
江华:“…鸭掌吃多了吧你!缓存穿透啊!”
老胡:“判断用户合法性的时候,小帅说只查Redis,他的意思是把所有用户数据都放在Redis,不用查DB,严格来说不会存在缓存穿透问题。如果请求都是合法用户的请求,问题不算太大,但如果大部分请求都是非法用户的时候,这样做存在以下问题。”
1、Redis只需存MemberId等必要信息,不然可能造成浪费,尤其用户量庞大的时候。
2、Redis数据需要预热,也就是需要把用户数据预先加载到Redis。
3、空值缓存,当Redis查询结果为空需要缓存空值,减轻Redis负担。
老胡:“综上所述,更优的做法,应该是加一层布隆过滤器来过滤非法用户请求,然后使用本地缓存+Redis缓存来判断用户合法性和抢购状态,用户非法或者抢购成功返回失败或者订单页,请求结束。”
老胡:“布隆过滤器空间压缩比非常高,50w的MemberId占不多少内存,可以提前Load到本地缓存,虽然有一定的误判率,但已经非常高效了!”
老胡:“经过上面的一系列操作,到达的都是合法请求了,小帅,上次教你的令牌算法可以派上用场了,没有还给我吧?”
小帅:“交了学费的,当然还记得!”(肉痛表情)
江华:“…”(人傻钱多真好)
老胡:“啊哈哈,记得就好。这里可以利用Redis的decr指令的原子性,定义一个库存Key,初始值为500,当返回正数表示扣减成功,抢购状态置为抢购成功,请求进入后续步骤。当返回负数时候,表示已经售卖完毕,关闭秒杀开关,请求中断。”
小帅:“经过令牌过滤后的请求,最极端的情况下也就500个请求,我是不是可以直接操作DB,生成订单和扣减真正库存了?”
老胡:“这里建议加一层MQ,把获得令牌成功的请求推到消息队列。”
江华:“老胡,没必要吧?这样会不会过度设计了。”
老胡:“前面说过,所有架构设计都要基于实际情况,没有最好的,只有合适的。这里加一层MQ主要出于以下考虑。”
1、解耦:秒杀模块和订单库存模块完全解耦。
2、复用:秒杀活动可以抽取出比较独立的模块,供多种商品多场次提供秒杀服务。
3、对账:可以分两个消费组,其一用来跑主流程,其二跑延时Check流程,保证获得令牌的用户都能成功创建对应的订单,放弃支付的归还令牌等等操作。
4、安全:DB是公司最珍贵也是最薄弱的环节,无论是突然意外还是人为Bug导致海量请求冲垮DB,这都是不可挽回的损失。
老胡:“上面提到,只要获取令牌成功,我们就认为用户秒杀成功。所以从kafka消费到的请求数据,就可以调用订单服务创建订单。”
小帅:“只创建订单,不用扣减库存吗?”
老胡:“先不用,因为用户创建了订单,不一定会支付。我们这里用预扣模式,当用户完成支付时,才需要调用库存服务去扣减库存。”
小帅:“那库存服务怎样才能正确扣减库存呢?多线程去消费MQ的时候可能会扣多了。”
江华:“说你蠢还不信,乐观锁啊,还有脸称自己为资深CRUD工程师!”
老胡:“…”
小帅:“那如果扣减库存失败了呢?”
老胡:“这个时候对账流程就起作用了,它就是用来保证最终一致性的。库存扣减失败就会重试,直到成功,当然重试的时候需要做幂等。”
小帅:“Soga,涨姿势了,小檬下班了,我去目送一下哈,告辞!”
老胡:“…”(好…猥琐)
江华:“…”(好…猥琐)
你是否苦恼于自己技术不差但总是得不到大厂面试机会,又拉不下面子求熟人欠别人人情,社招流程慢进度不透明,苦苦等待却不知道面试结果,关注一下公众号即可获得各种大厂内推资格,各种优质岗位等着你,欢迎投递。