秒杀的架构 -摘自网络

昨天回答的太差了,明明都是些很简单的东西,我居然回答的那么差,让我很有挫败感, 一些概念性的东西这里就不说了,下面两个问题,重新梳理一下:

1,一致性哈希虚拟节点与真实节点映射关系的建立:现在我们使用的是{AAAA......AAA,ZZZZ....ZZZ}字符串构成的圆环, 每台真实服务器生成N个虚拟节点,虚拟节点生成的规则为, 用$i遍历从0到N-1, 对字符串"{$i}_{$realServer}"做md5, 生成一个虚拟节点。如果是{0, 2^32-1}整形圆环, 可以每台服务器生成N个虚拟节点, 同样遍历从0到N-1,每次遍历生成0到2^32-1的一个随机数, 这个随机数作为虚拟节点。
 
2,秒杀问题:我对秒杀业务并不熟悉, 所以这里我只能假设业务:M个用户在一个时间点抢N个商品,M有可能在0到几千万不等, N为1到数千。要做一个这样的系统, 业务会分为两个阶段,第一个阶段是秒杀开始前某个时间到秒杀开始, 这个阶段可以称之为准备阶段,用户在准备阶段等待秒杀; 第二个阶段就是秒杀开始到所有参与秒杀的用户获得秒杀结果, 这个就称为秒杀阶段吧。
 
首先要有一个展示秒杀商品的页面, 在这个页面上做一个秒杀活动开始的倒计时, 在准备阶段内用户会陆续打开这个秒杀的页面, 并且可能不停的刷新页面。这里需要考虑两个问题:
 
第一个是秒杀页面的展示, 我们知道一个html页面还是比较大的,即使做了压缩,http头和内容的大小也可能高达数十K,加上其他的css, js,图片等资源,如果同时有几千万人参与一个商品的抢购,一般机房带宽也就只有1G~10G,网络带宽就极有可能成为瓶颈,所以 这个页面上各类静态资源首先应分开存放,然后放到cdn节点上分散压力,由于CDN节点遍布全国各地,  能缓冲掉绝大部分的压力, 而且还比机房带宽便宜~
 
第二个是倒计时, 出于性能原因这个一般由js调用客户端本地时间,就有可能出现客户端时钟与服务器时钟不一致, 另外服务器之间也是有可能出现时钟不一致。客户端与服务器时钟不一致可以采用客户端定时和服务器同步时间, 这里考虑一下性能问题,用于同步时间的接口由于不涉及到后端逻辑, 只需要将当前web服务器的时间发送给客户端就可以了, 因此速度很快, 就我以前测试的结果来看,一台标准的web服务器2W+QPS不会有问题,如果100W人同时刷,100W QPS也只需要50台web, 一台硬件LB就可以了~,并且web服务器群是可以很容易的横向扩展的(LB+DNS轮询), 这个接口可以只返回一小段json格式的数据,而且可以优化一下减少不必要cookie和其他http头的信息,所以数据量不会很大, 一般来说网络不会成为瓶颈,即使成为瓶颈也可以考虑多机房专线连通,加智能DNS的解决方案 ;web服务器之间时间不同步可以采用统一时间服务器的方式, 比如每隔1分钟所有参与秒杀活动的web服务器就与时间服务器做一次时间同步。
 
准备阶段的就是这些了, 下面正式开始进行秒杀,两种思路, 第一种为中心化,第二种为去中心化。这里假设一种场景, 100W人抢1个商品,如果用去中心化的思路显然没法搞(我昨天说的使用商品池就是去中心化的思路, 晚上想来是有问题的), 但这种业务很可能是大量存在的, 所以我们尝试使用中心化的思路。
 
先想的简单一点, 1个商品在中心化的商品服务器上, 当一个秒杀的请求到来的时候, 我们需要锁住这个商品,判断商品状态,修改这个商品的状态为已被秒杀, 然后释放锁。这个过程就算再优化, 再快,我就算给你单台100W qps,那如果有1000W人来秒杀,1000W请求同时从web服务器涌来, 它也顶不住, 因为它无法扩展, 所以我们需要想一点办法。
 
我想到的办法是加一个候选人中间件在web服务器和中心商品服务器之间(见附件的架构图),在候选人中间件中维护一个候选人队列(所有候选人队列中的队列单元数量之和需要大于秒杀商品数量), 当请求从web服务器转发过来的时候, 首先判断这个队列是否已满, 如果没满就将请求放入队列中排队,队列满以后的所有请求直接返回秒杀失败。中间件在处理web服务器请求的同时,转发队列中的请求给中心商品服务器(这里可以使用生产者-消费者模型),中心商品服务器将前面N个成功秒杀的结果返回后, 返回秒杀结束。中间件收到商品服务器返回的秒杀结束,给队列中的所有请求返回失败, 以后进来的请求也全部返回失败, 这样就大大降低了中心商品服务器的请求数, 甚至可以在候选人中间件和中心商品服务器之间再加一层候选人中间件,这样形成一个多层次的缓存架构,流量每经过一层就会减少, 最后将到达中心商品服务器的流量控制在一定的范围内。
 
最后还要考虑一些单点的问题, 这里时间服务器和后面的中心商品服务器都是单点。
 
 时间服务器好办, 可以采用树状结构的时间服务器模型, 所有时间服务器都去同步root节点的时间服务器, 然后web服务器设置同步多台时间服务器,当一台down掉的话web服务器还可以和另外的时间服务器继续同步,如果root节点的时间服务器down掉, 就从下层的服务器里面拿一台出来做root就可以了。
 
中心商品服务器的话可以使用一些同步复制的技术,比如底层的drbd或者类似mysql的 semi synchronous。当运气实在太背~,一台商品服务器在秒杀的过程中down掉,另外一台可以迅速接管。另外还可以使用共享存储的方式,不过据说这个比较昂贵哈~。
 
写这么多,还花了个图, 累死我了, 看来还是夜深人静的时候思路清晰一些,如果以后还有面试机会, 请半夜打电话来~
,呵呵。
原文地址:https://www.cnblogs.com/haoliansheng/p/4430618.html