由秒杀活动想到的

@fwon 2014-09-22 15:15:19发表于 fwon/blog

由秒杀活动想到的


我们经常会在网页中看到各种倒计时,如彩票开奖倒计时,秒杀活动倒计时等。
对于一般的倒计时如开奖倒计时等对于时间的精准性要求并不高,我们只需要给用户提供一个可以看得到的倒计时,至于相差那么一两秒也是无关紧要的。因为对用户来说这并不会对他照成任何损失。
而对于秒杀活动,那么倒计时的精准性要求就很高了。假设有这样一个场景,用户打开个某个秒杀活动页,一直不刷新页面,等待了一天,这个时候用户看到的时间是准确的吗?
对前端有所了解的开发人员都知道倒计时一般是基于setInterval来实现的,通过类似的代码来实现:

setInterval(showTime, 1000);

而setInterval本身存在着计算误差,假设某个时候有一个CPU密集型的计算在执行。
showTime执行的间隔时间就会大于1秒.随着时间的累加,这个值是不断增大的。所以时间会越来越不准确。具体分析可看这里。我这里要讲一下怎么在前端实现准确倒计时。
由于客户端的时间千差网别,所以网页的倒计时一般都要首先获取服务端的时间。假设为serverTimeLong。
当前端获取到服务器时间后,转化为时间格式并对其进行倒计时

var serverTime1 = new Date(serverTimeLong);
var clientTime1 = new Date();
countDown(serverTime1);

当一段时间后,前端计时已经不准确,这时候要重新倒计时,我们通过将第一次获取的服务器时间和客户端时间进行差值运算

var clientTime2 = new Date();
var serverTime2 = new Date(serverTime1.getTime() + (clientTime2.getTime() - clientTime1.getTime()));

由客户端的时间差重新计算得到服务端的时间,我们可以每个一段时间进行这样一次校验,使得倒计时重新进行。之所以能这样做是因为即时代码中的异步计时是不准确的,但客户端的时钟是准确,所以其时间差也是准确的。
这个做法有两个优势,一个是不需再对服务端接口请求时间,减少负载,第二是能准确地进行倒计时重置。

问题到这,可能会想到,秒杀活动的时候肯定是不断刷新页面的,这时候带来的并发访问是平时的成百上千倍。网站想要hold得住,就必需用到比平时多更多的服务器。而这些服务器大部分时候是用不着的,会照成资源的浪费。
其实网站的秒杀业务并不能使用正常的网站业务流程,也不能和正常的网站交易共用服务器,必须设计部属专门的秒杀系统,进行专门应对。否则不仅无法完成这样大并发的请求,而且很有可能对常规业务造成影响,服务器资源消耗殆尽。

在这样的高并发下,会对应用服务器和数据库造成极大的负载压力,同时会突然增加网络及服务器带宽。在这些情况下,我们是否有可行的应对措施呢?通过查阅资料,了解到了一些解决办法。

  1. 秒杀系统独立部属
    为了避免因为秒杀活动的高并发访问拖垮整个网站,使整个网站不必面对蜂拥而至的用户访问,可将秒杀系统独立部属;如果需要,还可以使用独立的域名,使其与网站完全隔离,即时秒杀系统崩溃了,也不会对网站造成任何影响。
  2. 秒杀商品页面静态化
    将秒杀页面作为静态页,一方面能减少应用服务器的请求,也不用访问数据库。同时还能将静态页面缓存在CDN中,所以秒杀商品服务不需要部属动态的web服务器和数据库服务器。
  3. 租借秒杀活动网络带宽
    因为秒杀新增的网络带宽,必须和运营商重新购买或租借,为了减轻网站服务器的压力,需要将描述商品页面缓存在CDN,同样需要和CDN服务商临时租借新增的出口带宽。

下图为业界秒杀下单的基本流程和秒杀系统的整体架构。
秒杀下单流程图
roadmap.path
秒杀系统整体架构图
roadmap.path