解决方法
把秒杀活动部署到单独的机子上,并且用单独的域名
② 高并发,用户在秒杀活动开始之前会不停的刷新页面,如果用php脚本连接数据库的方式,会对服务器的压力较大
解决方法
使用静态页面,并且使用cdn缓存,解决带宽压力大等问题
③ 避免用户直接通过下单连接下单
解决方法
带个随机参数,在秒杀开始之前才能得到
④ 控制抢购按钮,页面设计为静态页面并且使用了cdn缓存,如何点亮抢购按钮
解决方法
js文件后面带个随机版本号,这样不会被cdn缓存,直接到达服务器,来控制按钮点亮,这个js文件要小,不然会对服务器带来带宽的压力
⑤ 抢购程序设计,如果直接使用数据库事务,数据库压力太大
解决方法
使用redis或memcache等内存缓存,速度快还能解决超卖等问题
抢购静态页面代码
抢购静态页面代码
- <!DOCTYPE html>
- <html>
- <head lang="en">
- <meta charset="UTF-8">
- <title>秒杀!</title>
- <link rel="stylesheet" href="/public/css/level1_index.css"/>
- <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js" ></script>
- <script type="text/javascript" src="/public/js/level1_config.js"></script>
- <script type="text/javascript" src="/public/js/level1_index.js"></script>
- </head>
- <body>
- <div class="jingshan">
- <span id="timebox">01天01时01分01秒</span>
- </div>
- </body>
- <script type="text/javascript" src="/public/js/level1_countdown.js" ></script>
- <script type="text/javascript">
- //动态加载js文件
- document.write("<s" + "cript type='text/javascript' src='/public/js/nocdn.js?ver=" + Math.random() + "'></s" + "cript>");
- </script>
- </html>
使用二级域名,用cdn缓存html页面,css,js,图片等
nocdn.js生成脚本
- <?php
- $redis = new \Redis();
- if ($redis->connect('127.0.0.1','6379') == false) {
- die($redis->getLastError());
- }
- //设置token
- $token=md5(rand(100,10000));
- $redis->set("token",$token);
- $hl=fopen("public/js/nocdn.js","w");
- $js=<<<EOF
- var button = '<div class="jingshan">'+
- '<span id="timebox" class="start">秒杀已经开始</span>'+
- '<span id="qianggou"><a href="javascript:;" ><img src="/public/images/level1_button.jpg" alt="抢购按钮"/></a></span></div>';
- $(".jingshan").html(button);
- $(function(){
- var flag=1;
- $("#qianggou").click(function(){
- if(flag!=1){
- return ;
- }
- flag=2;
- var token='{$token}';
- var url="http://192.168.128.128/redis.php"
- $.ajax({
- type: "POST",
- url: url,
- data: "token="+token,
- success: function(msg){
- alert( "Data Saved: " + msg );
- }
- });
- })
- })
- EOF;
- fwrite($hl, $js);
- fclose($hl);
主要生成nocdn.js文件的内容,用linux crontab设置定时脚本
内容主要是显示秒杀的按钮,生成随机的参数,生成ajax的提交脚本,如果要跨域使用jsonp【推荐阅读: js跨域4种解决方案】
抢购代码
- $redis = new \Redis();
- if ($redis->connect('127.0.0.1','6379') == false) {
- die($redis->getLastError());
- }
- //判断用户是否已经抢购
- if($redis->hexists("mywatchlist","user_id_")==1){
- exit("已经抢购");
- }
- //带参数的url
- if($redis->get("token")!=$_GET['token']){
- exit("参数错误");
- }
- $redis->watch("mywatchkey");//命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
- $mywatchkey=$redis->get("mywatchkey");
- $limit=10;
- if($mywatchkey>=$limit){
- exit("活动结束");
- }
- $redis->multi();//事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性(atomic)地执行。
- $redis->set("mywatchkey",$mywatchkey+1);
- //sleep(5);//测试watch
- $rob_result = $redis->exec();//按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil,在php中成功返回array(0->1)失败返回空
- if($rob_result){
- //保证库存,原子判断,确保当两个客户同时访问 Redis 服务器得到的是更新后的值
- if($redis->incr("stock")>$limit){
- echo "抢购失败,请重试";
- }
- echo "抢购成功";
- //抢购成功
- //$redis->hSet("mywatchlist","user_id_".mt_rand(1, 9999),time());
- $redis->LPUSH("success",rand(1,20));
- }else{
- echo "抢购失败,请重试";
- }
- exit;
主要是redis的watch,如果执行事务发现mywatchkey变动过就执行事务失败,redis事务失败不会回滚,代码测试过
用ab测试没有超卖的问题