商城秒杀活动要点

商城秒杀的特性:

1、定时秒杀。即商品在秒杀时间点之前是不能进行购买下单。业务较简单。

2、秒杀前用户会频繁刷新秒杀页面。

3、秒杀持续时间短、瞬时访问流量高。

4、同一用户/IP禁止秒杀多次。

秒杀系统设计要点:

1、将秒杀系统独立部署,甚至使用独立域名,使其与原有网站完全隔离。主要防止秒杀对现有网站业务造成冲击。

2、页面静态化:将活动页面上的所有可以静态的元素全部静态化,并尽量减少动态元素。通过CDN来抗峰值。

3、禁止重复提交:用户提交之后按钮置灰,禁止重复提交 用户限流:在某一时间段内只允许用户提交一次请求,比如可以采取IP限流

4、秒杀提交地址在秒杀开始的时候才能得到。

5、使用缓存、队列。将秒杀数量提前写入redis中。接收到请求时,直接递减redis中总数量,如果下单数量为0则请求直接返回。下单过程失败则增加redis中总数量。

秒杀架构设计

秒杀系统为秒杀而设计,不同于一般的网购行为,参与秒杀活动的用户更关心的是如何能快速刷新商品页面,在秒杀开始的时候抢先进入下单页面,而不是商品详情等用户体验细节,因此秒杀系统的页面设计应尽可能简单。

商品页面中的购买按钮只有在秒杀活动开始的时候才变亮,在此之前及秒杀商品卖出后,该按钮都是灰色的,不可以点击。

下单表单也尽可能简单,购买数量只能是一个且不可以修改,送货地址和付款方式都使用用户默认设置,没有默认也可以不填,允许等订单提交后修改;只有第一个提交的订单发送给网站的订单子系统,其余用户提交订单后只能看到秒杀结束页面。

要做一个这样的秒杀系统,业务会分为两个阶段,第一个阶段是秒杀开始前某个时间到秒杀开始, 这个阶段可以称之为准备阶段,用户在准备阶段等待秒杀; 第二个阶段就是秒杀开始到所有参与秒杀的用户获得秒杀结果, 这个就称为秒杀阶段吧。

1 前端层设计

首先要有一个展示秒杀商品的页面, 在这个页面上做一个秒杀活动开始的倒计时, 在准备阶段内用户会陆续打开这个秒杀的页面, 并且可能不停的刷新页面。这里需要考虑两个问题:

  1. 第一个是秒杀页面的展示

    我们知道一个html页面还是比较大的,即使做了压缩,http头和内容的大小也可能高达数十K,加上其他的css, js,图片等资源,如果同时有几千万人参与一个商品的抢购,一般机房带宽也就只有1G~10G,网络带宽就极有可能成为瓶颈,所以这个页面上各类静态资源首先应分开存放,然后放到cdn节点上分散压力,由于CDN节点遍布全国各地,能缓冲掉绝大部分的压力,而且还比机房带宽便宜~

  2. 第二个是倒计时

    出于性能原因这个一般由js调用客户端本地时间,就有可能出现客户端时钟与服务器时钟不一致,另外服务器之间也是有可能出现时钟不一致。客户端与服务器时钟不一致可以采用客户端定时和服务器同步时间,这里考虑一下性能问题,用于同步时间的接口由于不涉及到后端逻辑,只需要将当前web服务器的时间发送给客户端就可以了,因此速度很快,就我以前测试的结果来看,一台标准的web服务器2W+QPS不会有问题,如果100W人同时刷,100W QPS也只需要50台web,一台硬件LB就可以了~,并且web服务器群是可以很容易的横向扩展的(LB+DNS轮询),这个接口可以只返回一小段json格式的数据,而且可以优化一下减少不必要cookie和其他http头的信息,所以数据量不会很大,一般来说网络不会成为瓶颈,即使成为瓶颈也可以考虑多机房专线连通,加智能DNS的解决方案;web服务器之间时间不同步可以采用统一时间服务器的方式,比如每隔1分钟所有参与秒杀活动的web服务器就与时间服务器做一次时间同步

  3. 浏览器层请求拦截

    (1)产品层面,用户点击“查询”或者“购票”后,按钮置灰,禁止用户重复提交请求;

    (2)JS层面,限制用户在x秒之内只能提交一次请求;

2 站点层设计

前端层的请求拦截,只能拦住小白用户(不过这是99%的用户哟),高端的程序员根本不吃这一套,写个for循环,直接调用你后端的http请求,怎么整?

(1)同一个uid,限制访问频度,做页面缓存,x秒内到达站点层的请求,均返回同一页面

(2)同一个item的查询,例如手机车次,做页面缓存,x秒内到达站点层的请求,均返回同一页面

如此限流,又有99%的流量会被拦截在站点层。

3 服务层设计

站点层的请求拦截,只能拦住普通程序员,高级黑客,假设他控制了10w台肉鸡(并且假设买票不需要实名认证),这下uid的限制不行了吧?怎么整?

(1)大哥,我是服务层,我清楚的知道小米只有1万部手机,我清楚的知道一列火车只有2000张车票,我透10w个请求去数据库有什么意义呢?对于写请求,做请求队列,每次只透过有限的写请求去数据层,如果均成功再放下一批,如果库存不够则队列里的写请求全部返回“已售完”

(2)对于读请求,还用说么?cache来抗,不管是memcached还是redis,单机抗个每秒10w应该都是没什么问题的;

如此限流,只有非常少的写请求,和非常少的读缓存mis的请求会透到数据层去,又有99.9%的请求被拦住了。

  1. 用户请求分发模块:使用Nginx或Apache将用户的请求分发到不同的机器上。
  2. 用户请求预处理模块:判断商品是不是还有剩余来决定是不是要处理该请求。
  3. 用户请求处理模块:把通过预处理的请求封装成事务提交给数据库,并返回是否成功。
  4. 数据库接口模块:该模块是数据库的唯一接口,负责与数据库交互,提供RPC接口供查询是否秒杀结束、剩余数量等信息。
  • 用户请求预处理模块

    经过HTTP服务器的分发后,单个服务器的负载相对低了一些,但总量依然可能很大,如果后台商品已经被秒杀完毕,那么直接给后来的请求返回秒杀失败即可,不必再进一步发送事务了,示例代码可以如下所示:

    package seckill;
    import org.apache.http.HttpRequest;
    /**
    * 预处理阶段,把不必要的请求直接驳回,必要的请求添加到队列中进入下一阶段.
    */
    public class PreProcessor {
      // 商品是否还有剩余
      private static boolean reminds = true;
      private static void forbidden() {
          // Do something.
      }
      public static boolean checkReminds() {
          if (reminds) {
              // 远程检测是否还有剩余,该RPC接口应由数据库服务器提供,不必完全严格检查.
              if (!RPC.checkReminds()) {
                  reminds = false;
              }
          }
          return reminds;
      }
      /**
       * 每一个HTTP请求都要经过该预处理.
       */
      public static void preProcess(HttpRequest request) {
          if (checkReminds()) {
              // 一个并发的队列
              RequestQueue.queue.add(request);
          } else {
              // 如果已经没有商品了,则直接驳回请求即可.
              forbidden();
          }
      }
    }
    
    • 并发队列的选择

    Java的并发包提供了三个常用的并发队列实现,分别是:ConcurrentLinkedQueue 、 LinkedBlockingQueue 和 ArrayBlockingQueue。

    ArrayBlockingQueue是初始容量固定的阻塞队列,我们可以用来作为数据库模块成功竞拍的队列,比如有10个商品,那么我们就设定一个10大小的数组队列。

    ConcurrentLinkedQueue使用的是CAS原语无锁队列实现,是一个异步队列,入队的速度很快,出队进行了加锁,性能稍慢。

    LinkedBlockingQueue也是阻塞的队列,入队和出队都用了加锁,当队空的时候线程会暂时阻塞。

    由于我们的系统入队需求要远大于出队需求,一般不会出现队空的情况,所以我们可以选择ConcurrentLinkedQueue来作为我们的请求队列实现:

    package seckill;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.ConcurrentLinkedQueue;
    import org.apache.http.HttpRequest;
    public class RequestQueue {
      public static ConcurrentLinkedQueue<HttpRequest> queue = new ConcurrentLinkedQueue<HttpRequest>();
    }
    
  • 用户请求模块
    package seckill;
    import org.apache.http.HttpRequest;
    public class Processor {
      /**
       * 发送秒杀事务到数据库队列.
       */
      public static void kill(BidInfo info) {
          DB.bids.add(info);
      }
      public static void process() {
          BidInfo info = new BidInfo(RequestQueue.queue.poll());
          if (info != null) {
              kill(info);
          }
      }
    }
    class BidInfo {
      BidInfo(HttpRequest request) {
          // Do something.
      }
    }
    
  • 数据库模块

    数据库主要是使用一个ArrayBlockingQueue来暂存有可能成功的用户请求。

    package seckill;
    import java.util.concurrent.ArrayBlockingQueue;
    /**
    * DB应该是数据库的唯一接口.
    */
    public class DB {
      public static int count = 10;
      public static ArrayBlockingQueue<BidInfo> bids = new ArrayBlockingQueue<BidInfo>(10);
      public static boolean checkReminds() {
          // TODO
          return true;
      }
      // 单线程操作
      public static void bid() {
          BidInfo info = bids.poll();
          while (count-- > 0) {
              // insert into table Bids values(item_id, user_id, bid_date, other)
              // select count(id) from Bids where item_id = ?
              // 如果数据库商品数量大约总数,则标志秒杀已完成,设置标志位reminds = false.
              info = bids.poll();
          }
      }
    }

原文地址:https://www.cnblogs.com/shenjianjun/p/9205101.html

时间: 2024-07-29 12:05:50

商城秒杀活动要点的相关文章

【总结】瞬时高并发(秒杀/活动)Redis方案(转)

转载地址:http://bradyzhu.iteye.com/blog/2270698 1,Redis 丰富的数据结构(Data Structures) 字符串(String) Redis字符串能包含任意类型的数据 一个字符串类型的值最多能存储512M字节的内容 利用INCR命令簇(INCR, DECR, INCRBY)来把字符串当作原子计数器使用 使用APPEND命令在字符串后添加内容 列表(List) Redis列表是简单的字符串列表,按照插入顺序排序 你可以添加一个元素到列表的头部(左边:

【公告】关于“天天秒杀”活动时间调整

自2017年1月中旬上线以来,天天秒杀活动得到了广大新老学员的热烈支持,在此感谢大家对51CTO学院的包容与厚爱! 下载"51CTO学院"APP,随时随地秒课>>> 这一年中,我们也在持续优化秒杀体验:从最初仅限在APP中参与,到现在网页端也同步进行:同时严格把控秒杀课程的质量,优选新课加入,旨在让学员用最合算的价格学到最超值的课程! 18年,我们希望天天秒杀能有一番新气象,给大家带来更加新鲜而火热的感觉!所以-重点就是: 每场秒杀持续时间从2小时改为1小时,这样才有

秒杀活动的技术挑战

作者:李智慧链接:https://www.zhihu.com/question/20496392/answer/17272255来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 1. 秒杀活动的技术挑战假设某网站秒杀活动只推出一件商品,预计会吸引1万人参加活动,也就说最大并发请求数是10000,秒杀系统需要面对的技术挑战有:对现有网站业务造成冲击秒杀活动只是网站营销的一个附加活动,这个活动具有时间短,并发访问量大的特点,如果和网站原有应用部署在一起,必然会对现有业务

PHP高并发商城秒杀

1.什么是秒杀 秒杀活动是一些购物平台推出的集中人气的活动,一般商品数量很少,价格很便宜,限定开始购买的时间,会在以秒为单位的时间内被购买一空.比如原价千元甚至万元的商品以一元的价格出售,但数量只有一件,在某天的某个时间开始出售,这就造成很多人去抢这一件商品.当然想抢到是需要很多因素的,比如你的电脑配置.网速,还有你的运气. 2.秒杀会带来的问题 (1).高并发 比较火热的秒杀在线人数都是10w起的,如此之高的在线人数对于网站架构从前到后都是一种考验. (2).超卖 任何商品都会有数量上限,如何

Java商城秒杀系统的设计与实战视频教程(SpringBoot版)_汇总贴

51CTO学院 Java商城秒杀系统的设计与实战视频教程(SpringBoot版) H:\BaiDu\微服务0830\2019最新 Java商城秒杀系统的设计与实战视频教程(SpringBoot版) 2019最新 Java商城秒杀系统的设计与实战视频教程(SpringBoot版)_1-1课程整体介绍 原文地址:https://www.cnblogs.com/wangjunwei/p/11524192.html

秒杀活动java怎么实现

秒杀与其他业务最大的区别在于:秒杀的瞬间: (1)系统的并发量会非常的大 (2)并发量大的同时,网络的流量也会瞬间变大. 一个秒杀或者抢购页面,通常分为2个部分,一个是静态的HTML等内容,另一个就是参与秒杀的Web后台请求接口. 第一种处理:某个商品可秒杀的数量是10,那么在秒杀活动开始之前,把商品的ID和数量加载到缓存,比如:Redis.服务端收到请求的时候,首先减一下Redis里面的数量,如果数量减到0随后的访问直接返回秒杀失败.也就是说,只有10个请求最终会去实际的请求DB. 第二种处理

商城-折扣活动设计方案

需求: 折扣管理: 商品支持打折活动,打折活动支持多种形式: 活动折扣要求:大于0,小于10,允许包含一位小树: 一旦折扣活动创建后,不可以修改折扣: 促销管理: 可以按照折扣活动查询对应参与活动的商品,显示对应活动商品的原价,折扣,减价,折扣后: 允许商家查看可以参加某活动的商品,且一个商品最多允许参加一个活动即使活动已经过期,必须取消已参与的其他活动后,才能参加另外一个活动: 设置某产品参加某活动时,默认按照该活动的折扣进行计算折扣.减价.折扣后,但也允许用户手动修改某个商品或者批量改变某几

php商城秒杀,可以购买多件商品,redis防高并发

<?php header('content-type:text/html;charset=utf-8');echo time();class SeckillRedis { static protected $validity_time = 30; // 有效期 5分钟 protected $goods_id; protected $user_queue_key; protected $goods_number_key; protected $user_id; public function __

精通Access数据库从零基础学习系列第1部特惠秒杀活动即将开始

Access是office办公套件中的一个极为重要的组成部分,现在它已经成为最流行的桌面数据库管理系统.本课程为Access数据库知识系列课程第1部,课程目标:结合简单实例讲解,零基础快速学习Access数据库,数据库格式为 mdb格式.课程编排重在实用,讲解通俗易懂.让您在最短的时间内学会最需要的知识点,为您节省宝贵的时间.本课程章节计划表:第一章:Access数据库概述1.Access数据库概述第二章:Access数据库安装文件下载及安装1.Access数据库安装文件下载及安装第三章:创建数