当前位置:首页 > PHP教程 > php高级应用 > 列表

PHP多线程模拟实现秒杀抢单

发布:smiling 来源: PHP粉丝网  添加日期:2018-10-11 12:55:42 浏览: 评论:0 

应集团要求给服务号做了个抢单秒杀的功能,需要对秒杀做个测试,想试试PHP多线程,就模拟了下抢单功能。

先说秒杀模块的思路:

正常情况下的用户秒杀操作

1、发起秒杀请求

2、进入秒杀队列

3、随机滞后 1 - 2 秒进行秒杀结果查询请求(算是变相分流吧)

4、成功则生成订单

5、返回结果

以下是模拟秒杀的代码:

  1. <?php 
  2.   
  3.   
  4. set_time_limit(0); 
  5.   
  6. /** 
  7. * 线程的执行任务 
  8. */ 
  9. class Threadrun extends Thread 
  10.   public $url
  11.   public $data
  12.   public $params
  13.   
  14.   public function __construct($url$params=[]) 
  15.   { 
  16.    $this->url = $url
  17.    $this->params = $params
  18.   } 
  19.   
  20.   public function run() 
  21.   { 
  22.    if(($url = $this->url)) 
  23.    { 
  24.      $params = [ 
  25.       'goods_id'  => 1, 
  26.       'activity_id'  => 1, 
  27.       'user_id'   => isset($this->params['user_id']) ? $this->params['user_id'] : $this->getCurrentThreadId(), 
  28.      ]; 
  29.      $startTime = microtime(true); 
  30.      $this->data = [ 
  31.       'id'   => $params['user_id'], 
  32.       'result'  => model_http_curl_get( $url$params ), 
  33.       'time'  => microtime(true)-$startTime
  34.       'now'   => microtime(true), 
  35.      ]; 
  36.    } 
  37.   } 
  38.   
  39. /** 
  40. * 执行多线程 
  41. */ 
  42. function model_thread_result_get($urls_array
  43.   foreach ($urls_array as $key => $value
  44.   { 
  45.    $threadPool[$key] = new Threadrun($value["url"],['user_id'=>$value['user_id']]); 
  46.    $threadPool[$key]->start(); 
  47.   } 
  48.   foreach ($threadPool as $thread_key => $thread_value
  49.   { 
  50.    while($threadPool[$thread_key]->isRunning()) 
  51.    { 
  52.      usleep(10); 
  53.    } 
  54.    if($threadPool[$thread_key]->join()) 
  55.    { 
  56.      $variable_data[$thread_key] = $threadPool[$thread_key]->data; 
  57.    } 
  58.   } 
  59.   return $variable_data
  60.   
  61. /** 
  62. * 发送 HTTP 请求 
  63. */ 
  64. function model_http_curl_get($url,$data=[],$userAgent=""
  65.   $userAgent = $userAgent ? $userAgent : 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2)'
  66.   $curl = curl_init(); 
  67.   curl_setopt($curl, CURLOPT_URL, $url); 
  68.   curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 
  69.   curl_setopt($curl, CURLOPT_TIMEOUT, 5); 
  70.   curl_setopt($curl, CURLOPT_USERAGENT, $userAgent); 
  71.   curl_setopt($curl, CURLOPT_POST, true); 
  72.   if( !emptyempty($data) ) { 
  73.    curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 
  74.   } 
  75.   $result = curl_exec($curl); 
  76.   curl_close($curl); 
  77.   return $result
  78.   
  79.   
  80. /** 
  81.  * 友好的打印变量 
  82.  * @param $val 
  83.  */ 
  84. function dump( $val ) 
  85.   echo '<pre>'
  86.   var_dump($val); 
  87.   echo '</pre>'
  88.   
  89. /** 
  90.  * 写日志 
  91.  * @param $msg 
  92.  * @param string $logPath 
  93.  */ 
  94. function writeLog( $msg$logPath='' ) { 
  95.   ifemptyempty($logPath) ) { 
  96.    $logPath = date('Y_m_d').'.log'
  97.   } 
  98.   if( !file_exists($logPath) ) { 
  99.    $fp = fopen$logPath,'w' ); 
  100.    fclose( $fp ); 
  101.   } 
  102.   error_log$msg.PHP_EOL, 3, $logPath); 
  103.   
  104. /** 
  105.  * 生成日志信息 
  106.  * @param $result 
  107.  * @param $timeDiff 
  108.  * @return bool|string 
  109.  */ 
  110. function createLog( $result$timeDiff ){ 
  111.   ifemptyempty($result) || !is_array($result) ) { 
  112.    return false; 
  113.   } 
  114.   $succeed = 0; 
  115.   $fail = 0; 
  116.   foreach$result as $v ) { 
  117.    $times[] = $v['time']; 
  118.    $v['result'] === false ? $fail++ : $succeed++; 
  119.   } 
  120.   $totalTime = array_sum$times ); 
  121.   $maxTime = max( $times ); 
  122.   $minTime = min( $times ); 
  123.   $sum = count$times ); 
  124.   $avgTime = $totalTime/$sum
  125.   $segment = str_repeat('=',100); 
  126.   $flag = $segment . PHP_EOL; 
  127.   $flag .= '总共执行时间:' . $timeDiff . PHP_EOL ; 
  128.   $flag .= '最大执行时间:' . $maxTime . PHP_EOL; 
  129.   $flag .= '最小执行时间:' . $minTime . PHP_EOL; 
  130.   $flag .= '平均请求时间:' . $avgTime . PHP_EOL; 
  131.   $flag .= '请求数:' . $sum . PHP_EOL; 
  132.   $flag .= '请求成功数:' . $succeed . PHP_EOL; 
  133.   $flag .= '请求失败数:' . $fail . PHP_EOL; 
  134.   $flag .= $segment . PHP_EOL; 
  135.   return $flag
  136.   
  137.   
  138.   
  139. /** 
  140.  * 发起秒杀请求 
  141.  */ 
  142. function insertList( $urls$logPath='' ) 
  143.   $t = microtime(true); 
  144.   $result = model_thread_result_get($urls); 
  145.   $e = microtime(true); 
  146.   $timeDiff = $e-$t
  147.   echo "总执行时间:" . $timeDiff . PHP_EOL; 
  148.   foreach$result as $v ) { 
  149.    $msg = '用户【' . $v['id'] . '】秒杀商品, 返回结果 ' . $v['result'] . ' 用时【' . $v['time'] . ' 秒】 当前时间【'.$v['now'].'】'
  150.    writeLog( $msg,$logPath ); 
  151.   } 
  152.   $logStr = createLog( $result$timeDiff); 
  153.   writeLog( $logStr$logPath ); 
  154.   return $result
  155.   
  156.   
  157. //发起秒杀请求 
  158. for ($i=0; $i < 1000; $i++) 
  159.   $urls_array[] = array("name" => "baidu""url" => "http://***.***.com/seckill/shopping/listinsert"); 
  160.   
  161. $list = insertList( $urls_array'./inset.log' ); 
  162.   
  163. //发起秒杀结果查询请求 
  164. $urls_array = []; 
  165. foreach$list as $v ) { 
  166.   if$v['result'] === false ) { 
  167.    continue
  168.   } 
  169.   $urls_array[] = array
  170.         "name"  => "baidu"
  171.         "url"  => "http://***.***.com/seckill/shopping/query"
  172.         'user_id' => $v['id'], 
  173.   );//phpfensi.com 
  174. insertList( $urls_array'./query.log' ); 

系统测试结果:

模拟 1000 并发的情况,单机每秒 300+ 订单,服务器毫无压力。

反倒是测试机受不了了,CPU 飙升 100%。 Apache 偶尔崩溃。

不知道是 PHP 多线程和 Windows 环境的支持不好,还是 PHP 多线程本身的问题,区区 1000 线程跑不动。多线程的地方还是比较需要 Python 和 C 出马。

Tags: 线程

分享到: