php秒杀系统架构设计实例

2024-12-22   浏览量:

  ① 对现有网站业务的冲击,如果秒杀程序部署到现有的服务器上,可能导致整个网站瘫痪

  解决方法

  把秒杀活动部署到单独的机子上,并且用单独的域名

  ② 高并发,用户在秒杀活动开始之前会不停的刷新页面,如果用php脚本连接数据库的方式,会对服务器的压力较大

  解决方法

  使用静态页面,并且使用cdn缓存,解决带宽压力大等问题

  ③ 避免用户直接通过下单连接下单

  解决方法

  带个随机参数,在秒杀开始之前才能得到

  ④ 控制抢购按钮,页面设计为静态页面并且使用了cdn缓存,如何点亮抢购按钮

  解决方法

  js文件后面带个随机版本号,这样不会被cdn缓存,直接到达服务器,来控制按钮点亮,这个js文件要小,不然会对服务器带来带宽的压力

  ⑤ 抢购程序设计,如果直接使用数据库事务,数据库压力太大

  解决方法

  使用redis或memcache等内存缓存,速度快还能解决超卖等问题

  抢购静态页面代码

抢购静态页面代码


  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title>秒杀!</title>
  6. <link rel="stylesheet" href="/public/css/level1_index.css"/>
  7. <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js" ></script>
  8. <script type="text/javascript" src="/public/js/level1_config.js"></script>
  9. <script type="text/javascript" src="/public/js/level1_index.js"></script>
  10. </head>
  11. <body>
  12. <div class="jingshan">
  13. <span id="timebox">01天01时01分01秒</span>
  14. </div>
  15. </body>
  16. <script type="text/javascript" src="/public/js/level1_countdown.js" ></script>
  17. <script type="text/javascript">
  18. //动态加载js文件
  19. document.write("<s" + "cript type='text/javascript' src='/public/js/nocdn.js?ver=" + Math.random() + "'></s" + "cript>");
  20. </script>
  21. </html>

使用二级域名,用cdn缓存html页面,css,js,图片等

nocdn.js生成脚本


  1. <?php
  2.  
  3. $redis = new \Redis();
  4. if ($redis->connect('127.0.0.1','6379') == false) {
  5. die($redis->getLastError());
  6. }
  7.  
  8.  
  9.  
  10. //设置token
  11. $token=md5(rand(100,10000));
  12. $redis->set("token",$token);
  13.  
  14. $hl=fopen("public/js/nocdn.js","w");
  15.  
  16.  
  17. $js=<<<EOF
  18. var button = '<div class="jingshan">'+
  19. '<span id="timebox" class="start">秒杀已经开始</span>'+
  20. '<span id="qianggou"><a href="javascript:;" ><img src="/public/images/level1_button.jpg" alt="抢购按钮"/></a></span></div>';
  21. $(".jingshan").html(button);
  22.  
  23. $(function(){
  24. var flag=1;
  25. $("#qianggou").click(function(){
  26. if(flag!=1){
  27. return ;
  28. }
  29. flag=2;
  30. var token='{$token}';
  31. var url="http://192.168.128.128/redis.php"
  32. $.ajax({
  33. type: "POST",
  34. url: url,
  35. data: "token="+token,
  36. success: function(msg){
  37. alert( "Data Saved: " + msg );
  38. }
  39. });
  40. })
  41.  
  42. })
  43. EOF;
  44. fwrite($hl, $js);
  45. fclose($hl);

主要生成nocdn.js文件的内容,用linux crontab设置定时脚本
内容主要是显示秒杀的按钮,生成随机的参数,生成ajax的提交脚本,如果要跨域使用jsonp【推荐阅读: js跨域4种解决方案

抢购代码


  1. $redis = new \Redis();
  2.  
  3. if ($redis->connect('127.0.0.1','6379') == false) {
  4. die($redis->getLastError());
  5. }
  6.  
  7. //判断用户是否已经抢购
  8. if($redis->hexists("mywatchlist","user_id_")==1){
  9. exit("已经抢购");
  10. }
  11. //带参数的url
  12. if($redis->get("token")!=$_GET['token']){
  13. exit("参数错误");
  14. }
  15.  
  16. $redis->watch("mywatchkey");//命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
  17. $mywatchkey=$redis->get("mywatchkey");
  18. $limit=10;
  19. if($mywatchkey>=$limit){
  20. exit("活动结束");
  21. }
  22. $redis->multi();//事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性(atomic)地执行。
  23. $redis->set("mywatchkey",$mywatchkey+1);
  24. //sleep(5);//测试watch
  25. $rob_result = $redis->exec();//按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil,在php中成功返回array(0->1)失败返回空
  26.  
  27. if($rob_result){
  28. //保证库存,原子判断,确保当两个客户同时访问 Redis 服务器得到的是更新后的值
  29. if($redis->incr("stock")>$limit){
  30. echo "抢购失败,请重试";
  31. }
  32. echo "抢购成功";
  33. //抢购成功
  34. //$redis->hSet("mywatchlist","user_id_".mt_rand(1, 9999),time());
  35. $redis->LPUSH("success",rand(1,20));
  36. }else{
  37. echo "抢购失败,请重试";
  38. }
  39. exit;

主要是redis的watch,如果执行事务发现mywatchkey变动过就执行事务失败,redis事务失败不会回滚,代码测试过
用ab测试没有超卖的问题

上一篇:网站建设浅谈php中使用websocket              下一篇:影响网站建设费用的主要原因有哪几点