太傻了!下次二面再回答不好“秒杀系统“设计原理,我就捶死自己( 三 )


  • 第一个阶段是秒杀开始前某个时间到秒杀开始 ,  这个阶段可以称之为准备阶段 , 用户在准备阶段等待秒杀;
  • 第二个阶段就是秒杀开始到所有参与秒杀的用户获得秒杀结果 ,  这个就称为秒杀阶段吧 。
前端层设计
首先要有一个展示秒杀商品的页面 , 在这个页面上做一个秒杀活动开始的倒计时 , 在准备阶段内用户会陆续打开这个秒杀的页面 ,  并且可能不停的刷新页面 。这里需要考虑两个问题:
1、秒杀页面的展示
我们知道一个html页面还是比较大的 , 即使做了压缩 , http头和内容的大小也可能高达数十K , 加上其他的css ,  js , 图片等资源 , 如果同时有几千万人参与一个商品的抢购 , 一般机房带宽也就只有1G10G , 网络带宽就极有可能成为瓶颈 , 所以这个页面上各类静态资源首先应分开存放 , 然后放到CDN节点上分散压力 , 由于CDN节点遍布全国各地 , 能缓冲掉绝大部分的压力 , 而且还比机房带宽便宜 。
2、倒计时
出于性能原因这个一般由js调用客户端本地时间 , 就有可能出现客户端时钟与服务器时钟不一致 , 另外服务器之间也是有可能出现时钟不一致 。客户端与服务器时钟不一致可以采用客户端定时和服务器同步时间 , 这里考虑一下性能问题 , 用于同步时间的接口由于不涉及到后端逻辑 , 只需要将当前Web服务器的时间发送给客户端就可以了 , 因此速度很快 。
就我以前测试的结果来看 , 一台标准的Web服务器2W+QPS不会有问题 , 如果100W人同时刷 , 100W QPS也只需要50台web , 一台硬件LB就可以了~ , 并且web服务器群是可以很容易的横向扩展的(LB+DNS轮询) , 这个接口可以只返回一小段json格式的数据 , 而且可以优化一下减少不必要cookie和其他http头的信息 , 所以数据量不会很大 , 一般来说网络不会成为瓶颈 , 即使成为瓶颈也可以考虑多机房专线连通 , 加智能DNS的解决方案;
web服务器之间时间不同步可以采用统一时间服务器的方式 , 比如每隔1分钟所有参与秒杀活动的Web服务器就与时间服务器做一次时间同步 。
浏览器层请求拦截:
  • 产品层面 , 用户点击“查询”或者“购票”后 , 按钮置灰 , 禁止用户重复提交请求;
  • JS层面 , 限制用户在x秒之内只能提交一次请求 。
站点层设计
前端层的请求拦截 , 只能拦住小白用户(不过这是99%的用户哟) , 高端的程序员根本不吃这一套 , 写个for循环 , 直接调用你后端的http请求 , 怎么整?
  • 同一个uid , 限制访问频度 , 做页面缓存 , x秒内到达站点层的请求 , 均返回同一页面
  • 同一个item的查询 , 例如手机车次 , 做页面缓存 , x秒内到达站点层的请求 , 均返回同一页面
如此限流 , 又有99%的流量会被拦截在站点层 。关注公众号Java面试那些事儿 , 回复关键字面试 , 获取最新的面试资料 。
服务层设计
站点层的请求拦截 , 只能拦住普通程序员 , 高级黑客 , 假设他控制了10w台肉鸡(并且假设买票不需要实名认证) , 这下uid的限制不行了吧?怎么整?
  • 大哥 , 我是服务层 , 我清楚的知道小米只有1万部手机 , 我清楚的知道一列火车只有2000张车票 , 我透10w个请求去数据库有什么意义呢?对于写请求 , 做请求队列 , 每次只透过有限的写请求去数据层 , 如果均成功再放下一批 , 如果库存不够则队列里的写请求全部返回“已售完”;
  • 对于读请求 , 还用说么?cache来抗 , 不管是memcached还是Redis , 单机抗个每秒10w应该都是没什么问题的 。
如此限流 , 只有非常少的写请求 , 和非常少的读缓存mis的请求会透到数据层去 , 又有99.9%的请求被拦住了 。