秒杀场景需要考虑这些关键词:高并发、响应时效性、流程削峰、恶意流量攻击、秒杀原子操作与数据安全、服务高可用(应对雪崩)等。
面试题:设计一个10000并发的秒杀方案,9.9秒内秒杀100个商品。
【1】秒杀业务场景分析
① 秒杀/抢购业务场景
比如商品秒杀、商品抢购、群红包、抢优惠券、抽奖等等。
② 秒杀/抢购业务特点
秒杀商品价格低廉、抢购商品很好|抢手、大幅推广|广为人知、瞬时售空、一般是定时上架、持续时间短、瞬时并发量高等。
③ 秒杀/抢购技术特点
高并发、读多写少、资源冲突(容易导致超卖问题)。
概览一下技术实现:
削峰利器-异步MQ
如下所示,将用户请求通过消息中间件转发到秒杀服务器,通知秒杀系统一个个处理。
队列
可以使用Java内部队列来控制用户请求并保证公平性–先到先得,然后将队列转发给秒杀系统处理。
④ 系统基本架构与秒杀配套解决方案
不同于OSI的七层架构模型,这里应用系统架构分为五层,如下所示:
其中按钮控制、图形验证码是为了尽可能屏蔽恶意流量,比如机器人请求、脚本自动请求。
四层负载均衡
四层负载均衡工作在OSI模型的传输层,由于在传输层,只有TCP/UDP协议,这两种协议中除了包含源IP、目标IP以外,还包含源端口号及目的端口号。四层负载均衡服务器在接受到客户端请求后,以后通过修改数据包的地址信息(IP+端口号)将流量转发到应用服务器。
七层负载均衡
七层负载均衡工作在OSI模型的应用层,应用层协议较多,常用http、radius、dns等。七层负载就可以基于这些协议来负载。这些应用层协议中会包含很多有意义的内容。比如同一个Web服务器的负载均衡,除了根据IP加端口进行负载外,还可根据七层的URL、浏览器类别、语言来决定是否要进行负载均衡。
【2】秒杀技术方案落地实现
① 秒杀人群、并发规模的预估
为什么要估算?当然是为了确定一个最终的技术选型以及服务器容量。
那么如何估算呢?
平均并发用户数为C=nL/T,秒杀的并发规模就要根据公司活动历时以来的最高峰值再扩容。
② Nginx限流算法
常见限流算法有"漏桶算法"和“令牌桶算法”,Nginx提供了一个叫ngx_http_limit_req_module的模块进行流量控制,Nginx是基于漏桶算法实现。
③ 从前到后流程示意如
④ 乐观锁和悲观锁
乐观锁如使用version控制,悲观锁则如Select count from tb_goods where id=1 for update
。
乐观锁如下所示:
在高并发秒杀场景中,乐观锁是不太适合的。因为其冲突频率、重试代价所带来的性能消耗是大于悲观锁的。
【3】秒杀技术特点/优化
① 页面/静态资源的优化
动态页面静态化其实就是将传统的jsp或者thymeleaf、freemarker转换HTML+AJAX。静态页面可以缓存到客户端(比如浏览器),通过AJAX发送请求获取数据然后渲染页面。
静态资源优化
JS/CSS压缩,减少流量;
多个JS/CSS组合减少连接数(或者说多个JS/CSS请求组合为单个请求--参数Tengine);
- CDN支持
页面缓存
比如使用thymeleaf时,把页面缓存到redis一段时间,前端请求过来时先去redis获取页面。代码示例如下:
//取缓存,如果有直接返回 String html = redisService.get(GoodsKey.getGoodsDetail, ""+goodsId, String.class); if(!StringUtils.isEmpty(html)) { return html; } //如果没有就生成并放入redis SpringWebContext ctx = new SpringWebContext(request,response, request.getServletContext(),request.getLocale(), model.asMap(), applicationContext ); //后台自己解析页面并缓存起来 String html = thymeleafViewResolver.getTemplateEngine().process("goods_detail", ctx); if(!StringUtils.isEmpty(html)) { redisService.set(GoodsKey.getGoodsDetail, ""+goodsId, html); }
通常现在项目中都是采用前后端分离模式开发,比如前端的VUE。那么部署的时候采用动静分离分别部署即可。